mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-16 03:31:27 +00:00
Add eslint rule and fix as many issues as possible
This commit is contained in:
@@ -1,21 +1,21 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useRouter } from 'next/router';
|
||||
import { faFolderOpen, faKey, faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useRouter } from "next/router";
|
||||
import { faFolderOpen, faKey, faMagnifyingGlass } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import NavHeader from '@app/components/navigation/NavHeader';
|
||||
import { Button, Input, TableContainer, Tooltip } from '@app/components/v2';
|
||||
import { useWorkspace } from '@app/context';
|
||||
import NavHeader from "@app/components/navigation/NavHeader";
|
||||
import { Button, Input, TableContainer, Tooltip } from "@app/components/v2";
|
||||
import { useWorkspace } from "@app/context";
|
||||
import {
|
||||
useGetProjectFoldersBatch,
|
||||
useGetProjectSecretsByKey,
|
||||
useGetUserWsEnvironments,
|
||||
useGetUserWsKey
|
||||
} from '@app/hooks/api';
|
||||
} from "@app/hooks/api";
|
||||
|
||||
import { EnvComparisonRow } from './components/EnvComparisonRow';
|
||||
import { FolderComparisonRow } from './components/EnvComparisonRow/FolderComparisonRow';
|
||||
import { EnvComparisonRow } from "./components/EnvComparisonRow";
|
||||
import { FolderComparisonRow } from "./components/EnvComparisonRow/FolderComparisonRow";
|
||||
|
||||
export const DashboardEnvOverview = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -24,12 +24,12 @@ export const DashboardEnvOverview = () => {
|
||||
const { currentWorkspace, isLoading } = useWorkspace();
|
||||
const workspaceId = currentWorkspace?._id as string;
|
||||
const { data: latestFileKey } = useGetUserWsKey(workspaceId);
|
||||
const [searchFilter, setSearchFilter] = useState('');
|
||||
const [searchFilter, setSearchFilter] = useState("");
|
||||
const secretPath = router.query?.secretPath as string;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && !workspaceId && router.isReady) {
|
||||
router.push('/noprojects');
|
||||
router.push("/noprojects");
|
||||
}
|
||||
}, [isLoading, workspaceId, router.isReady]);
|
||||
|
||||
@@ -107,13 +107,13 @@ export const DashboardEnvOverview = () => {
|
||||
pathname: router.pathname,
|
||||
query: {
|
||||
...router.query,
|
||||
secretPath: `${router.query?.secretPath || ''}/${path}`
|
||||
secretPath: `${router.query?.secretPath || ""}/${path}`
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onFolderCrumbClick = (index: number) => {
|
||||
const newSecPath = secretPath.split('/').filter(Boolean).slice(0, index).join('/');
|
||||
const newSecPath = secretPath.split("/").filter(Boolean).slice(0, index).join("/");
|
||||
const query = { ...router.query, secretPath: `/${newSecPath}` } as Record<string, string>;
|
||||
// root condition
|
||||
if (index === 0) delete query.secretPath;
|
||||
@@ -144,7 +144,7 @@ export const DashboardEnvOverview = () => {
|
||||
return (
|
||||
<div className="container mx-auto max-w-full px-6 text-mineshaft-50 dark:[color-scheme:dark]">
|
||||
<div className="relative right-5">
|
||||
<NavHeader pageName={t('dashboard.title')} isProjectRelated />
|
||||
<NavHeader pageName={t("dashboard.title")} isProjectRelated />
|
||||
</div>
|
||||
<div className="mt-6">
|
||||
<p className="text-3xl font-semibold text-bunker-100">Secrets Overview</p>
|
||||
@@ -180,14 +180,14 @@ export const DashboardEnvOverview = () => {
|
||||
>
|
||||
<FontAwesomeIcon icon={faFolderOpen} className="text-primary" />
|
||||
</div>
|
||||
{(secretPath || '')
|
||||
.split('/')
|
||||
{(secretPath || "")
|
||||
.split("/")
|
||||
.filter(Boolean)
|
||||
.map((path, index, arr) => (
|
||||
<div
|
||||
key={`secret-path-${index + 1}`}
|
||||
className={`breadcrumb relative z-20 ${
|
||||
index + 1 === arr.length ? 'cursor-default' : 'cursor-pointer'
|
||||
index + 1 === arr.length ? "cursor-default" : "cursor-pointer"
|
||||
} border-solid border-mineshaft-600 py-1 pl-5 pr-2 text-sm text-mineshaft-200`}
|
||||
onClick={() => onFolderCrumbClick(index + 1)}
|
||||
onKeyDown={() => null}
|
||||
@@ -247,7 +247,7 @@ export const DashboardEnvOverview = () => {
|
||||
</div>
|
||||
<div
|
||||
className={`${
|
||||
isDashboardEmpty ? '' : ''
|
||||
isDashboardEmpty ? "" : ""
|
||||
} no-scrollbar::-webkit-scrollbar mt-3 flex h-full max-h-[calc(100vh-370px)] w-full min-w-[60.3rem] flex-grow flex-row items-start justify-center overflow-x-hidden rounded-md border border-mineshaft-600 no-scrollbar`}
|
||||
>
|
||||
{!isDashboardEmpty && (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { FormProvider, useFieldArray, useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { FormProvider, useFieldArray, useForm } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useRouter } from "next/router";
|
||||
import {
|
||||
faArrowLeft,
|
||||
faCheck,
|
||||
@@ -13,13 +13,13 @@ import {
|
||||
faFolderPlus,
|
||||
faMagnifyingGlass,
|
||||
faPlus
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { useNotificationContext } from '@app/components/context/Notifications/NotificationProvider';
|
||||
import NavHeader from '@app/components/navigation/NavHeader';
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import NavHeader from "@app/components/navigation/NavHeader";
|
||||
import {
|
||||
Button,
|
||||
DeleteActionModal,
|
||||
@@ -33,10 +33,10 @@ import {
|
||||
TableContainer,
|
||||
Tag,
|
||||
Tooltip
|
||||
} from '@app/components/v2';
|
||||
import { leaveConfirmDefaultMessage } from '@app/const';
|
||||
import { useWorkspace } from '@app/context';
|
||||
import { useLeaveConfirm, usePopUp, useToggle } from '@app/hooks';
|
||||
} from "@app/components/v2";
|
||||
import { leaveConfirmDefaultMessage } from "@app/const";
|
||||
import { useWorkspace } from "@app/context";
|
||||
import { useLeaveConfirm, usePopUp, useToggle } from "@app/hooks";
|
||||
import {
|
||||
useBatchSecretsOp,
|
||||
useCreateFolder,
|
||||
@@ -55,23 +55,23 @@ import {
|
||||
usePerformSecretRollback,
|
||||
useRegisterUserAction,
|
||||
useUpdateFolder
|
||||
} from '@app/hooks/api';
|
||||
import { secretKeys } from '@app/hooks/api/secrets/queries';
|
||||
import { WorkspaceEnv } from '@app/hooks/api/types';
|
||||
} from "@app/hooks/api";
|
||||
import { secretKeys } from "@app/hooks/api/secrets/queries";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||
|
||||
import { CompareSecret } from './components/CompareSecret';
|
||||
import { CreateTagModal } from './components/CreateTagModal';
|
||||
import { CompareSecret } from "./components/CompareSecret";
|
||||
import { CreateTagModal } from "./components/CreateTagModal";
|
||||
import {
|
||||
FolderForm,
|
||||
FolderSection,
|
||||
TDeleteFolderForm,
|
||||
TEditFolderForm
|
||||
} from './components/FolderSection';
|
||||
import { PitDrawer } from './components/PitDrawer';
|
||||
import { SecretDetailDrawer } from './components/SecretDetailDrawer';
|
||||
import { SecretDropzone } from './components/SecretDropzone';
|
||||
import { SecretInputRow } from './components/SecretInputRow';
|
||||
import { SecretTableHeader } from './components/SecretTableHeader';
|
||||
} from "./components/FolderSection";
|
||||
import { PitDrawer } from "./components/PitDrawer";
|
||||
import { SecretDetailDrawer } from "./components/SecretDetailDrawer";
|
||||
import { SecretDropzone } from "./components/SecretDropzone";
|
||||
import { SecretInputRow } from "./components/SecretInputRow";
|
||||
import { SecretTableHeader } from "./components/SecretTableHeader";
|
||||
import {
|
||||
DEFAULT_SECRET_VALUE,
|
||||
downloadSecret,
|
||||
@@ -80,9 +80,9 @@ import {
|
||||
transformSecretsToBatchSecretReq,
|
||||
TSecOverwriteOpt,
|
||||
TSecretDetailsOpen
|
||||
} from './DashboardPage.utils';
|
||||
} from "./DashboardPage.utils";
|
||||
|
||||
const USER_ACTION_PUSH = 'first_time_secrets_pushed';
|
||||
const USER_ACTION_PUSH = "first_time_secrets_pushed";
|
||||
|
||||
/*
|
||||
* Some imp aspects to consider. Here there are multiple stats changing
|
||||
@@ -104,19 +104,19 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
|
||||
const secretContainer = useRef<HTMLDivElement | null>(null);
|
||||
const { popUp, handlePopUpOpen, handlePopUpToggle, handlePopUpClose } = usePopUp([
|
||||
'secretDetails',
|
||||
'addTag',
|
||||
'secretSnapshots',
|
||||
'uploadedSecOpts',
|
||||
'compareSecrets',
|
||||
'folderForm',
|
||||
'deleteFolder'
|
||||
"secretDetails",
|
||||
"addTag",
|
||||
"secretSnapshots",
|
||||
"uploadedSecOpts",
|
||||
"compareSecrets",
|
||||
"folderForm",
|
||||
"deleteFolder"
|
||||
] as const);
|
||||
const [isSecretValueHidden, setIsSecretValueHidden] = useToggle(true);
|
||||
const [searchFilter, setSearchFilter] = useState('');
|
||||
const [searchFilter, setSearchFilter] = useState("");
|
||||
const [snapshotId, setSnaphotId] = useState<string | null>(null);
|
||||
const [selectedEnv, setSelectedEnv] = useState<WorkspaceEnv | null>(null);
|
||||
const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc');
|
||||
const [sortDir, setSortDir] = useState<"asc" | "desc">("asc");
|
||||
const deletedSecretIds = useRef<string[]>([]);
|
||||
const { hasUnsavedChanges, setHasUnsavedChanges } = useLeaveConfirm({ initialValue: false });
|
||||
|
||||
@@ -129,7 +129,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && !workspaceId && router.isReady) {
|
||||
router.push('/noprojects');
|
||||
router.push("/noprojects");
|
||||
}
|
||||
}, [isLoading, workspaceId, router.isReady]);
|
||||
|
||||
@@ -157,15 +157,15 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
|
||||
const { data: secrets, isLoading: isSecretsLoading } = useGetProjectSecrets({
|
||||
workspaceId,
|
||||
env: selectedEnv?.slug || '',
|
||||
env: selectedEnv?.slug || "",
|
||||
decryptFileKey: latestFileKey!,
|
||||
isPaused: Boolean(snapshotId),
|
||||
folderId
|
||||
});
|
||||
|
||||
const { data: folderData, isLoading: isFoldersLoading } = useGetProjectFolders({
|
||||
workspaceId: workspaceId || '',
|
||||
environment: selectedEnv?.slug || '',
|
||||
workspaceId: workspaceId || "",
|
||||
environment: selectedEnv?.slug || "",
|
||||
parentFolderId: folderId,
|
||||
isPaused: isRollbackMode,
|
||||
sortDir
|
||||
@@ -178,7 +178,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
isFetchingNextPage
|
||||
} = useGetWorkspaceSecretSnapshots({
|
||||
workspaceId,
|
||||
environment: selectedEnv?.slug || '',
|
||||
environment: selectedEnv?.slug || "",
|
||||
folder: folderId,
|
||||
limit: 10
|
||||
});
|
||||
@@ -188,14 +188,14 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
isLoading: isSnapshotSecretsLoading,
|
||||
isFetching: isSnapshotChanging
|
||||
} = useGetSnapshotSecrets({
|
||||
snapshotId: snapshotId || '',
|
||||
env: selectedEnv?.slug || '',
|
||||
snapshotId: snapshotId || "",
|
||||
env: selectedEnv?.slug || "",
|
||||
decryptFileKey: latestFileKey!
|
||||
});
|
||||
|
||||
const { data: snapshotCount, isLoading: isLoadingSnapshotCount } = useGetWsSnapshotCount(
|
||||
workspaceId,
|
||||
selectedEnv?.slug || '',
|
||||
selectedEnv?.slug || "",
|
||||
folderId
|
||||
);
|
||||
|
||||
@@ -213,7 +213,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
// why any: well yup inferred ts expects other keys to defined as undefined
|
||||
defaultValues: secrets as any,
|
||||
values: secrets as any,
|
||||
mode: 'onBlur',
|
||||
mode: "onBlur",
|
||||
resolver: yupResolver(schema)
|
||||
});
|
||||
|
||||
@@ -225,7 +225,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
formState: { isSubmitting, isDirty },
|
||||
reset
|
||||
} = method;
|
||||
const { fields, prepend, append, remove } = useFieldArray({ control, name: 'secrets' });
|
||||
const { fields, prepend, append, remove } = useFieldArray({ control, name: "secrets" });
|
||||
const isReadOnly = selectedEnv?.isWriteDenied;
|
||||
const isAddOnly = selectedEnv?.isReadDenied && !selectedEnv?.isWriteDenied;
|
||||
const canDoRollback = !isReadOnly && !isAddOnly;
|
||||
@@ -242,17 +242,17 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
}, [isSubmitDisabled]);
|
||||
|
||||
const onSortSecrets = () => {
|
||||
const dir = sortDir === 'asc' ? 'desc' : 'asc';
|
||||
const sec = getValues('secrets') || [];
|
||||
const dir = sortDir === "asc" ? "desc" : "asc";
|
||||
const sec = getValues("secrets") || [];
|
||||
const sortedSec = sec.sort((a, b) =>
|
||||
dir === 'asc' ? a?.key?.localeCompare(b?.key || '') : b?.key?.localeCompare(a?.key || '')
|
||||
dir === "asc" ? a?.key?.localeCompare(b?.key || "") : b?.key?.localeCompare(a?.key || "")
|
||||
);
|
||||
setValue('secrets', sortedSec);
|
||||
setValue("secrets", sortedSec);
|
||||
setSortDir(dir);
|
||||
};
|
||||
|
||||
const handleUploadedEnv = (uploadedSec: TSecOverwriteOpt['secrets']) => {
|
||||
const sec = getValues('secrets') || [];
|
||||
const handleUploadedEnv = (uploadedSec: TSecOverwriteOpt["secrets"]) => {
|
||||
const sec = getValues("secrets") || [];
|
||||
const conflictingSec = sec.filter(({ key }) => Boolean(uploadedSec?.[key]));
|
||||
const conflictingSecIds = conflictingSec.reduce<Record<string, boolean>>(
|
||||
(prev, curr) => ({
|
||||
@@ -271,18 +271,18 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
...DEFAULT_SECRET_VALUE,
|
||||
key,
|
||||
value: uploadedSec[key].value,
|
||||
comment: uploadedSec[key].comments.join(',')
|
||||
comment: uploadedSec[key].comments.join(",")
|
||||
});
|
||||
}
|
||||
});
|
||||
setValue('secrets', sec, { shouldDirty: true });
|
||||
setValue("secrets", sec, { shouldDirty: true });
|
||||
if (conflictingSec.length > 0) {
|
||||
handlePopUpOpen('uploadedSecOpts', { secrets: conflictingUploadedSec });
|
||||
handlePopUpOpen("uploadedSecOpts", { secrets: conflictingUploadedSec });
|
||||
}
|
||||
};
|
||||
|
||||
const onOverwriteSecrets = () => {
|
||||
const sec = getValues('secrets') || [];
|
||||
const sec = getValues("secrets") || [];
|
||||
const uploadedSec = (popUp?.uploadedSecOpts?.data as TSecOverwriteOpt)?.secrets;
|
||||
const data: Array<{ key: string; index: number }> = [];
|
||||
sec.forEach(({ key }, index) => {
|
||||
@@ -290,7 +290,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
});
|
||||
data.forEach(({ key, index }) => {
|
||||
const { value, comments } = uploadedSec[key];
|
||||
const comment = comments.join(', ');
|
||||
const comment = comments.join(", ");
|
||||
sec[index] = {
|
||||
...DEFAULT_SECRET_VALUE,
|
||||
key,
|
||||
@@ -299,15 +299,15 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
tags: sec[index].tags
|
||||
};
|
||||
});
|
||||
setValue('secrets', sec, { shouldDirty: true });
|
||||
handlePopUpClose('uploadedSecOpts');
|
||||
setValue("secrets", sec, { shouldDirty: true });
|
||||
handlePopUpClose("uploadedSecOpts");
|
||||
};
|
||||
|
||||
const onSecretRollback = async () => {
|
||||
if (!snapshotSecret?.version) {
|
||||
createNotification({
|
||||
text: 'Failed to find secret version',
|
||||
type: 'success'
|
||||
text: "Failed to find secret version",
|
||||
type: "success"
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -315,23 +315,23 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
await performSecretRollback({
|
||||
workspaceId,
|
||||
version: snapshotSecret.version,
|
||||
environment: selectedEnv?.slug || '',
|
||||
environment: selectedEnv?.slug || "",
|
||||
folderId
|
||||
});
|
||||
setValue('isSnapshotMode', false);
|
||||
setValue("isSnapshotMode", false);
|
||||
setSnaphotId(null);
|
||||
queryClient.invalidateQueries(
|
||||
secretKeys.getProjectSecret(workspaceId, selectedEnv?.slug || '')
|
||||
secretKeys.getProjectSecret(workspaceId, selectedEnv?.slug || "")
|
||||
);
|
||||
createNotification({
|
||||
text: 'Successfully rollback secrets',
|
||||
type: 'success'
|
||||
text: "Successfully rollback secrets",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
createNotification({
|
||||
text: 'Failed to rollback secrets',
|
||||
type: 'error'
|
||||
text: "Failed to rollback secrets",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -344,7 +344,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
return;
|
||||
}
|
||||
// just closing this if save is triggered from drawer
|
||||
handlePopUpClose('secretDetails');
|
||||
handlePopUpClose("secretDetails");
|
||||
// when add only mode remove rest of things not created
|
||||
const sec = isAddOnly ? userSec.filter(({ _id }) => !_id) : userSec;
|
||||
// encrypt and format the secrets to batch api format
|
||||
@@ -365,8 +365,8 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
environment: selectedEnv?.slug
|
||||
});
|
||||
createNotification({
|
||||
text: 'Successfully saved changes',
|
||||
type: 'success'
|
||||
text: "Successfully saved changes",
|
||||
type: "success"
|
||||
});
|
||||
deletedSecretIds.current = [];
|
||||
if (!hasUserPushed) {
|
||||
@@ -375,14 +375,14 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
createNotification({
|
||||
text: 'Failed to save changes',
|
||||
type: 'error'
|
||||
text: "Failed to save changes",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onDrawerOpen = (dto: TSecretDetailsOpen) => {
|
||||
handlePopUpOpen('secretDetails', dto);
|
||||
handlePopUpOpen("secretDetails", dto);
|
||||
};
|
||||
|
||||
const onEnvChange = (slug: string) => {
|
||||
@@ -407,7 +407,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
if (overrideId) deletedSecretIds.current.push(overrideId);
|
||||
remove(index);
|
||||
// just the case if this is called from drawer
|
||||
handlePopUpClose('secretDetails');
|
||||
handlePopUpClose("secretDetails");
|
||||
};
|
||||
|
||||
const onCreateWsTag = async (tagName: string) => {
|
||||
@@ -415,24 +415,24 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
await createWsTag({
|
||||
workspaceID: workspaceId,
|
||||
tagName,
|
||||
tagSlug: tagName.replace(' ', '_')
|
||||
tagSlug: tagName.replace(" ", "_")
|
||||
});
|
||||
handlePopUpClose('addTag');
|
||||
handlePopUpClose("addTag");
|
||||
createNotification({
|
||||
text: 'Successfully created a tag',
|
||||
type: 'success'
|
||||
text: "Successfully created a tag",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to create a tag',
|
||||
type: 'error'
|
||||
text: "Failed to create a tag",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleFolderOpen = (id: string) => {
|
||||
setSearchFilter('');
|
||||
setSearchFilter("");
|
||||
router.push({
|
||||
pathname: router.pathname,
|
||||
query: {
|
||||
@@ -448,20 +448,20 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
try {
|
||||
await createFolder({
|
||||
workspaceId,
|
||||
environment: selectedEnv?.slug || '',
|
||||
environment: selectedEnv?.slug || "",
|
||||
folderName: name,
|
||||
parentFolderId: folderId
|
||||
});
|
||||
createNotification({
|
||||
type: 'success',
|
||||
text: 'Successfully created folder'
|
||||
type: "success",
|
||||
text: "Successfully created folder"
|
||||
});
|
||||
handlePopUpClose('folderForm');
|
||||
handlePopUpClose("folderForm");
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to create folder',
|
||||
type: 'error'
|
||||
text: "Failed to create folder",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -472,19 +472,19 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
await updateFolder({
|
||||
folderId: id,
|
||||
workspaceId,
|
||||
environment: selectedEnv?.slug || '',
|
||||
environment: selectedEnv?.slug || "",
|
||||
name
|
||||
});
|
||||
createNotification({
|
||||
type: 'success',
|
||||
text: 'Successfully updated folder'
|
||||
type: "success",
|
||||
text: "Successfully updated folder"
|
||||
});
|
||||
handlePopUpClose('folderForm');
|
||||
handlePopUpClose("folderForm");
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to update folder',
|
||||
type: 'error'
|
||||
text: "Failed to update folder",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -494,19 +494,19 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
try {
|
||||
deleteFolder({
|
||||
workspaceId,
|
||||
environment: selectedEnv?.slug || '',
|
||||
environment: selectedEnv?.slug || "",
|
||||
folderId: id
|
||||
});
|
||||
createNotification({
|
||||
type: 'success',
|
||||
text: 'Successfully removed folder'
|
||||
type: "success",
|
||||
text: "Successfully removed folder"
|
||||
});
|
||||
handlePopUpClose('deleteFolder');
|
||||
handlePopUpClose("deleteFolder");
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to remove folder',
|
||||
type: 'error'
|
||||
text: "Failed to remove folder",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -544,9 +544,9 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
{/* breadcrumb row */}
|
||||
<div className="relative right-6 -top-2 mb-2">
|
||||
<NavHeader
|
||||
pageName={t('dashboard.title')}
|
||||
pageName={t("dashboard.title")}
|
||||
currentEnv={
|
||||
userAvailableEnvs?.filter((envir) => envir.slug === envFromTop)[0].name || ''
|
||||
userAvailableEnvs?.filter((envir) => envir.slug === envFromTop)[0].name || ""
|
||||
}
|
||||
isFolderMode
|
||||
folders={folderData?.dir}
|
||||
@@ -556,10 +556,10 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<h6 className="text-2xl">{isRollbackMode ? 'Secret Snapshot' : ''}</h6>
|
||||
<h6 className="text-2xl">{isRollbackMode ? "Secret Snapshot" : ""}</h6>
|
||||
{isRollbackMode && Boolean(snapshotSecret) && (
|
||||
<Tag colorSchema="green">
|
||||
{new Date(snapshotSecret?.createdAt || '').toLocaleString()}
|
||||
{new Date(snapshotSecret?.createdAt || "").toLocaleString()}
|
||||
</Tag>
|
||||
)}
|
||||
</div>
|
||||
@@ -588,7 +588,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
>
|
||||
<div className="flex flex-col space-y-2">
|
||||
<Button
|
||||
onClick={() => downloadSecret(getValues('secrets'), selectedEnv?.slug)}
|
||||
onClick={() => downloadSecret(getValues("secrets"), selectedEnv?.slug)}
|
||||
colorSchema="primary"
|
||||
variant="outline_bg"
|
||||
className="h-8 bg-bunker-700"
|
||||
@@ -600,7 +600,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
</Popover>
|
||||
</div>
|
||||
<div>
|
||||
<Tooltip content={isSecretValueHidden ? 'Reveal Secrets' : 'Hide secrets'}>
|
||||
<Tooltip content={isSecretValueHidden ? "Reveal Secrets" : "Hide secrets"}>
|
||||
<IconButton
|
||||
ariaLabel="reveal"
|
||||
variant="outline_bg"
|
||||
@@ -615,7 +615,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
<IconButton
|
||||
ariaLabel="recovery"
|
||||
variant="outline_bg"
|
||||
onClick={() => handlePopUpOpen('secretSnapshots')}
|
||||
onClick={() => handlePopUpOpen("secretSnapshots")}
|
||||
>
|
||||
<FontAwesomeIcon icon={faCodeCommit} />
|
||||
</IconButton>
|
||||
@@ -624,7 +624,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
<div className="hidden xl:block">
|
||||
<Button
|
||||
variant="outline_bg"
|
||||
onClick={() => handlePopUpOpen('secretSnapshots')}
|
||||
onClick={() => handlePopUpOpen("secretSnapshots")}
|
||||
leftIcon={<FontAwesomeIcon icon={faCodeCommit} />}
|
||||
isLoading={isLoadingSnapshotCount}
|
||||
isDisabled={!canDoRollback}
|
||||
@@ -640,7 +640,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
<IconButton
|
||||
ariaLabel="recovery"
|
||||
variant="outline_bg"
|
||||
onClick={() => handlePopUpOpen('folderForm')}
|
||||
onClick={() => handlePopUpOpen("folderForm")}
|
||||
>
|
||||
<FontAwesomeIcon icon={faFolderPlus} />
|
||||
</IconButton>
|
||||
@@ -656,7 +656,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
if (secretContainer.current) {
|
||||
secretContainer.current.scroll({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
behavior: "smooth"
|
||||
});
|
||||
}
|
||||
prepend(DEFAULT_SECRET_VALUE, { shouldFocus: false });
|
||||
@@ -669,7 +669,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
<div className="hidden lg:block">
|
||||
<Button
|
||||
leftIcon={<FontAwesomeIcon icon={faFolderPlus} />}
|
||||
onClick={() => handlePopUpOpen('folderForm')}
|
||||
onClick={() => handlePopUpOpen("folderForm")}
|
||||
isDisabled={isReadOnly || isRollbackMode}
|
||||
variant="outline_bg"
|
||||
className="h-10"
|
||||
@@ -685,7 +685,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
if (secretContainer.current) {
|
||||
secretContainer.current.scroll({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
behavior: "smooth"
|
||||
});
|
||||
}
|
||||
prepend(DEFAULT_SECRET_VALUE, { shouldFocus: false });
|
||||
@@ -721,13 +721,13 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
color="primary"
|
||||
variant="solid"
|
||||
>
|
||||
{isRollbackMode ? 'Rollback' : 'Save Changes'}
|
||||
{isRollbackMode ? "Rollback" : "Save Changes"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`${
|
||||
isEmptyPage ? 'flex flex-col items-center justify-center' : ''
|
||||
isEmptyPage ? "flex flex-col items-center justify-center" : ""
|
||||
} no-scrollbar::-webkit-scrollbar mt-3 h-3/4 overflow-x-hidden overflow-y-scroll no-scrollbar`}
|
||||
ref={secretContainer}
|
||||
>
|
||||
@@ -738,8 +738,8 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
<tbody className="max-h-96 overflow-y-auto">
|
||||
<FolderSection
|
||||
onFolderOpen={handleFolderOpen}
|
||||
onFolderUpdate={(id, name) => handlePopUpOpen('folderForm', { id, name })}
|
||||
onFolderDelete={(id, name) => handlePopUpOpen('deleteFolder', { id, name })}
|
||||
onFolderUpdate={(id, name) => handlePopUpOpen("folderForm", { id, name })}
|
||||
onFolderDelete={(id, name) => handlePopUpOpen("deleteFolder", { id, name })}
|
||||
folders={folderList}
|
||||
search={searchFilter}
|
||||
/>
|
||||
@@ -755,7 +755,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
onRowExpand={() => onDrawerOpen({ id: _id as string, index })}
|
||||
isSecretValueHidden={isSecretValueHidden}
|
||||
wsTags={wsTags}
|
||||
onCreateTagOpen={() => handlePopUpOpen('addTag')}
|
||||
onCreateTagOpen={() => handlePopUpOpen("addTag")}
|
||||
/>
|
||||
))}
|
||||
{!isReadOnly && !isRollbackMode && (
|
||||
@@ -778,7 +778,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
)}
|
||||
<PitDrawer
|
||||
isDrawerOpen={popUp?.secretSnapshots?.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle('secretSnapshots', isOpen)}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("secretSnapshots", isOpen)}
|
||||
fetchNextPage={fetchNextPage}
|
||||
hasNextPage={hasNextPage}
|
||||
snapshotId={snapshotId}
|
||||
@@ -791,10 +791,10 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
isReadOnly={isReadOnly || isRollbackMode}
|
||||
onSecretDelete={onSecretDelete}
|
||||
isDrawerOpen={popUp?.secretDetails?.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle('secretDetails', isOpen)}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("secretDetails", isOpen)}
|
||||
secretVersion={secretVersion}
|
||||
index={(popUp?.secretDetails?.data as TSecretDetailsOpen)?.index}
|
||||
onEnvCompare={(key) => handlePopUpOpen('compareSecrets', key)}
|
||||
onEnvCompare={(key) => handlePopUpOpen("compareSecrets", key)}
|
||||
/>
|
||||
<SecretDropzone
|
||||
isSmaller={!isEmptyPage}
|
||||
@@ -808,7 +808,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
<Modal
|
||||
isOpen={popUp?.addTag?.isOpen}
|
||||
onOpenChange={(open) => {
|
||||
handlePopUpToggle('addTag', open);
|
||||
handlePopUpToggle("addTag", open);
|
||||
}}
|
||||
>
|
||||
<ModalContent
|
||||
@@ -821,7 +821,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
{/* Uploaded env override or not confirmation modal */}
|
||||
<Modal
|
||||
isOpen={popUp?.uploadedSecOpts?.isOpen}
|
||||
onOpenChange={(open) => handlePopUpToggle('uploadedSecOpts', open)}
|
||||
onOpenChange={(open) => handlePopUpToggle("uploadedSecOpts", open)}
|
||||
>
|
||||
<ModalContent
|
||||
title="Duplicate Secrets"
|
||||
@@ -829,7 +829,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
<Button
|
||||
key="keep-old-btn"
|
||||
className="mr-4"
|
||||
onClick={() => handlePopUpClose('uploadedSecOpts')}
|
||||
onClick={() => handlePopUpClose("uploadedSecOpts")}
|
||||
>
|
||||
Keep old
|
||||
</Button>,
|
||||
@@ -843,7 +843,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
<div className="text-sm text-gray-400">
|
||||
{Object.keys((popUp?.uploadedSecOpts?.data as TSecOverwriteOpt)?.secrets || {})
|
||||
?.map((key) => key)
|
||||
.join(', ')}
|
||||
.join(", ")}
|
||||
</div>
|
||||
<div>Are you sure you want to overwrite these secrets?</div>
|
||||
</div>
|
||||
@@ -851,9 +851,9 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
</Modal>
|
||||
<Modal
|
||||
isOpen={popUp?.folderForm?.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle('folderForm', isOpen)}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("folderForm", isOpen)}
|
||||
>
|
||||
<ModalContent title={isEditFolder ? 'Edit Folder' : 'Create Folder'}>
|
||||
<ModalContent title={isEditFolder ? "Edit Folder" : "Create Folder"}>
|
||||
<FolderForm
|
||||
isEdit={isEditFolder}
|
||||
onUpdateFolder={handleFolderUpdate}
|
||||
@@ -866,12 +866,12 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
|
||||
isOpen={popUp.deleteFolder.isOpen}
|
||||
deleteKey={(popUp.deleteFolder?.data as TDeleteFolderForm)?.name}
|
||||
title="Do you want to delete this folder?"
|
||||
onChange={(isOpen) => handlePopUpToggle('deleteFolder', isOpen)}
|
||||
onChange={(isOpen) => handlePopUpToggle("deleteFolder", isOpen)}
|
||||
onDeleteApproved={handleFolderDelete}
|
||||
/>
|
||||
<Modal
|
||||
isOpen={popUp?.compareSecrets?.isOpen}
|
||||
onOpenChange={(open) => handlePopUpToggle('compareSecrets', open)}
|
||||
onOpenChange={(open) => handlePopUpToggle("compareSecrets", open)}
|
||||
>
|
||||
<ModalContent
|
||||
title={popUp?.compareSecrets?.data as string}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import crypto from 'crypto';
|
||||
import crypto from "crypto";
|
||||
|
||||
import * as yup from 'yup';
|
||||
import * as yup from "yup";
|
||||
|
||||
import {
|
||||
decryptAssymmetric,
|
||||
encryptSymmetric
|
||||
} from '@app/components/utilities/cryptography/crypto';
|
||||
import { BatchSecretDTO, DecryptedSecret } from '@app/hooks/api/secrets/types';
|
||||
} from "@app/components/utilities/cryptography/crypto";
|
||||
import { BatchSecretDTO, DecryptedSecret } from "@app/hooks/api/secrets/types";
|
||||
|
||||
export enum SecretActionType {
|
||||
Created = 'created',
|
||||
Modified = 'modified',
|
||||
Deleted = 'deleted'
|
||||
Created = "created",
|
||||
Modified = "modified",
|
||||
Deleted = "deleted"
|
||||
}
|
||||
|
||||
export const DEFAULT_SECRET_VALUE = {
|
||||
@@ -20,9 +20,9 @@ export const DEFAULT_SECRET_VALUE = {
|
||||
overrideAction: undefined,
|
||||
idOverride: undefined,
|
||||
valueOverride: undefined,
|
||||
comment: '',
|
||||
key: '',
|
||||
value: '',
|
||||
comment: "",
|
||||
key: "",
|
||||
value: "",
|
||||
tags: []
|
||||
};
|
||||
|
||||
@@ -32,12 +32,12 @@ const secretSchema = yup.object({
|
||||
.string()
|
||||
.trim()
|
||||
.required()
|
||||
.label('Secret key')
|
||||
.test('starts-with-number', 'Should start with an alphabet', (val) =>
|
||||
.label("Secret key")
|
||||
.test("starts-with-number", "Should start with an alphabet", (val) =>
|
||||
Boolean(val?.charAt(0)?.match(/[a-zA-Z]/i))
|
||||
)
|
||||
.test({
|
||||
name: 'duplicate-keys',
|
||||
name: "duplicate-keys",
|
||||
// TODO:(akhilmhdh) ts keeps throwing from not found need to see how to resolve this
|
||||
test: (val, ctx: any) => {
|
||||
const secrets: Array<{ key: string }> = ctx?.from?.[1]?.value?.secrets || [];
|
||||
@@ -49,7 +49,7 @@ const secretSchema = yup.object({
|
||||
if (pos.length <= 1) {
|
||||
return true;
|
||||
}
|
||||
return ctx.createError({ message: `Same key in row ${pos.join(', ')}` });
|
||||
return ctx.createError({ message: `Same key in row ${pos.join(", ")}` });
|
||||
}
|
||||
}),
|
||||
value: yup.string().trim(),
|
||||
@@ -75,14 +75,14 @@ export type FormData = yup.InferType<typeof schema>;
|
||||
export type TSecretDetailsOpen = { index: number; id: string };
|
||||
export type TSecOverwriteOpt = { secrets: Record<string, { comments: string[]; value: string }> };
|
||||
|
||||
export const downloadSecret = (secrets: FormData['secrets'] = [], env: string = 'unknown') => {
|
||||
export const downloadSecret = (secrets: FormData["secrets"] = [], env: string = "unknown") => {
|
||||
const finalSecret = secrets.map(({ key, value, valueOverride, overrideAction, comment }) => ({
|
||||
key,
|
||||
value: overrideAction && overrideAction !== SecretActionType.Deleted ? valueOverride : value,
|
||||
comment
|
||||
}));
|
||||
|
||||
let file = '';
|
||||
let file = "";
|
||||
finalSecret.forEach(({ key, value, comment }) => {
|
||||
if (comment) {
|
||||
file += `# ${comment}\n${key}=${value}\n`;
|
||||
@@ -93,7 +93,7 @@ export const downloadSecret = (secrets: FormData['secrets'] = [], env: string =
|
||||
|
||||
const blob = new Blob([file]);
|
||||
const fileDownloadUrl = URL.createObjectURL(blob);
|
||||
const alink = document.createElement('a');
|
||||
const alink = document.createElement("a");
|
||||
alink.href = fileDownloadUrl;
|
||||
alink.download = `${env}.env`;
|
||||
alink.click();
|
||||
@@ -121,7 +121,7 @@ const encryptASecret = (randomBytes: string, key: string, value?: string, commen
|
||||
iv: secretValueIV,
|
||||
tag: secretValueTag
|
||||
} = encryptSymmetric({
|
||||
plaintext: value ?? '',
|
||||
plaintext: value ?? "",
|
||||
key: randomBytes
|
||||
});
|
||||
|
||||
@@ -131,7 +131,7 @@ const encryptASecret = (randomBytes: string, key: string, value?: string, commen
|
||||
iv: secretCommentIV,
|
||||
tag: secretCommentTag
|
||||
} = encryptSymmetric({
|
||||
plaintext: comment ?? '',
|
||||
plaintext: comment ?? "",
|
||||
key: randomBytes
|
||||
});
|
||||
|
||||
@@ -158,18 +158,18 @@ const deepCompareSecrets = (lhs: DecryptedSecret, rhs: any) =>
|
||||
export const transformSecretsToBatchSecretReq = (
|
||||
deletedSecretIds: string[],
|
||||
latestFileKey: any,
|
||||
secrets: FormData['secrets'],
|
||||
secrets: FormData["secrets"],
|
||||
intialValues: DecryptedSecret[] = []
|
||||
) => {
|
||||
// deleted secrets
|
||||
const secretsToBeDeleted: BatchSecretDTO['requests'] = deletedSecretIds.map((id) => ({
|
||||
method: 'DELETE',
|
||||
const secretsToBeDeleted: BatchSecretDTO["requests"] = deletedSecretIds.map((id) => ({
|
||||
method: "DELETE",
|
||||
secret: { _id: id }
|
||||
}));
|
||||
|
||||
const secretsToBeUpdated: BatchSecretDTO['requests'] = [];
|
||||
const secretsToBeCreated: BatchSecretDTO['requests'] = [];
|
||||
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY') as string;
|
||||
const secretsToBeUpdated: BatchSecretDTO["requests"] = [];
|
||||
const secretsToBeCreated: BatchSecretDTO["requests"] = [];
|
||||
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string;
|
||||
|
||||
const randomBytes = latestFileKey
|
||||
? decryptAssymmetric({
|
||||
@@ -178,7 +178,7 @@ export const transformSecretsToBatchSecretReq = (
|
||||
publicKey: latestFileKey.sender.publicKey,
|
||||
privateKey: PRIVATE_KEY
|
||||
})
|
||||
: crypto.randomBytes(16).toString('hex');
|
||||
: crypto.randomBytes(16).toString("hex");
|
||||
|
||||
secrets?.forEach((secret) => {
|
||||
const {
|
||||
@@ -193,9 +193,9 @@ export const transformSecretsToBatchSecretReq = (
|
||||
} = secret;
|
||||
if (!idOverride && overrideAction === SecretActionType.Created) {
|
||||
secretsToBeCreated.push({
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
secret: {
|
||||
type: 'personal',
|
||||
type: "personal",
|
||||
tags,
|
||||
secretName: key,
|
||||
...encryptASecret(randomBytes, key, valueOverride, comment)
|
||||
@@ -205,9 +205,9 @@ export const transformSecretsToBatchSecretReq = (
|
||||
// to be created ones as they don't have server generated id
|
||||
if (!_id) {
|
||||
secretsToBeCreated.push({
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
secret: {
|
||||
type: 'shared',
|
||||
type: "shared",
|
||||
tags,
|
||||
secretName: key,
|
||||
...encryptASecret(randomBytes, key, value, comment)
|
||||
@@ -221,10 +221,10 @@ export const transformSecretsToBatchSecretReq = (
|
||||
const initialSecretValue = intialValues?.find(({ _id: secId }) => secId === _id)!;
|
||||
if (!deepCompareSecrets(initialSecretValue, secret)) {
|
||||
secretsToBeUpdated.push({
|
||||
method: 'PATCH',
|
||||
method: "PATCH",
|
||||
secret: {
|
||||
_id,
|
||||
type: 'shared',
|
||||
type: "shared",
|
||||
tags,
|
||||
secretName: key,
|
||||
...encryptASecret(randomBytes, key, value, comment)
|
||||
@@ -235,16 +235,16 @@ export const transformSecretsToBatchSecretReq = (
|
||||
if (idOverride) {
|
||||
// if action is deleted meaning override has been removed but id is kept to collect at this point
|
||||
if (overrideAction === SecretActionType.Deleted) {
|
||||
secretsToBeDeleted.push({ method: 'DELETE', secret: { _id: idOverride } });
|
||||
secretsToBeDeleted.push({ method: "DELETE", secret: { _id: idOverride } });
|
||||
} else {
|
||||
// if not deleted action then as id is there its an updated
|
||||
const initialSecretValue = intialValues?.find(({ _id: secId }) => secId === _id)!;
|
||||
if (!deepCompareSecrets(initialSecretValue, secret)) {
|
||||
secretsToBeUpdated.push({
|
||||
method: 'PATCH',
|
||||
method: "PATCH",
|
||||
secret: {
|
||||
_id: idOverride,
|
||||
type: 'personal',
|
||||
type: "personal",
|
||||
tags,
|
||||
secretName: key,
|
||||
...encryptASecret(randomBytes, key, valueOverride, comment)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback } from "react";
|
||||
|
||||
import { FormControl, Input, Spinner } from '@app/components/v2';
|
||||
import { useGetProjectSecrets, useGetUserWsKey } from '@app/hooks/api';
|
||||
import { FormControl, Input, Spinner } from "@app/components/v2";
|
||||
import { useGetProjectSecrets, useGetUserWsKey } from "@app/hooks/api";
|
||||
|
||||
type SecretValueProps = {
|
||||
workspaceId: string;
|
||||
@@ -21,7 +21,7 @@ const SecretValue = ({ workspaceId, env, envName, secretKey }: SecretValueProps)
|
||||
const getValue = useCallback(
|
||||
(data: typeof secret) => {
|
||||
const sec = data?.secrets?.find(({ key: secKey }) => secKey === secretKey);
|
||||
return sec?.value || 'Not found';
|
||||
return sec?.value || "Not found";
|
||||
},
|
||||
[secretKey]
|
||||
);
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { CompareSecret } from './CompareSecret';
|
||||
export { CompareSecret } from "./CompareSecret";
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import * as yup from 'yup';
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import * as yup from "yup";
|
||||
|
||||
import { Button, FormControl, Input, ModalClose } from '@app/components/v2';
|
||||
import { Button, FormControl, Input, ModalClose } from "@app/components/v2";
|
||||
|
||||
type Props = {
|
||||
onCreateTag: (tagName: string) => Promise<void>;
|
||||
};
|
||||
|
||||
const createTagSchema = yup.object({
|
||||
name: yup.string().required().trim().label('Tag Name')
|
||||
name: yup.string().required().trim().label("Tag Name")
|
||||
});
|
||||
type FormData = yup.InferType<typeof createTagSchema>;
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
export {CreateTagModal} from './CreateTagModal'
|
||||
export {CreateTagModal} from "./CreateTagModal"
|
||||
@@ -1,8 +1,8 @@
|
||||
/* eslint-disable react/jsx-no-useless-fragment */
|
||||
import { useCallback, useState } from 'react';
|
||||
import { faCircle, faEye, faEyeSlash, faKey, faMinus } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import { useCallback, useState } from "react";
|
||||
import { faCircle, faEye, faEyeSlash, faKey, faMinus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
type Props = {
|
||||
secrets: any[] | undefined;
|
||||
@@ -39,7 +39,7 @@ const DashboardInput = ({
|
||||
<span className="ph-no-capture text-yellow" key={`${val}-${index + 1}`}>
|
||||
{word.slice(0, 2)}
|
||||
<span className="ph-no-capture text-yellow-200/80">{word.slice(2, word.length - 1)}</span>
|
||||
{word.slice(word.length - 1, word.length) === '}' ? (
|
||||
{word.slice(word.length - 1, word.length) === "}" ? (
|
||||
<span className="ph-no-capture text-yellow">
|
||||
{word.slice(word.length - 1, word.length)}
|
||||
</span>
|
||||
@@ -59,29 +59,29 @@ const DashboardInput = ({
|
||||
|
||||
return (
|
||||
<td
|
||||
key={`row-${secret?.key || ''}--`}
|
||||
key={`row-${secret?.key || ""}--`}
|
||||
className={`flex h-10 w-full cursor-default flex-row items-center justify-center ${
|
||||
!(secret?.value || secret?.value === '') ? 'bg-red-800/10' : 'bg-mineshaft-900/30'
|
||||
!(secret?.value || secret?.value === "") ? "bg-red-800/10" : "bg-mineshaft-900/30"
|
||||
}`}
|
||||
>
|
||||
<div className="group relative flex w-full cursor-default flex-col justify-center whitespace-pre">
|
||||
<input
|
||||
value={isOverridden ? secret.valueOverride : secret?.value || ''}
|
||||
value={isOverridden ? secret.valueOverride : secret?.value || ""}
|
||||
readOnly={isReadOnly}
|
||||
className={twMerge(
|
||||
'ph-no-capture no-scrollbar::-webkit-scrollbar duration-50 peer z-10 w-full cursor-default bg-transparent px-2 py-2 font-mono text-sm text-transparent caret-transparent outline-none no-scrollbar',
|
||||
isSecretValueHidden && 'text-transparent focus:text-transparent active:text-transparent'
|
||||
"ph-no-capture no-scrollbar::-webkit-scrollbar duration-50 peer z-10 w-full cursor-default bg-transparent px-2 py-2 font-mono text-sm text-transparent caret-transparent outline-none no-scrollbar",
|
||||
isSecretValueHidden && "text-transparent focus:text-transparent active:text-transparent"
|
||||
)}
|
||||
spellCheck="false"
|
||||
/>
|
||||
<div
|
||||
className={twMerge(
|
||||
'ph-no-capture min-w-16 no-scrollbar::-webkit-scrollbar duration-50 absolute z-0 mt-0.5 flex h-10 w-full cursor-default flex-row overflow-x-scroll whitespace-pre bg-transparent px-2 py-2 font-mono text-sm outline-none no-scrollbar peer-focus:visible',
|
||||
isSecretValueHidden && secret?.value ? 'invisible' : 'visible',
|
||||
"ph-no-capture min-w-16 no-scrollbar::-webkit-scrollbar duration-50 absolute z-0 mt-0.5 flex h-10 w-full cursor-default flex-row overflow-x-scroll whitespace-pre bg-transparent px-2 py-2 font-mono text-sm outline-none no-scrollbar peer-focus:visible",
|
||||
isSecretValueHidden && secret?.value ? "invisible" : "visible",
|
||||
isSecretValueHidden &&
|
||||
secret?.value &&
|
||||
'duration-50 text-bunker-800 group-hover:text-gray-400 peer-focus:text-gray-100 peer-active:text-gray-400',
|
||||
!secret?.value && 'justify-center text-bunker-400'
|
||||
"duration-50 text-bunker-800 group-hover:text-gray-400 peer-focus:text-gray-100 peer-active:text-gray-400",
|
||||
!secret?.value && "justify-center text-bunker-400"
|
||||
)}
|
||||
>
|
||||
{syntaxHighlight(secret?.value)}
|
||||
@@ -89,8 +89,8 @@ const DashboardInput = ({
|
||||
{isSecretValueHidden && secret?.value && (
|
||||
<div className="duration-50 peer absolute z-0 flex h-10 w-full flex-row items-center justify-between text-clip pr-2 text-bunker-400 group-hover:bg-white/[0.00] peer-focus:hidden peer-active:hidden">
|
||||
<div className="no-scrollbar::-webkit-scrollbar flex flex-row items-center overflow-x-scroll px-2 no-scrollbar">
|
||||
{(isOverridden ? secret.valueOverride : secret?.value || '')
|
||||
?.split('')
|
||||
{(isOverridden ? secret.valueOverride : secret?.value || "")
|
||||
?.split("")
|
||||
.map((_a: string, index: number) => (
|
||||
<FontAwesomeIcon
|
||||
key={`${secret?.value}_${index + 1}`}
|
||||
@@ -98,7 +98,7 @@ const DashboardInput = ({
|
||||
icon={faCircle}
|
||||
/>
|
||||
))}
|
||||
{(isOverridden ? secret.valueOverride : secret?.value || '')?.split('').length ===
|
||||
{(isOverridden ? secret.valueOverride : secret?.value || "")?.split("").length ===
|
||||
0 && <span className="text-sm text-bunker-400/80">EMPTY</span>}
|
||||
</div>
|
||||
</div>
|
||||
@@ -130,7 +130,7 @@ export const EnvComparisonRow = ({
|
||||
</td>
|
||||
<td className="flex h-full min-w-[200px] flex-row items-center justify-between lg:min-w-[220px] xl:min-w-[250px]">
|
||||
<div className="flex h-8 cursor-default flex-row items-center truncate">
|
||||
{secrets![0].key || ''}
|
||||
{secrets![0].key || ""}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
@@ -143,7 +143,7 @@ export const EnvComparisonRow = ({
|
||||
{userAvailableEnvs?.map(({ slug }) => (
|
||||
<DashboardInput
|
||||
isReadOnly={isReadOnly}
|
||||
key={`row-${secrets![0].key || ''}-${slug}`}
|
||||
key={`row-${secrets![0].key || ""}-${slug}`}
|
||||
isOverridden={false}
|
||||
secret={getSecretByEnv(slug, secrets)}
|
||||
isSecretValueHidden={areValuesHiddenThisRow && isSecretValueHidden}
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
import { faCheck, faFolder, faXmark } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faCheck, faFolder, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
type Props = {
|
||||
folderInEnv: Record<string, boolean>;
|
||||
@@ -30,8 +30,8 @@ export const FolderComparisonRow = ({
|
||||
<td
|
||||
className={`flex h-10 w-full cursor-default flex-row items-center justify-center ${
|
||||
folderInEnv[slug]
|
||||
? 'bg-mineshaft-900/30 text-green-500/80'
|
||||
: 'bg-red-800/10 text-red-500/80'
|
||||
? "bg-mineshaft-900/30 text-green-500/80"
|
||||
: "bg-red-800/10 text-red-500/80"
|
||||
}`}
|
||||
key={`${folderName}-${slug}`}
|
||||
>
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { EnvComparisonRow } from './EnvComparisonRow';
|
||||
export { EnvComparisonRow } from "./EnvComparisonRow";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import * as yup from 'yup';
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import * as yup from "yup";
|
||||
|
||||
import { Button, FormControl, Input, ModalClose } from '@app/components/v2';
|
||||
import { Button, FormControl, Input, ModalClose } from "@app/components/v2";
|
||||
|
||||
type Props = {
|
||||
onCreateFolder: (folderName: string) => Promise<void>;
|
||||
@@ -16,8 +16,8 @@ const formSchema = yup.object({
|
||||
.string()
|
||||
.required()
|
||||
.trim()
|
||||
.matches(/^[a-zA-Z0-9-_]+$/, 'Folder name cannot contain spaces. Only underscore and dashes')
|
||||
.label('Tag Name')
|
||||
.matches(/^[a-zA-Z0-9-_]+$/, "Folder name cannot contain spaces. Only underscore and dashes")
|
||||
.label("Tag Name")
|
||||
});
|
||||
type TFormData = yup.InferType<typeof formSchema>;
|
||||
|
||||
@@ -62,7 +62,7 @@ export const FolderForm = ({
|
||||
/>
|
||||
<div className="mt-8 flex items-center">
|
||||
<Button className="mr-4" type="submit" isDisabled={isSubmitting} isLoading={isSubmitting}>
|
||||
{isEdit ? 'Save' : 'Create'}
|
||||
{isEdit ? "Save" : "Create"}
|
||||
</Button>
|
||||
<ModalClose asChild>
|
||||
<Button variant="plain" colorSchema="secondary">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { faEdit, faFolder, faXmark } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faEdit, faFolder, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { IconButton, Tooltip } from '@app/components/v2';
|
||||
import { IconButton, Tooltip } from "@app/components/v2";
|
||||
|
||||
type Props = {
|
||||
folders?: Array<{ id: string; name: string }>;
|
||||
@@ -15,7 +15,7 @@ export const FolderSection = ({
|
||||
onFolderUpdate: handleFolderUpdate,
|
||||
onFolderDelete: handleFolderDelete,
|
||||
onFolderOpen: handleFolderOpen,
|
||||
search = '',
|
||||
search = "",
|
||||
folders = []
|
||||
}: Props) => {
|
||||
return (
|
||||
@@ -33,7 +33,7 @@ export const FolderSection = ({
|
||||
<td
|
||||
colSpan={2}
|
||||
className="relative flex w-full min-w-[220px] items-center justify-between overflow-hidden text-ellipsis lg:min-w-[240px] xl:min-w-[280px]"
|
||||
style={{ paddingTop: '0', paddingBottom: '0' }}
|
||||
style={{ paddingTop: "0", paddingBottom: "0" }}
|
||||
>
|
||||
<div
|
||||
className="flex-grow cursor-default p-2"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export { FolderForm } from './FolderForm';
|
||||
export { FolderSection } from './FolderSection';
|
||||
export * from './types';
|
||||
export { FolderForm } from "./FolderForm";
|
||||
export { FolderSection } from "./FolderSection";
|
||||
export * from "./types";
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Fragment, useCallback } from 'react';
|
||||
import { InfiniteData } from '@tanstack/react-query';
|
||||
import { Fragment, useCallback } from "react";
|
||||
import { InfiniteData } from "@tanstack/react-query";
|
||||
|
||||
import { Button, Drawer, DrawerContent } from '@app/components/v2';
|
||||
import timeSince from '@app/ee/utilities/timeSince';
|
||||
import { TWorkspaceSecretSnapshot } from '@app/hooks/api/secretSnapshots/types';
|
||||
import { Button, Drawer, DrawerContent } from "@app/components/v2";
|
||||
import timeSince from "@app/ee/utilities/timeSince";
|
||||
import { TWorkspaceSecretSnapshot } from "@app/hooks/api/secretSnapshots/types";
|
||||
|
||||
type Props = {
|
||||
isDrawerOpen: boolean;
|
||||
@@ -27,9 +27,9 @@ export const PitDrawer = ({
|
||||
fetchNextPage
|
||||
}: Props): JSX.Element => {
|
||||
const getButtonLabel = useCallback((isFirstChild: boolean, isSelectedSnapshot: boolean) => {
|
||||
if (isFirstChild) return 'Current Version';
|
||||
if (isSelectedSnapshot) return 'Currently Viewing';
|
||||
return 'Explore';
|
||||
if (isFirstChild) return "Current Version";
|
||||
if (isSelectedSnapshot) return "Currently Viewing";
|
||||
return "Explore";
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -48,13 +48,13 @@ export const PitDrawer = ({
|
||||
isFullWidth
|
||||
colorSchema={
|
||||
(i === 0 && index === 0 && snapshotId === null) || snapshotId === _id
|
||||
? 'primary'
|
||||
: 'secondary'
|
||||
? "primary"
|
||||
: "secondary"
|
||||
}
|
||||
variant={
|
||||
(i === 0 && index === 0 && snapshotId === null) || snapshotId === _id
|
||||
? 'selected'
|
||||
: 'star'
|
||||
? "selected"
|
||||
: "star"
|
||||
}
|
||||
onClick={() => onSelectSnapshot(_id)}
|
||||
>
|
||||
@@ -75,7 +75,7 @@ export const PitDrawer = ({
|
||||
isDisabled={isFetchingNextPage || !hasNextPage}
|
||||
onClick={fetchNextPage}
|
||||
>
|
||||
{hasNextPage ? 'Load More' : 'End of history'}
|
||||
{hasNextPage ? "Load More" : "End of history"}
|
||||
</Button>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { PitDrawer } from './PitDrawer';
|
||||
export { PitDrawer } from "./PitDrawer";
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import crypto from 'crypto';
|
||||
import crypto from "crypto";
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { faMinus, faPlus, faShuffle } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { faMinus, faPlus, faShuffle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { Button, Input } from '@app/components/v2';
|
||||
import { Button, Input } from "@app/components/v2";
|
||||
|
||||
type Props = {
|
||||
onGenerate: (val: string) => void;
|
||||
@@ -16,7 +16,7 @@ export const GenRandomNumber = ({ onGenerate }: Props) => {
|
||||
const [value, setValue] = useState(32);
|
||||
|
||||
const onGenerateRandomHex = () => {
|
||||
const rand = crypto.randomBytes(value).toString('hex');
|
||||
const rand = crypto.randomBytes(value).toString("hex");
|
||||
onGenerate(rand);
|
||||
};
|
||||
|
||||
@@ -28,7 +28,7 @@ export const GenRandomNumber = ({ onGenerate }: Props) => {
|
||||
leftIcon={<FontAwesomeIcon icon={faShuffle} />}
|
||||
onClick={onGenerateRandomHex}
|
||||
>
|
||||
{t('dashboard.sidebar.generate-random-hex')}
|
||||
{t("dashboard.sidebar.generate-random-hex")}
|
||||
</Button>
|
||||
<div className="rounded-r-md bg-mineshaft p-2">
|
||||
<div className="flex rounded-md border-mineshaft-400 bg-bunker-800 p-1 px-2">
|
||||
|
||||
+17
-17
@@ -1,6 +1,6 @@
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
import { faCircle, faCircleDot, faShuffle } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { useFormContext, useWatch } from "react-hook-form";
|
||||
import { faCircle, faCircleDot, faShuffle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import {
|
||||
Button,
|
||||
@@ -13,11 +13,11 @@ import {
|
||||
PopoverTrigger,
|
||||
Switch,
|
||||
TextArea
|
||||
} from '@app/components/v2';
|
||||
import { useToggle } from '@app/hooks';
|
||||
} from "@app/components/v2";
|
||||
import { useToggle } from "@app/hooks";
|
||||
|
||||
import { FormData, SecretActionType } from '../../DashboardPage.utils';
|
||||
import { GenRandomNumber } from './GenRandomNumber';
|
||||
import { FormData, SecretActionType } from "../../DashboardPage.utils";
|
||||
import { GenRandomNumber } from "./GenRandomNumber";
|
||||
|
||||
type Props = {
|
||||
isDrawerOpen: boolean;
|
||||
@@ -55,7 +55,7 @@ export const SecretDetailDrawer = ({
|
||||
if (isOverridden) {
|
||||
// when user created a new override but then removes
|
||||
if (SecretActionType.Created) {
|
||||
setValue(`secrets.${index}.valueOverride`, '', { shouldDirty: true });
|
||||
setValue(`secrets.${index}.valueOverride`, "", { shouldDirty: true });
|
||||
}
|
||||
setValue(`secrets.${index}.overrideAction`, SecretActionType.Deleted, { shouldDirty: true });
|
||||
} else {
|
||||
@@ -114,7 +114,7 @@ export const SecretDetailDrawer = ({
|
||||
placeholder="EMPTY"
|
||||
onBlur={setCanRevealSecVal.off}
|
||||
onFocus={setCanRevealSecVal.on}
|
||||
type={canRevealSecVal ? 'text' : 'password'}
|
||||
type={canRevealSecVal ? "text" : "password"}
|
||||
rightIcon={
|
||||
<PopoverTrigger disabled={isOverridden || isReadOnly}>
|
||||
<FontAwesomeIcon icon={faShuffle} />
|
||||
@@ -150,7 +150,7 @@ export const SecretDetailDrawer = ({
|
||||
isReadOnly={!isOverridden || isReadOnly}
|
||||
{...register(`secrets.${index}.valueOverride`)}
|
||||
placeholder="EMPTY"
|
||||
type={canRevealSecOverride ? 'text' : 'password'}
|
||||
type={canRevealSecOverride ? "text" : "password"}
|
||||
onBlur={setCanRevealSecOverride.off}
|
||||
onFocus={setCanRevealSecOverride.on}
|
||||
rightIcon={
|
||||
@@ -182,13 +182,13 @@ export const SecretDetailDrawer = ({
|
||||
<FontAwesomeIcon icon={i === 0 ? faCircleDot : faCircle} size="sm" />
|
||||
</div>
|
||||
<div>
|
||||
{new Date(createdAt).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
{new Date(createdAt).toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit"
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1 +1 @@
|
||||
export {SecretDetailDrawer} from './SecretDetailDrawer'
|
||||
export {SecretDetailDrawer} from "./SecretDetailDrawer"
|
||||
@@ -1,14 +1,14 @@
|
||||
import { ChangeEvent, DragEvent } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { faUpload } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import { ChangeEvent, DragEvent } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { faUpload } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { useNotificationContext } from '@app/components/context/Notifications/NotificationProvider';
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
// TODO:(akhilmhdh) convert all the util functions like this into a lib folder grouped by functionalityj
|
||||
import { parseDotEnv } from '@app/components/utilities/parseDotEnv';
|
||||
import { Button } from '@app/components/v2';
|
||||
import { useToggle } from '@app/hooks/useToggle';
|
||||
import { parseDotEnv } from "@app/components/utilities/parseDotEnv";
|
||||
import { Button } from "@app/components/v2";
|
||||
import { useToggle } from "@app/hooks/useToggle";
|
||||
|
||||
type Props = {
|
||||
isSmaller: boolean;
|
||||
@@ -25,9 +25,9 @@ export const SecretDropzone = ({ isSmaller, onParsedEnv, onAddNewSecret }: Props
|
||||
const handleDrag = (e: DragEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (e.type === 'dragenter' || e.type === 'dragover') {
|
||||
if (e.type === "dragenter" || e.type === "dragover") {
|
||||
setDragActive.on();
|
||||
} else if (e.type === 'dragleave') {
|
||||
} else if (e.type === "dragleave") {
|
||||
setDragActive.off();
|
||||
}
|
||||
};
|
||||
@@ -36,8 +36,8 @@ export const SecretDropzone = ({ isSmaller, onParsedEnv, onAddNewSecret }: Props
|
||||
const reader = new FileReader();
|
||||
if (!file) {
|
||||
createNotification({
|
||||
text: `You can't inject files from VS Code. Click 'Reveal in finder', and drag your file directly from the directory where it's located.`,
|
||||
type: 'error',
|
||||
text: "You can't inject files from VS Code. Click 'Reveal in finder', and drag your file directly from the directory where it's located.",
|
||||
type: "error",
|
||||
timeoutMs: 10000
|
||||
});
|
||||
return;
|
||||
@@ -67,7 +67,7 @@ export const SecretDropzone = ({ isSmaller, onParsedEnv, onAddNewSecret }: Props
|
||||
return;
|
||||
}
|
||||
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
e.dataTransfer.dropEffect = "copy";
|
||||
setDragActive.off();
|
||||
parseFile(e.dataTransfer.files[0]);
|
||||
};
|
||||
@@ -84,10 +84,10 @@ export const SecretDropzone = ({ isSmaller, onParsedEnv, onAddNewSecret }: Props
|
||||
onDragOver={handleDrag}
|
||||
onDrop={handleDrop}
|
||||
className={twMerge(
|
||||
'relative mx-0.5 mb-4 mt-4 flex w-full max-w-[calc(100vw-292px)] cursor-pointer items-center justify-center space-x-2 rounded-md bg-mineshaft-900 py-8 px-2 text-mineshaft-200 opacity-60 outline-dashed outline-2 outline-chicago-600 duration-200 hover:opacity-100',
|
||||
isDragActive && 'opacity-100',
|
||||
!isSmaller && 'max-w-3xl flex-col space-y-4 py-20',
|
||||
isLoading && 'bg-bunker-800'
|
||||
"relative mx-0.5 mb-4 mt-4 flex w-full max-w-[calc(100vw-292px)] cursor-pointer items-center justify-center space-x-2 rounded-md bg-mineshaft-900 py-8 px-2 text-mineshaft-200 opacity-60 outline-dashed outline-2 outline-chicago-600 duration-200 hover:opacity-100",
|
||||
isDragActive && "opacity-100",
|
||||
!isSmaller && "max-w-3xl flex-col space-y-4 py-20",
|
||||
isLoading && "bg-bunker-800"
|
||||
)}
|
||||
>
|
||||
{isLoading ? (
|
||||
@@ -97,10 +97,10 @@ export const SecretDropzone = ({ isSmaller, onParsedEnv, onAddNewSecret }: Props
|
||||
) : (
|
||||
<>
|
||||
<div>
|
||||
<FontAwesomeIcon icon={faUpload} size={isSmaller ? '2x' : '5x'} />
|
||||
<FontAwesomeIcon icon={faUpload} size={isSmaller ? "2x" : "5x"} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="">{t(isSmaller ? 'common.drop-zone-keys' : 'common.drop-zone')}</p>
|
||||
<p className="">{t(isSmaller ? "common.drop-zone-keys" : "common.drop-zone")}</p>
|
||||
</div>
|
||||
<input
|
||||
id="fileSelect"
|
||||
@@ -122,7 +122,7 @@ export const SecretDropzone = ({ isSmaller, onParsedEnv, onAddNewSecret }: Props
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}{' '}
|
||||
)}{" "}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { SecretDropzone } from './SecretDropzone';
|
||||
export { SecretDropzone } from "./SecretDropzone";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
import { faCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import { useCallback } from "react";
|
||||
import { useFormContext, useWatch } from "react-hook-form";
|
||||
import { faCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { FormData } from '../../DashboardPage.utils';
|
||||
import { FormData } from "../../DashboardPage.utils";
|
||||
|
||||
type Props = {
|
||||
isReadOnly?: boolean;
|
||||
@@ -29,7 +29,7 @@ export const MaskedInput = ({ isReadOnly, isSecretValueHidden, index, isOverridd
|
||||
<span className="ph-no-capture text-yellow" key={`${val}-${i + 1}`}>
|
||||
{word.slice(0, 2)}
|
||||
<span className="ph-no-capture text-yellow-200/80">{word.slice(2, word.length - 1)}</span>
|
||||
{word.slice(word.length - 1, word.length) === '}' ? (
|
||||
{word.slice(word.length - 1, word.length) === "}" ? (
|
||||
<span className="ph-no-capture text-yellow">
|
||||
{word.slice(word.length - 1, word.length)}
|
||||
</span>
|
||||
@@ -54,9 +54,9 @@ export const MaskedInput = ({ isReadOnly, isSecretValueHidden, index, isOverridd
|
||||
{...register(`secrets.${index}.valueOverride`)}
|
||||
readOnly={isReadOnly}
|
||||
className={twMerge(
|
||||
'ph-no-capture min-w-16 no-scrollbar::-webkit-scrollbar duration-50 peer z-10 w-full bg-transparent px-2 py-2 font-mono text-sm text-transparent caret-white outline-none no-scrollbar',
|
||||
"ph-no-capture min-w-16 no-scrollbar::-webkit-scrollbar duration-50 peer z-10 w-full bg-transparent px-2 py-2 font-mono text-sm text-transparent caret-white outline-none no-scrollbar",
|
||||
!isSecretValueHidden &&
|
||||
'text-transparent focus:text-transparent active:text-transparent'
|
||||
"text-transparent focus:text-transparent active:text-transparent"
|
||||
)}
|
||||
spellCheck="false"
|
||||
/>
|
||||
@@ -65,39 +65,39 @@ export const MaskedInput = ({ isReadOnly, isSecretValueHidden, index, isOverridd
|
||||
{...register(`secrets.${index}.value`)}
|
||||
readOnly={isReadOnly}
|
||||
className={twMerge(
|
||||
'ph-no-capture min-w-16 no-scrollbar::-webkit-scrollbar duration-50 peer z-10 w-full bg-transparent px-2 py-2 font-mono text-sm text-transparent caret-white outline-none no-scrollbar',
|
||||
"ph-no-capture min-w-16 no-scrollbar::-webkit-scrollbar duration-50 peer z-10 w-full bg-transparent px-2 py-2 font-mono text-sm text-transparent caret-white outline-none no-scrollbar",
|
||||
!isSecretValueHidden &&
|
||||
'text-transparent focus:text-transparent active:text-transparent'
|
||||
"text-transparent focus:text-transparent active:text-transparent"
|
||||
)}
|
||||
spellCheck="false"
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={twMerge(
|
||||
'ph-no-capture min-w-16 no-scrollbar::-webkit-scrollbar duration-50 absolute z-0 mt-0.5 flex h-10 w-full flex-row overflow-x-scroll whitespace-pre bg-transparent px-2 py-2 font-mono text-sm outline-none no-scrollbar peer-focus:visible',
|
||||
isSecretValueHidden ? 'invisible' : 'visible',
|
||||
"ph-no-capture min-w-16 no-scrollbar::-webkit-scrollbar duration-50 absolute z-0 mt-0.5 flex h-10 w-full flex-row overflow-x-scroll whitespace-pre bg-transparent px-2 py-2 font-mono text-sm outline-none no-scrollbar peer-focus:visible",
|
||||
isSecretValueHidden ? "invisible" : "visible",
|
||||
isOverridden
|
||||
? 'text-primary-300'
|
||||
: 'duration-50 text-gray-400 group-hover:text-gray-400 peer-focus:text-gray-100 peer-active:text-gray-400'
|
||||
? "text-primary-300"
|
||||
: "duration-50 text-gray-400 group-hover:text-gray-400 peer-focus:text-gray-100 peer-active:text-gray-400"
|
||||
)}
|
||||
>
|
||||
{syntaxHighlight(value || '')}
|
||||
{syntaxHighlight(value || "")}
|
||||
</div>
|
||||
<div
|
||||
className={twMerge(
|
||||
'duration-50 peer absolute z-0 flex h-10 w-full flex-row items-center justify-between text-clip pr-2 text-bunker-400 group-hover:bg-white/[0.00] peer-focus:hidden peer-active:hidden',
|
||||
!isSecretValueHidden ? 'invisible' : 'visible'
|
||||
"duration-50 peer absolute z-0 flex h-10 w-full flex-row items-center justify-between text-clip pr-2 text-bunker-400 group-hover:bg-white/[0.00] peer-focus:hidden peer-active:hidden",
|
||||
!isSecretValueHidden ? "invisible" : "visible"
|
||||
)}
|
||||
>
|
||||
<div className="no-scrollbar::-webkit-scrollbar flex flex-row items-center overflow-x-scroll px-2 no-scrollbar">
|
||||
{value?.split('').map((val, i) => (
|
||||
{value?.split("").map((val, i) => (
|
||||
<FontAwesomeIcon
|
||||
key={`${value}_${val}_${i + 1}`}
|
||||
className="mr-0.5 text-xxs"
|
||||
icon={faCircle}
|
||||
/>
|
||||
))}
|
||||
{value?.split('').length === 0 && (
|
||||
{value?.split("").length === 0 && (
|
||||
<span className="text-sm text-bunker-400/80">EMPTY</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable react/jsx-no-useless-fragment */
|
||||
import { memo, useRef } from 'react';
|
||||
import { Controller, useFieldArray, useFormContext, useWatch } from 'react-hook-form';
|
||||
import { memo, useRef } from "react";
|
||||
import { Controller, useFieldArray, useFormContext, useWatch } from "react-hook-form";
|
||||
import {
|
||||
faCodeBranch,
|
||||
faComment,
|
||||
@@ -9,10 +9,10 @@ import {
|
||||
faPlus,
|
||||
faTags,
|
||||
faXmark
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { cx } from 'cva';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { cx } from "cva";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import {
|
||||
Button,
|
||||
@@ -29,11 +29,11 @@ import {
|
||||
Tag,
|
||||
TextArea,
|
||||
Tooltip
|
||||
} from '@app/components/v2';
|
||||
import { WsTag } from '@app/hooks/api/types';
|
||||
} from "@app/components/v2";
|
||||
import { WsTag } from "@app/hooks/api/types";
|
||||
|
||||
import { FormData, SecretActionType } from '../../DashboardPage.utils';
|
||||
import { MaskedInput } from './MaskedInput';
|
||||
import { FormData, SecretActionType } from "../../DashboardPage.utils";
|
||||
import { MaskedInput } from "./MaskedInput";
|
||||
|
||||
type Props = {
|
||||
index: number;
|
||||
@@ -53,14 +53,14 @@ type Props = {
|
||||
};
|
||||
|
||||
const tagColors = [
|
||||
{ bg: 'bg-[#f1c40f]/40', text: 'text-[#fcf0c3]/70' },
|
||||
{ bg: 'bg-[#cb1c8d]/40', text: 'text-[#f2c6e3]/70' },
|
||||
{ bg: 'bg-[#badc58]/40', text: 'text-[#eef6d5]/70' },
|
||||
{ bg: 'bg-[#ff5400]/40', text: 'text-[#ffddcc]/70' },
|
||||
{ bg: 'bg-[#3AB0FF]/40', text: 'text-[#f0fffd]/70' },
|
||||
{ bg: 'bg-[#6F1AB6]/40', text: 'text-[#FFE5F1]/70' },
|
||||
{ bg: 'bg-[#C40B13]/40', text: 'text-[#FFDEDE]/70' },
|
||||
{ bg: 'bg-[#332FD0]/40', text: 'text-[#DFF6FF]/70' }
|
||||
{ bg: "bg-[#f1c40f]/40", text: "text-[#fcf0c3]/70" },
|
||||
{ bg: "bg-[#cb1c8d]/40", text: "text-[#f2c6e3]/70" },
|
||||
{ bg: "bg-[#badc58]/40", text: "text-[#eef6d5]/70" },
|
||||
{ bg: "bg-[#ff5400]/40", text: "text-[#ffddcc]/70" },
|
||||
{ bg: "bg-[#3AB0FF]/40", text: "text-[#f0fffd]/70" },
|
||||
{ bg: "bg-[#6F1AB6]/40", text: "text-[#FFE5F1]/70" },
|
||||
{ bg: "bg-[#C40B13]/40", text: "text-[#FFDEDE]/70" },
|
||||
{ bg: "bg-[#332FD0]/40", text: "text-[#DFF6FF]/70" }
|
||||
];
|
||||
|
||||
export const SecretInputRow = memo(
|
||||
@@ -111,12 +111,12 @@ export const SecretInputRow = memo(
|
||||
if (isOverridden) {
|
||||
// when user created a new override but then removes
|
||||
if (overrideAction === SecretActionType.Created)
|
||||
setValue(`secrets.${index}.valueOverride`, '');
|
||||
setValue(`secrets.${index}.valueOverride`, "");
|
||||
setValue(`secrets.${index}.overrideAction`, SecretActionType.Deleted, {
|
||||
shouldDirty: true
|
||||
});
|
||||
} else {
|
||||
setValue(`secrets.${index}.valueOverride`, '');
|
||||
setValue(`secrets.${index}.valueOverride`, "");
|
||||
setValue(
|
||||
`secrets.${index}.overrideAction`,
|
||||
idOverride ? SecretActionType.Modified : SecretActionType.Created,
|
||||
@@ -147,7 +147,7 @@ export const SecretInputRow = memo(
|
||||
secKey?.toUpperCase().includes(searchTerm?.toUpperCase()) ||
|
||||
tags
|
||||
?.map((tag) => tag.name)
|
||||
.join(' ')
|
||||
.join(" ")
|
||||
?.toUpperCase()
|
||||
.includes(searchTerm?.toUpperCase()) ||
|
||||
secComment?.toUpperCase().includes(searchTerm?.toUpperCase())
|
||||
@@ -168,7 +168,7 @@ export const SecretInputRow = memo(
|
||||
render={({ fieldState: { error }, field }) => (
|
||||
<HoverCard openDelay={0} open={error?.message ? undefined : false}>
|
||||
<HoverCardTrigger asChild>
|
||||
<td className={cx(error?.message ? 'rounded ring ring-red/50' : null)}>
|
||||
<td className={cx(error?.message ? "rounded ring ring-red/50" : null)}>
|
||||
<div className="relative flex w-full min-w-[220px] items-center justify-end lg:min-w-[240px] xl:min-w-[280px]">
|
||||
<Input
|
||||
autoComplete="off"
|
||||
@@ -246,7 +246,7 @@ export const SecretInputRow = memo(
|
||||
hideCloseBtn
|
||||
>
|
||||
<div className="mb-2 px-2 text-center text-sm font-medium text-bunker-200">
|
||||
Add tags to {secKey || 'this secret'}
|
||||
Add tags to {secKey || "this secret"}
|
||||
</div>
|
||||
<div className="flex flex-col space-y-1">
|
||||
{wsTags?.map((wsTag) => (
|
||||
@@ -254,8 +254,8 @@ export const SecretInputRow = memo(
|
||||
variant="plain"
|
||||
size="sm"
|
||||
className={twMerge(
|
||||
'justify-start bg-mineshaft-600 text-bunker-100 hover:bg-mineshaft-500',
|
||||
selectedTagIds?.[wsTag.slug] && 'text-primary'
|
||||
"justify-start bg-mineshaft-600 text-bunker-100 hover:bg-mineshaft-500",
|
||||
selectedTagIds?.[wsTag.slug] && "text-primary"
|
||||
)}
|
||||
onClick={() => onSelectTag(wsTag)}
|
||||
leftIcon={
|
||||
@@ -296,8 +296,8 @@ export const SecretInputRow = memo(
|
||||
<IconButton
|
||||
variant="plain"
|
||||
className={twMerge(
|
||||
'mt-0.5 w-0 overflow-hidden p-0 group-hover:ml-1 group-hover:w-7',
|
||||
isOverridden && 'ml-1 w-7 text-primary'
|
||||
"mt-0.5 w-0 overflow-hidden p-0 group-hover:ml-1 group-hover:w-7",
|
||||
isOverridden && "ml-1 w-7 text-primary"
|
||||
)}
|
||||
onClick={onSecretOverride}
|
||||
size="md"
|
||||
@@ -312,14 +312,14 @@ export const SecretInputRow = memo(
|
||||
</div>
|
||||
)}
|
||||
<Tooltip content="Comment">
|
||||
<div className={`mt-0.5 overflow-hidden `}>
|
||||
<div className="mt-0.5 overflow-hidden ">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<IconButton
|
||||
className={twMerge(
|
||||
'w-7 overflow-hidden p-0',
|
||||
'w-0 group-hover:w-7 data-[state=open]:w-7',
|
||||
hasComment ? 'w-7 text-primary' : 'group-hover:w-7'
|
||||
"w-7 overflow-hidden p-0",
|
||||
"w-0 group-hover:w-7 data-[state=open]:w-7",
|
||||
hasComment ? "w-7 text-primary" : "group-hover:w-7"
|
||||
)}
|
||||
variant="plain"
|
||||
size="md"
|
||||
@@ -383,4 +383,4 @@ export const SecretInputRow = memo(
|
||||
}
|
||||
);
|
||||
|
||||
SecretInputRow.displayName = 'SecretInputRow';
|
||||
SecretInputRow.displayName = "SecretInputRow";
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { SecretInputRow } from './SecretInputRow';
|
||||
export { SecretInputRow } from "./SecretInputRow";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { faArrowDown, faArrowUp } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faArrowDown, faArrowUp } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { IconButton } from '@app/components/v2';
|
||||
import { IconButton } from "@app/components/v2";
|
||||
|
||||
type Props = {
|
||||
sortDir: 'asc' | 'desc';
|
||||
sortDir: "asc" | "desc";
|
||||
onSort: () => void;
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ export const SecretTableHeader = ({ sortDir, onSort }: Props): JSX.Element => (
|
||||
<div className="text-md inline-flex items-end font-medium">
|
||||
Key
|
||||
<IconButton variant="plain" className="ml-2" ariaLabel="sort" onClick={onSort}>
|
||||
<FontAwesomeIcon icon={sortDir === 'asc' ? faArrowDown : faArrowUp} />
|
||||
<FontAwesomeIcon icon={sortDir === "asc" ? faArrowDown : faArrowUp} />
|
||||
</IconButton>
|
||||
</div>
|
||||
<div className="flex w-max flex-row items-center justify-end">
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { SecretTableHeader } from './SecretTableHeader';
|
||||
export { SecretTableHeader } from "./SecretTableHeader";
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { DashboardPage } from './DashboardPage';
|
||||
export { DashboardPage } from "./DashboardPage";
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useRouter } from 'next/router';
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import NavHeader from '@app/components/navigation/NavHeader';
|
||||
import NavHeader from "@app/components/navigation/NavHeader";
|
||||
|
||||
import { SAProjectLevelPermissionsTable } from './components/SAProjectLevelPermissionsTable';
|
||||
import { SAProjectLevelPermissionsTable } from "./components/SAProjectLevelPermissionsTable";
|
||||
import {
|
||||
CopyServiceAccountPublicKeySection,
|
||||
ServiceAccountNameChangeSection
|
||||
} from './components';
|
||||
} from "./components";
|
||||
|
||||
export const CreateServiceAccountPage = () => {
|
||||
const router = useRouter();
|
||||
@@ -24,7 +24,7 @@ export const CreateServiceAccountPage = () => {
|
||||
A service account represents a machine identity such as a VM or application client.
|
||||
</p>
|
||||
</div>
|
||||
{typeof serviceAccountId === 'string' && (
|
||||
{typeof serviceAccountId === "string" && (
|
||||
<div className="max-w-8xl mx-6">
|
||||
<ServiceAccountNameChangeSection
|
||||
serviceAccountId={serviceAccountId}
|
||||
|
||||
+5
-5
@@ -1,9 +1,9 @@
|
||||
import { useEffect } from 'react';
|
||||
import { faCheck, faCopy } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { useEffect } from "react";
|
||||
import { faCheck, faCopy } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { IconButton } from '@app/components/v2';
|
||||
import { useToggle } from '@app/hooks';
|
||||
import { IconButton } from "@app/components/v2";
|
||||
import { useToggle } from "@app/hooks";
|
||||
|
||||
type Props = {
|
||||
serviceAccountId: string;
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export { CopyServiceAccountIDSection } from './CopyServiceAccountIDSection';
|
||||
export { CopyServiceAccountIDSection } from "./CopyServiceAccountIDSection";
|
||||
+6
-6
@@ -1,10 +1,10 @@
|
||||
import { useEffect } from 'react';
|
||||
import { faCheck, faCopy } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { useEffect } from "react";
|
||||
import { faCheck, faCopy } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { IconButton } from '@app/components/v2';
|
||||
import { useToggle } from '@app/hooks';
|
||||
import { useGetServiceAccountById } from '@app/hooks/api';
|
||||
import { IconButton } from "@app/components/v2";
|
||||
import { useToggle } from "@app/hooks";
|
||||
import { useGetServiceAccountById } from "@app/hooks/api";
|
||||
|
||||
type Props = {
|
||||
serviceAccountId: string;
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export { CopyServiceAccountPublicKeySection } from './CopyServiceAccountPublicKeySection';
|
||||
export { CopyServiceAccountPublicKeySection } from "./CopyServiceAccountPublicKeySection";
|
||||
+29
-29
@@ -1,18 +1,18 @@
|
||||
import { useState } from 'react';
|
||||
import { Controller,useForm } from 'react-hook-form';
|
||||
import { useState } from "react";
|
||||
import { Controller,useForm } from "react-hook-form";
|
||||
import {
|
||||
faKey,
|
||||
faMagnifyingGlass,
|
||||
faPlus,
|
||||
faTrash} from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import * as yup from 'yup';
|
||||
faTrash} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import * as yup from "yup";
|
||||
|
||||
import {
|
||||
decryptAssymmetric,
|
||||
encryptAssymmetric,
|
||||
verifyPrivateKey} from '@app/components/utilities/cryptography/crypto';
|
||||
verifyPrivateKey} from "@app/components/utilities/cryptography/crypto";
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
@@ -33,21 +33,21 @@ import {
|
||||
Td,
|
||||
Th,
|
||||
THead,
|
||||
Tr} from '@app/components/v2';
|
||||
import { usePopUp } from '@app/hooks';
|
||||
Tr} from "@app/components/v2";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import {
|
||||
useCreateServiceAccountProjectLevelPermission,
|
||||
useDeleteServiceAccountProjectLevelPermission,
|
||||
useGetServiceAccountById,
|
||||
useGetServiceAccountProjectLevelPermissions,
|
||||
useGetUserWorkspaces
|
||||
} from '@app/hooks/api';
|
||||
import getLatestFileKey from '@app/pages/api/workspace/getLatestFileKey';
|
||||
} from "@app/hooks/api";
|
||||
import getLatestFileKey from "@app/pages/api/workspace/getLatestFileKey";
|
||||
|
||||
const createProjectLevelPermissionSchema = yup.object({
|
||||
privateKey: yup.string().required().label('Private Key'),
|
||||
workspace: yup.string().required().label('Workspace'),
|
||||
environment: yup.string().required().label('Environment'),
|
||||
privateKey: yup.string().required().label("Private Key"),
|
||||
workspace: yup.string().required().label("Workspace"),
|
||||
environment: yup.string().required().label("Environment"),
|
||||
permissions: yup.object().shape({
|
||||
read: yup.boolean().required(),
|
||||
write: yup.boolean().required()
|
||||
@@ -65,7 +65,7 @@ export const SAProjectLevelPermissionsTable = ({
|
||||
}: Props): JSX.Element => {
|
||||
const { data: serviceAccount } = useGetServiceAccountById(serviceAccountId);
|
||||
const { data: userWorkspaces, isLoading: isUserWorkspacesLoading } = useGetUserWorkspaces();
|
||||
const [searchPermissions, setSearchPermissions] = useState('');
|
||||
const [searchPermissions, setSearchPermissions] = useState("");
|
||||
|
||||
const { data: serviceAccountWorkspacePermissions, isLoading: isPermissionsLoading } = useGetServiceAccountProjectLevelPermissions(serviceAccountId);
|
||||
|
||||
@@ -73,8 +73,8 @@ export const SAProjectLevelPermissionsTable = ({
|
||||
const deleteServiceAccountProjectLevelPermission = useDeleteServiceAccountProjectLevelPermission();
|
||||
|
||||
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||
'addProjectLevelPermission',
|
||||
'removeProjectLevelPermission',
|
||||
"addProjectLevelPermission",
|
||||
"removeProjectLevelPermission",
|
||||
] as const);
|
||||
|
||||
const [, setSelectedWorkspace] = useState<undefined | string>(undefined);
|
||||
@@ -106,7 +106,7 @@ export const SAProjectLevelPermissionsTable = ({
|
||||
publicKey: serviceAccount.publicKey
|
||||
});
|
||||
|
||||
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY') as string;
|
||||
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string;
|
||||
|
||||
const key = decryptAssymmetric({
|
||||
ciphertext: latestKey.encryptedKey,
|
||||
@@ -130,7 +130,7 @@ export const SAProjectLevelPermissionsTable = ({
|
||||
encryptedKey: ciphertext,
|
||||
nonce
|
||||
});
|
||||
handlePopUpClose('addProjectLevelPermission');
|
||||
handlePopUpClose("addProjectLevelPermission");
|
||||
}
|
||||
|
||||
const onRemoveProjectLevelPermission = async () => {
|
||||
@@ -139,7 +139,7 @@ export const SAProjectLevelPermissionsTable = ({
|
||||
serviceAccountId,
|
||||
serviceAccountWorkspacePermissionId
|
||||
});
|
||||
handlePopUpClose('removeProjectLevelPermission');
|
||||
handlePopUpClose("removeProjectLevelPermission");
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -157,7 +157,7 @@ export const SAProjectLevelPermissionsTable = ({
|
||||
<Button
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={() => {
|
||||
handlePopUpOpen('addProjectLevelPermission')
|
||||
handlePopUpOpen("addProjectLevelPermission")
|
||||
reset();
|
||||
}}
|
||||
>
|
||||
@@ -208,7 +208,7 @@ export const SAProjectLevelPermissionsTable = ({
|
||||
<IconButton
|
||||
ariaLabel="delete"
|
||||
colorSchema="danger"
|
||||
onClick={() => handlePopUpOpen('removeProjectLevelPermission', { _id })}
|
||||
onClick={() => handlePopUpOpen("removeProjectLevelPermission", { _id })}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} />
|
||||
</IconButton>
|
||||
@@ -230,7 +230,7 @@ export const SAProjectLevelPermissionsTable = ({
|
||||
<Modal
|
||||
isOpen={popUp?.addProjectLevelPermission?.isOpen}
|
||||
onOpenChange={(isOpen) => {
|
||||
handlePopUpToggle('addProjectLevelPermission', isOpen);
|
||||
handlePopUpToggle("addProjectLevelPermission", isOpen);
|
||||
}}
|
||||
>
|
||||
<ModalContent
|
||||
@@ -337,12 +337,12 @@ export const SAProjectLevelPermissionsTable = ({
|
||||
render={({ field: { onChange, value }, fieldState: { error }}) => {
|
||||
const options = [
|
||||
{
|
||||
label: 'Read (default)',
|
||||
value: 'read'
|
||||
label: "Read (default)",
|
||||
value: "read"
|
||||
},
|
||||
{
|
||||
label: 'Write',
|
||||
value: 'write'
|
||||
label: "Write",
|
||||
value: "write"
|
||||
}
|
||||
];
|
||||
|
||||
@@ -360,7 +360,7 @@ export const SAProjectLevelPermissionsTable = ({
|
||||
key={optionValue}
|
||||
className="data-[state=checked]:bg-primary"
|
||||
isChecked={value[optionValue]}
|
||||
isDisabled={optionValue === 'read'}
|
||||
isDisabled={optionValue === "read"}
|
||||
onCheckedChange={(state) => {
|
||||
onChange({
|
||||
...value,
|
||||
@@ -399,7 +399,7 @@ export const SAProjectLevelPermissionsTable = ({
|
||||
isOpen={popUp.removeProjectLevelPermission.isOpen}
|
||||
deleteKey="remove"
|
||||
title="Do you want to remove this permission from the service account?"
|
||||
onChange={(isOpen) => handlePopUpToggle('removeProjectLevelPermission', isOpen)}
|
||||
onChange={(isOpen) => handlePopUpToggle("removeProjectLevelPermission", isOpen)}
|
||||
onDeleteApproved={onRemoveProjectLevelPermission}
|
||||
/>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export { SAProjectLevelPermissionsTable } from './SAProjectLevelPermissionsTable';
|
||||
export { SAProjectLevelPermissionsTable } from "./SAProjectLevelPermissionsTable";
|
||||
+9
-9
@@ -1,21 +1,21 @@
|
||||
import { useEffect } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { faCheck } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import * as yup from 'yup';
|
||||
import { useEffect } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { faCheck } 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,
|
||||
FormControl,
|
||||
Input} from '@app/components/v2';
|
||||
Input} from "@app/components/v2";
|
||||
import {
|
||||
useGetServiceAccountById,
|
||||
useRenameServiceAccount
|
||||
} from '@app/hooks/api';
|
||||
} from "@app/hooks/api";
|
||||
|
||||
const formSchema = yup.object({
|
||||
name: yup.string().required().label('Service Account Name')
|
||||
name: yup.string().required().label("Service Account Name")
|
||||
});
|
||||
|
||||
type FormData = yup.InferType<typeof formSchema>;
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export { ServiceAccountNameChangeSection } from './ServiceAccountNameChangeSection';
|
||||
export { ServiceAccountNameChangeSection } from "./ServiceAccountNameChangeSection";
|
||||
@@ -1,4 +1,4 @@
|
||||
export { CopyServiceAccountIDSection } from './CopyServiceAccountIDSection';
|
||||
export { CopyServiceAccountPublicKeySection } from './CopyServiceAccountPublicKeySection';
|
||||
export { SAProjectLevelPermissionsTable } from './SAProjectLevelPermissionsTable';
|
||||
export { ServiceAccountNameChangeSection } from './ServiceAccountNameChangeSection';
|
||||
export { CopyServiceAccountIDSection } from "./CopyServiceAccountIDSection";
|
||||
export { CopyServiceAccountPublicKeySection } from "./CopyServiceAccountPublicKeySection";
|
||||
export { SAProjectLevelPermissionsTable } from "./SAProjectLevelPermissionsTable";
|
||||
export { ServiceAccountNameChangeSection } from "./ServiceAccountNameChangeSection";
|
||||
@@ -1 +1 @@
|
||||
export { CreateServiceAccountPage } from './CreateServiceAccountPage';
|
||||
export { CreateServiceAccountPage } from "./CreateServiceAccountPage";
|
||||
@@ -1,15 +1,15 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { plans } from 'public/data/frequentConstants';
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { plans } from "public/data/frequentConstants";
|
||||
|
||||
import { useNotificationContext } from '@app/components/context/Notifications/NotificationProvider';
|
||||
import NavHeader from '@app/components/navigation/NavHeader';
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import NavHeader from "@app/components/navigation/NavHeader";
|
||||
import {
|
||||
decryptAssymmetric,
|
||||
encryptAssymmetric
|
||||
} from '@app/components/utilities/cryptography/crypto';
|
||||
import { useOrganization, useSubscription, useUser, useWorkspace } from '@app/context';
|
||||
} from "@app/components/utilities/cryptography/crypto";
|
||||
import { useOrganization, useSubscription, useUser, useWorkspace } from "@app/context";
|
||||
import {
|
||||
useAddIncidentContact,
|
||||
useAddUserToOrg,
|
||||
@@ -22,14 +22,14 @@ import {
|
||||
useRenameOrg,
|
||||
useUpdateOrgUserRole,
|
||||
useUploadWsKey
|
||||
} from '@app/hooks/api';
|
||||
} from "@app/hooks/api";
|
||||
|
||||
import {
|
||||
OrgIncidentContactsTable,
|
||||
OrgMembersTable,
|
||||
OrgNameChangeSection,
|
||||
OrgServiceAccountsTable
|
||||
} from './components';
|
||||
} from "./components";
|
||||
|
||||
export const OrgSettingsPage = () => {
|
||||
const host = window.location.origin;
|
||||
@@ -41,12 +41,12 @@ export const OrgSettingsPage = () => {
|
||||
const { subscription } = useSubscription();
|
||||
const { createNotification } = useNotificationContext();
|
||||
|
||||
const orgId = currentOrg?._id || '';
|
||||
const orgId = currentOrg?._id || "";
|
||||
|
||||
const { data: orgUsers, isLoading: isOrgUserLoading } = useGetOrgUsers(orgId);
|
||||
const { data: workspaceMemberships, isLoading: IsWsMembershipLoading } =
|
||||
useGetUserWorkspaceMemberships(orgId);
|
||||
const { data: wsKey } = useGetUserWsKey(currentWorkspace?._id || '');
|
||||
const { data: wsKey } = useGetUserWsKey(currentWorkspace?._id || "");
|
||||
const { data: incidentContact, isLoading: IsIncidentContactLoading } =
|
||||
useGetOrgIncidentContact(orgId);
|
||||
|
||||
@@ -58,7 +58,7 @@ export const OrgSettingsPage = () => {
|
||||
const addIncidentContact = useAddIncidentContact();
|
||||
const removeIncidentContact = useDeleteIncidentContact();
|
||||
|
||||
const [completeInviteLink, setcompleteInviteLink] = useState<string | undefined>('');
|
||||
const [completeInviteLink, setcompleteInviteLink] = useState<string | undefined>("");
|
||||
|
||||
const isMoreUsersNotAllowed = subscription?.memberLimit ? (subscription.membersUsed >= subscription.memberLimit) : false;
|
||||
|
||||
@@ -68,14 +68,14 @@ export const OrgSettingsPage = () => {
|
||||
try {
|
||||
await renameOrg.mutateAsync({ orgId: currentOrg?._id, newOrgName: name });
|
||||
createNotification({
|
||||
text: 'Successfully renamed organization',
|
||||
type: 'success'
|
||||
text: "Successfully renamed organization",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to rename organization',
|
||||
type: 'error'
|
||||
text: "Failed to rename organization",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -86,14 +86,14 @@ export const OrgSettingsPage = () => {
|
||||
try {
|
||||
await removeUserOrgMembership.mutateAsync({ orgId: currentOrg?._id, membershipId });
|
||||
createNotification({
|
||||
text: 'Successfully removed user from org',
|
||||
type: 'success'
|
||||
text: "Successfully removed user from org",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to remove user from the organization',
|
||||
type: 'error'
|
||||
text: "Failed to remove user from the organization",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -110,15 +110,15 @@ export const OrgSettingsPage = () => {
|
||||
// only show this notification when email is configured. A [completeInviteLink] will not be sent if smtp is configured
|
||||
if (!data.completeInviteLink) {
|
||||
createNotification({
|
||||
text: 'Successfully invited user to the organization.',
|
||||
type: 'success'
|
||||
text: "Successfully invited user to the organization.",
|
||||
type: "success"
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to invite user to org',
|
||||
type: 'error'
|
||||
text: "Failed to invite user to org",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -129,21 +129,21 @@ export const OrgSettingsPage = () => {
|
||||
try {
|
||||
await updateOrgUserRole.mutateAsync({ organizationId: currentOrg?._id, membershipId, role });
|
||||
createNotification({
|
||||
text: 'Successfully updated user role',
|
||||
type: 'success'
|
||||
text: "Successfully updated user role",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to update user role',
|
||||
type: 'error'
|
||||
text: "Failed to update user role",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onGrantUserAccess = async (userId: string, publicKey: string) => {
|
||||
try {
|
||||
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY') as string;
|
||||
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string;
|
||||
if (!PRIVATE_KEY || !wsKey) return;
|
||||
|
||||
// assymmetrically decrypt symmetric key with local private key
|
||||
@@ -164,13 +164,13 @@ export const OrgSettingsPage = () => {
|
||||
userId,
|
||||
nonce,
|
||||
encryptedKey: ciphertext,
|
||||
workspaceId: currentWorkspace?._id || ''
|
||||
workspaceId: currentWorkspace?._id || ""
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: 'Failed to grant access to user',
|
||||
type: 'error'
|
||||
text: "Failed to grant access to user",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -181,14 +181,14 @@ export const OrgSettingsPage = () => {
|
||||
try {
|
||||
await addIncidentContact.mutateAsync({ orgId, email });
|
||||
createNotification({
|
||||
text: 'Successfully added incident contact',
|
||||
type: 'success'
|
||||
text: "Successfully added incident contact",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to add incident contact',
|
||||
type: 'error'
|
||||
text: "Failed to add incident contact",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -199,14 +199,14 @@ export const OrgSettingsPage = () => {
|
||||
try {
|
||||
await removeIncidentContact.mutateAsync({ orgId, email });
|
||||
createNotification({
|
||||
text: 'Successfully removed incident contact',
|
||||
type: 'success'
|
||||
text: "Successfully removed incident contact",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to remove incident contact',
|
||||
type: 'error'
|
||||
text: "Failed to remove incident contact",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -234,25 +234,25 @@ export const OrgSettingsPage = () => {
|
||||
//
|
||||
return (
|
||||
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||
<NavHeader pageName={t('settings.org.title')} />
|
||||
<NavHeader pageName={t("settings.org.title")} />
|
||||
<div className="my-8 ml-8 max-w-5xl">
|
||||
<p className="text-3xl font-semibold text-gray-200">{t('settings.org.title')}</p>
|
||||
<p className="text-base font-normal text-gray-400">{t('settings.org.description')}</p>
|
||||
<p className="text-3xl font-semibold text-gray-200">{t("settings.org.title")}</p>
|
||||
<p className="text-base font-normal text-gray-400">{t("settings.org.description")}</p>
|
||||
</div>
|
||||
<div className="max-w-8xl ml-6 mr-6 flex flex-col text-mineshaft-50">
|
||||
<OrgNameChangeSection orgName={currentOrg?.name} onOrgNameChange={onRenameOrg} />
|
||||
<div className="mb-6 w-full rounded-md bg-white/5 p-6">
|
||||
<p className="mr-4 mb-4 text-xl font-semibold text-white">
|
||||
{t('section.members.org-members')}
|
||||
{t("section.members.org-members")}
|
||||
</p>
|
||||
<OrgMembersTable
|
||||
isLoading={isOrgUserLoading || IsWsMembershipLoading}
|
||||
isMoreUserNotAllowed={isMoreUsersNotAllowed}
|
||||
orgName={currentOrg?.name || ''}
|
||||
orgName={currentOrg?.name || ""}
|
||||
members={orgUsers}
|
||||
workspaceMemberships={workspaceMemberships}
|
||||
onInviteMember={onAddUserToOrg}
|
||||
userId={user?._id || ''}
|
||||
userId={user?._id || ""}
|
||||
onRemoveMember={onRemoveUserOrgMembership}
|
||||
onRoleChange={onUpdateOrgUserRole}
|
||||
onGrantAccess={onGrantUserAccess}
|
||||
@@ -268,10 +268,10 @@ export const OrgSettingsPage = () => {
|
||||
<div className="flex w-full max-w-5xl flex-row items-center justify-between">
|
||||
<div className="flex w-full max-w-3xl flex-col justify-between">
|
||||
<p className="mb-3 min-w-max text-xl font-semibold">
|
||||
{t('section.incident.incident-contacts')}
|
||||
{t("section.incident.incident-contacts")}
|
||||
</p>
|
||||
<p className="mb-2 min-w-max text-xs text-gray-500">
|
||||
{t('section.incident.incident-contacts-description')}
|
||||
{t("section.incident.incident-contacts-description")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+24
-24
@@ -1,14 +1,14 @@
|
||||
import { useState } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import {
|
||||
faContactBook,
|
||||
faMagnifyingGlass,
|
||||
faPlus,
|
||||
faTrash
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import * as yup from 'yup';
|
||||
} 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,
|
||||
@@ -28,10 +28,10 @@ import {
|
||||
Th,
|
||||
THead,
|
||||
Tr
|
||||
} from '@app/components/v2';
|
||||
import { usePopUp } from '@app/hooks';
|
||||
import { useFetchServerStatus } from '@app/hooks/api/serverDetails';
|
||||
import { IncidentContact } from '@app/hooks/api/types';
|
||||
} from "@app/components/v2";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { useFetchServerStatus } from "@app/hooks/api/serverDetails";
|
||||
import { IncidentContact } from "@app/hooks/api/types";
|
||||
|
||||
type Props = {
|
||||
isLoading?: boolean;
|
||||
@@ -41,7 +41,7 @@ type Props = {
|
||||
};
|
||||
|
||||
const addContactFormSchema = yup.object({
|
||||
email: yup.string().email().required().label('Email').trim()
|
||||
email: yup.string().email().required().label("Email").trim()
|
||||
});
|
||||
|
||||
type TAddContactForm = yup.InferType<typeof addContactFormSchema>;
|
||||
@@ -52,12 +52,12 @@ export const OrgIncidentContactsTable = ({
|
||||
onRemoveContact,
|
||||
isLoading
|
||||
}: Props) => {
|
||||
const [searchContact, setSearchContact] = useState('');
|
||||
const [searchContact, setSearchContact] = useState("");
|
||||
const {data: serverDetails } = useFetchServerStatus()
|
||||
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||
'addContact',
|
||||
'removeContact',
|
||||
'setUpEmail'
|
||||
"addContact",
|
||||
"removeContact",
|
||||
"setUpEmail"
|
||||
] as const);
|
||||
|
||||
const {
|
||||
@@ -69,14 +69,14 @@ export const OrgIncidentContactsTable = ({
|
||||
|
||||
const onAddIncidentContact = ({ email }: TAddContactForm) => {
|
||||
onAddContact(email);
|
||||
handlePopUpClose('addContact');
|
||||
handlePopUpClose("addContact");
|
||||
reset();
|
||||
};
|
||||
|
||||
const onRemoveIncidentContact = async () => {
|
||||
const incidentContactEmail = (popUp?.removeContact?.data as { email: string })?.email;
|
||||
await onRemoveContact(incidentContactEmail);
|
||||
handlePopUpClose('removeContact');
|
||||
handlePopUpClose("removeContact");
|
||||
};
|
||||
|
||||
const filteredContacts = contacts.filter(({ email }) =>
|
||||
@@ -99,9 +99,9 @@ export const OrgIncidentContactsTable = ({
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={() => {
|
||||
if (serverDetails?.emailConfigured){
|
||||
handlePopUpOpen('addContact');
|
||||
handlePopUpOpen("addContact");
|
||||
} else {
|
||||
handlePopUpOpen('setUpEmail');
|
||||
handlePopUpOpen("setUpEmail");
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -127,7 +127,7 @@ export const OrgIncidentContactsTable = ({
|
||||
<IconButton
|
||||
ariaLabel="delete"
|
||||
colorSchema="danger"
|
||||
onClick={() => handlePopUpOpen('removeContact', { email })}
|
||||
onClick={() => handlePopUpOpen("removeContact", { email })}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} />
|
||||
</IconButton>
|
||||
@@ -144,7 +144,7 @@ export const OrgIncidentContactsTable = ({
|
||||
<Modal
|
||||
isOpen={popUp?.addContact?.isOpen}
|
||||
onOpenChange={(isOpen) => {
|
||||
handlePopUpToggle('addContact', isOpen);
|
||||
handlePopUpToggle("addContact", isOpen);
|
||||
reset();
|
||||
}}
|
||||
>
|
||||
@@ -176,7 +176,7 @@ export const OrgIncidentContactsTable = ({
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
variant="plain"
|
||||
onClick={() => handlePopUpClose('addContact')}
|
||||
onClick={() => handlePopUpClose("addContact")}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
@@ -188,12 +188,12 @@ export const OrgIncidentContactsTable = ({
|
||||
isOpen={popUp.removeContact.isOpen}
|
||||
deleteKey="remove"
|
||||
title="Do you want to remove this email from incident contact?"
|
||||
onChange={(isOpen) => handlePopUpToggle('removeContact', isOpen)}
|
||||
onChange={(isOpen) => handlePopUpToggle("removeContact", isOpen)}
|
||||
onDeleteApproved={onRemoveIncidentContact}
|
||||
/>
|
||||
<EmailServiceSetupModal
|
||||
isOpen={popUp.setUpEmail?.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle('setUpEmail', isOpen)}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("setUpEmail", isOpen)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export { OrgIncidentContactsTable } from './OrgIncidentContactsTable';
|
||||
export { OrgIncidentContactsTable } from "./OrgIncidentContactsTable";
|
||||
|
||||
+35
-35
@@ -1,10 +1,10 @@
|
||||
import { Dispatch, SetStateAction, useEffect, useMemo, useState } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { useRouter } from 'next/router';
|
||||
import { faCheck, faCopy, faMagnifyingGlass, faPlus, faTrash, faUsers } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import * as yup from 'yup';
|
||||
import { Dispatch, SetStateAction, useEffect, useMemo, useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { useRouter } from "next/router";
|
||||
import { faCheck, faCopy, faMagnifyingGlass, faPlus, faTrash, faUsers } 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,
|
||||
@@ -26,10 +26,10 @@ import {
|
||||
Th,
|
||||
THead,
|
||||
Tr,
|
||||
UpgradePlanModal} from '@app/components/v2';
|
||||
import { usePopUp, useToggle } from '@app/hooks';
|
||||
import { useFetchServerStatus } from '@app/hooks/api/serverDetails';
|
||||
import { OrgUser, Workspace } from '@app/hooks/api/types';
|
||||
UpgradePlanModal} from "@app/components/v2";
|
||||
import { usePopUp, useToggle } from "@app/hooks";
|
||||
import { useFetchServerStatus } from "@app/hooks/api/serverDetails";
|
||||
import { OrgUser, Workspace } from "@app/hooks/api/types";
|
||||
|
||||
type Props = {
|
||||
members?: OrgUser[];
|
||||
@@ -48,7 +48,7 @@ type Props = {
|
||||
};
|
||||
|
||||
const addMemberFormSchema = yup.object({
|
||||
email: yup.string().email().required().label('Email').trim()
|
||||
email: yup.string().email().required().label("Email").trim()
|
||||
});
|
||||
|
||||
type TAddMemberForm = yup.InferType<typeof addMemberFormSchema>;
|
||||
@@ -68,14 +68,14 @@ export const OrgMembersTable = ({
|
||||
setCompleteInviteLink
|
||||
}: Props) => {
|
||||
const router = useRouter();
|
||||
const [searchMemberFilter, setSearchMemberFilter] = useState('');
|
||||
const [searchMemberFilter, setSearchMemberFilter] = useState("");
|
||||
const {data: serverDetails } = useFetchServerStatus()
|
||||
const [isInviteLinkCopied, setInviteLinkCopied] = useToggle(false);
|
||||
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||
'addMember',
|
||||
'removeMember',
|
||||
'upgradePlan',
|
||||
'setUpEmail'
|
||||
"addMember",
|
||||
"removeMember",
|
||||
"upgradePlan",
|
||||
"setUpEmail"
|
||||
] as const);
|
||||
|
||||
const {
|
||||
@@ -88,7 +88,7 @@ export const OrgMembersTable = ({
|
||||
const onAddMember = async ({ email }: TAddMemberForm) => {
|
||||
await onInviteMember(email);
|
||||
if (serverDetails?.emailConfigured){
|
||||
handlePopUpClose('addMember');
|
||||
handlePopUpClose("addMember");
|
||||
}
|
||||
|
||||
reset();
|
||||
@@ -97,11 +97,11 @@ export const OrgMembersTable = ({
|
||||
const onRemoveOrgMemberApproved = async () => {
|
||||
const orgMembershipId = (popUp?.removeMember?.data as { id: string })?.id;
|
||||
await onRemoveMember(orgMembershipId);
|
||||
handlePopUpClose('removeMember');
|
||||
handlePopUpClose("removeMember");
|
||||
};
|
||||
|
||||
const isIamOwner = useMemo(
|
||||
() => members.find(({ user }) => userId === user?._id)?.role === 'owner',
|
||||
() => members.find(({ user }) => userId === user?._id)?.role === "owner",
|
||||
[userId, members]
|
||||
);
|
||||
|
||||
@@ -145,9 +145,9 @@ export const OrgMembersTable = ({
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={() => {
|
||||
if (isMoreUserNotAllowed) {
|
||||
handlePopUpOpen('upgradePlan');
|
||||
handlePopUpOpen("upgradePlan");
|
||||
} else {
|
||||
handlePopUpOpen('addMember');
|
||||
handlePopUpOpen("addMember");
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -170,7 +170,7 @@ export const OrgMembersTable = ({
|
||||
{isLoading && <TableSkeleton columns={5} key="org-members" />}
|
||||
{!isLoading &&
|
||||
filterdUser.map(({ user, inviteEmail, role, _id: orgMembershipId, status }) => {
|
||||
const name = user ? `${user.firstName} ${user.lastName}` : '-';
|
||||
const name = user ? `${user.firstName} ${user.lastName}` : "-";
|
||||
const email = user?.email || inviteEmail;
|
||||
const userWs = workspaceMemberships?.[user?._id];
|
||||
|
||||
@@ -179,7 +179,7 @@ export const OrgMembersTable = ({
|
||||
<Td>{name}</Td>
|
||||
<Td>{email}</Td>
|
||||
<Td>
|
||||
{status === 'accepted' && (
|
||||
{status === "accepted" && (
|
||||
<Select
|
||||
defaultValue={role}
|
||||
isDisabled={userId === user?._id}
|
||||
@@ -189,19 +189,19 @@ export const OrgMembersTable = ({
|
||||
onRoleChange(orgMembershipId, selectedRole)
|
||||
}
|
||||
>
|
||||
{(isIamOwner || role === 'owner') && (
|
||||
{(isIamOwner || role === "owner") && (
|
||||
<SelectItem value="owner">owner</SelectItem>
|
||||
)}
|
||||
<SelectItem value="admin">admin</SelectItem>
|
||||
<SelectItem value="member">member</SelectItem>
|
||||
</Select>
|
||||
)}
|
||||
{((status === 'invited' || status === 'verified') && serverDetails?.emailConfigured) && (
|
||||
{((status === "invited" || status === "verified") && serverDetails?.emailConfigured) && (
|
||||
<Button className='w-40' colorSchema="primary" variant="outline_bg" onClick={() => onInviteMember(email)}>
|
||||
Resend Invite
|
||||
</Button>
|
||||
)}
|
||||
{status === 'completed' && (
|
||||
{status === "completed" && (
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
onClick={() => onGrantAccess(user?._id, user?.publicKey)}
|
||||
@@ -219,10 +219,10 @@ export const OrgMembersTable = ({
|
||||
))
|
||||
) : (
|
||||
<div className='flex flex-row'>
|
||||
{((status === 'invited' || status === 'verified') && serverDetails?.emailConfigured)
|
||||
{((status === "invited" || status === "verified") && serverDetails?.emailConfigured)
|
||||
? <Tag colorSchema="red">This user hasn't accepted the invite yet</Tag>
|
||||
: <Tag colorSchema="red">This user isn't part of any projects yet</Tag>}
|
||||
{router.query.id !== 'undefined' && !((status === 'invited' || status === 'verified') && serverDetails?.emailConfigured) && <button
|
||||
{router.query.id !== "undefined" && !((status === "invited" || status === "verified") && serverDetails?.emailConfigured) && <button
|
||||
type="button"
|
||||
onClick={() => router.push(`/users/${router.query.id}`)}
|
||||
className='text-sm bg-mineshaft w-max px-1.5 py-0.5 hover:bg-primary duration-200 hover:text-black cursor-pointer rounded-sm'
|
||||
@@ -239,7 +239,7 @@ export const OrgMembersTable = ({
|
||||
ariaLabel="delete"
|
||||
colorSchema="danger"
|
||||
isDisabled={userId === user?._id}
|
||||
onClick={() => handlePopUpOpen('removeMember', { id: orgMembershipId })}
|
||||
onClick={() => handlePopUpOpen("removeMember", { id: orgMembershipId })}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} />
|
||||
</IconButton>
|
||||
@@ -258,7 +258,7 @@ export const OrgMembersTable = ({
|
||||
<Modal
|
||||
isOpen={popUp?.addMember?.isOpen}
|
||||
onOpenChange={(isOpen) => {
|
||||
handlePopUpToggle('addMember', isOpen);
|
||||
handlePopUpToggle("addMember", isOpen);
|
||||
setCompleteInviteLink(undefined)
|
||||
}}
|
||||
>
|
||||
@@ -299,7 +299,7 @@ export const OrgMembersTable = ({
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
variant="plain"
|
||||
onClick={() => handlePopUpClose('addMember')}
|
||||
onClick={() => handlePopUpClose("addMember")}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
@@ -326,17 +326,17 @@ export const OrgMembersTable = ({
|
||||
isOpen={popUp.removeMember.isOpen}
|
||||
deleteKey="remove"
|
||||
title="Do you want to remove this user from the org?"
|
||||
onChange={(isOpen) => handlePopUpToggle('removeMember', isOpen)}
|
||||
onChange={(isOpen) => handlePopUpToggle("removeMember", isOpen)}
|
||||
onDeleteApproved={onRemoveOrgMemberApproved}
|
||||
/>
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle('upgradePlan', isOpen)}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
text="You can add custom environments if you switch to Infisical's Team plan."
|
||||
/>
|
||||
<EmailServiceSetupModal
|
||||
isOpen={popUp.setUpEmail?.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle('setUpEmail', isOpen)}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("setUpEmail", isOpen)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { OrgMembersTable } from './OrgMembersTable';
|
||||
export { OrgMembersTable } from "./OrgMembersTable";
|
||||
|
||||
+11
-11
@@ -1,12 +1,12 @@
|
||||
import { useEffect } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { faCheck } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import * as yup from 'yup';
|
||||
import { useEffect } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { faCheck } 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, FormControl, Input } from '@app/components/v2';
|
||||
import { Button, FormControl, Input } from "@app/components/v2";
|
||||
|
||||
type Props = {
|
||||
orgName?: string;
|
||||
@@ -14,7 +14,7 @@ type Props = {
|
||||
};
|
||||
|
||||
const formSchema = yup.object({
|
||||
name: yup.string().required().label('Project Name')
|
||||
name: yup.string().required().label("Project Name")
|
||||
});
|
||||
|
||||
type FormData = yup.InferType<typeof formSchema>;
|
||||
@@ -39,7 +39,7 @@ export const OrgNameChangeSection = ({ onOrgNameChange, orgName }: Props): JSX.E
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||
<div className="mb-6 flex w-full flex-col items-start rounded-md bg-white/5 px-6 pb-6 pt-3">
|
||||
<p className="mb-4 mt-2 text-xl font-semibold">{t('common.display-name')}</p>
|
||||
<p className="mb-4 mt-2 text-xl font-semibold">{t("common.display-name")}</p>
|
||||
<div className="mb-2 w-full max-w-lg">
|
||||
<Controller
|
||||
defaultValue=""
|
||||
@@ -61,7 +61,7 @@ export const OrgNameChangeSection = ({ onOrgNameChange, orgName }: Props): JSX.E
|
||||
isDisabled={!isDirty || isSubmitting}
|
||||
leftIcon={<FontAwesomeIcon icon={faCheck} />}
|
||||
>
|
||||
{t('common.save-changes')}
|
||||
{t("common.save-changes")}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export { OrgNameChangeSection } from './OrgNameChangeSection';
|
||||
export { OrgNameChangeSection } from "./OrgNameChangeSection";
|
||||
|
||||
+33
-33
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useMemo,useState } from 'react';
|
||||
import { Controller,useForm } from 'react-hook-form';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useMemo,useState } from "react";
|
||||
import { Controller,useForm } from "react-hook-form";
|
||||
import { useRouter } from "next/router";
|
||||
import {
|
||||
faCheck,
|
||||
faCopy,
|
||||
@@ -8,12 +8,12 @@ import {
|
||||
faPencil,
|
||||
faPlus,
|
||||
faServer,
|
||||
faTrash} from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import * as yup from 'yup';
|
||||
faTrash} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import * as yup from "yup";
|
||||
|
||||
import { generateKeyPair } from '@app/components/utilities/cryptography/crypto';
|
||||
import { generateKeyPair } from "@app/components/utilities/cryptography/crypto";
|
||||
import {
|
||||
Button,
|
||||
DeleteActionModal,
|
||||
@@ -33,26 +33,26 @@ import {
|
||||
Th,
|
||||
THead,
|
||||
Tr
|
||||
} from '@app/components/v2';
|
||||
import { useOrganization, useWorkspace } from '@app/context';
|
||||
import { usePopUp, useToggle } from '@app/hooks';
|
||||
} from "@app/components/v2";
|
||||
import { useOrganization, useWorkspace } from "@app/context";
|
||||
import { usePopUp, useToggle } from "@app/hooks";
|
||||
import {
|
||||
useCreateServiceAccount,
|
||||
useDeleteServiceAccount,
|
||||
useGetServiceAccounts} from '@app/hooks/api';
|
||||
useGetServiceAccounts} from "@app/hooks/api";
|
||||
|
||||
const serviceAccountExpiration = [
|
||||
{ label: '1 Day', value: 86400 },
|
||||
{ label: '7 Days', value: 604800 },
|
||||
{ label: '1 Month', value: 2592000 },
|
||||
{ label: '6 months', value: 15552000 },
|
||||
{ label: '12 months', value: 31104000 },
|
||||
{ label: 'Never', value: -1 }
|
||||
{ label: "1 Day", value: 86400 },
|
||||
{ label: "7 Days", value: 604800 },
|
||||
{ label: "1 Month", value: 2592000 },
|
||||
{ label: "6 months", value: 15552000 },
|
||||
{ label: "12 months", value: 31104000 },
|
||||
{ label: "Never", value: -1 }
|
||||
];
|
||||
|
||||
const addServiceAccountFormSchema = yup.object({
|
||||
name: yup.string().required().label('Name').trim(),
|
||||
expiresIn: yup.string().required().label('Service Account Expiration')
|
||||
name: yup.string().required().label("Name").trim(),
|
||||
expiresIn: yup.string().required().label("Service Account Expiration")
|
||||
});
|
||||
|
||||
type TAddServiceAccountForm = yup.InferType<typeof addServiceAccountFormSchema>;
|
||||
@@ -62,18 +62,18 @@ export const OrgServiceAccountsTable = () => {
|
||||
const { currentOrg } = useOrganization();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
|
||||
const orgId = currentOrg?._id || '';
|
||||
const orgId = currentOrg?._id || "";
|
||||
const [step, setStep] = useState(0);
|
||||
const [isAccessKeyCopied, setIsAccessKeyCopied] = useToggle(false);
|
||||
const [isPublicKeyCopied, setIsPublicKeyCopied] = useToggle(false);
|
||||
const [isPrivateKeyCopied, setIsPrivateKeyCopied] = useToggle(false);
|
||||
const [accessKey, setAccessKey] = useState('');
|
||||
const [publicKey, setPublicKey] = useState('');
|
||||
const [privateKey, setPrivateKey] = useState('');
|
||||
const [searchServiceAccountFilter, setSearchServiceAccountFilter] = useState('');
|
||||
const [accessKey, setAccessKey] = useState("");
|
||||
const [publicKey, setPublicKey] = useState("");
|
||||
const [privateKey, setPrivateKey] = useState("");
|
||||
const [searchServiceAccountFilter, setSearchServiceAccountFilter] = useState("");
|
||||
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||
'addServiceAccount',
|
||||
'removeServiceAccount',
|
||||
"addServiceAccount",
|
||||
"removeServiceAccount",
|
||||
] as const);
|
||||
|
||||
const { data: serviceAccounts = [], isLoading: isServiceAccountsLoading } = useGetServiceAccounts(orgId);
|
||||
@@ -128,7 +128,7 @@ export const OrgServiceAccountsTable = () => {
|
||||
const onRemoveServiceAccount = async () => {
|
||||
const serviceAccountId = (popUp?.removeServiceAccount?.data as { _id: string })?._id;
|
||||
await removeServiceAccount.mutateAsync(serviceAccountId);
|
||||
handlePopUpClose('removeServiceAccount');
|
||||
handlePopUpClose("removeServiceAccount");
|
||||
}
|
||||
|
||||
const filteredServiceAccounts = useMemo(
|
||||
@@ -195,7 +195,7 @@ export const OrgServiceAccountsTable = () => {
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
variant="plain"
|
||||
onClick={() => handlePopUpClose('addServiceAccount')}
|
||||
onClick={() => handlePopUpClose("addServiceAccount")}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
@@ -283,7 +283,7 @@ export const OrgServiceAccountsTable = () => {
|
||||
onClick={() => {
|
||||
setStep(0);
|
||||
reset();
|
||||
handlePopUpOpen('addServiceAccount');
|
||||
handlePopUpOpen("addServiceAccount");
|
||||
}}
|
||||
>
|
||||
Add Service Account
|
||||
@@ -325,7 +325,7 @@ export const OrgServiceAccountsTable = () => {
|
||||
<IconButton
|
||||
ariaLabel="delete"
|
||||
colorSchema="danger"
|
||||
onClick={() => handlePopUpOpen('removeServiceAccount', { _id: serviceAccountId })}
|
||||
onClick={() => handlePopUpOpen("removeServiceAccount", { _id: serviceAccountId })}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} />
|
||||
</IconButton>
|
||||
@@ -344,7 +344,7 @@ export const OrgServiceAccountsTable = () => {
|
||||
<Modal
|
||||
isOpen={popUp?.addServiceAccount?.isOpen}
|
||||
onOpenChange={(isOpen) => {
|
||||
handlePopUpToggle('addServiceAccount', isOpen);
|
||||
handlePopUpToggle("addServiceAccount", isOpen);
|
||||
reset();
|
||||
}}
|
||||
>
|
||||
@@ -359,7 +359,7 @@ export const OrgServiceAccountsTable = () => {
|
||||
isOpen={popUp.removeServiceAccount.isOpen}
|
||||
deleteKey="remove"
|
||||
title="Do you want to remove this service account from the org?"
|
||||
onChange={(isOpen) => handlePopUpToggle('removeServiceAccount', isOpen)}
|
||||
onChange={(isOpen) => handlePopUpToggle("removeServiceAccount", isOpen)}
|
||||
onDeleteApproved={onRemoveServiceAccount}
|
||||
/>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export { OrgServiceAccountsTable } from './OrgServiceAccountsTable';
|
||||
export { OrgServiceAccountsTable } from "./OrgServiceAccountsTable";
|
||||
@@ -1,5 +1,5 @@
|
||||
export { OrgIncidentContactsTable } from './OrgIncidentContactsTable';
|
||||
export { OrgMembersTable } from './OrgMembersTable';
|
||||
export { OrgNameChangeSection } from './OrgNameChangeSection';
|
||||
export { OrgServiceAccountsTable } from './OrgServiceAccountsTable';
|
||||
export { OrgIncidentContactsTable } from "./OrgIncidentContactsTable";
|
||||
export { OrgMembersTable } from "./OrgMembersTable";
|
||||
export { OrgNameChangeSection } from "./OrgNameChangeSection";
|
||||
export { OrgServiceAccountsTable } from "./OrgServiceAccountsTable";
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { OrgSettingsPage } from './OrgSettingsPage';
|
||||
export { OrgSettingsPage } from "./OrgSettingsPage";
|
||||
|
||||
+16
-16
@@ -1,26 +1,26 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { useNotificationContext } from '@app/components/context/Notifications/NotificationProvider';
|
||||
import { Checkbox, EmailServiceSetupModal } from '@app/components/v2';
|
||||
import { useFetchServerStatus } from '@app/hooks/api/serverDetails';
|
||||
import { usePopUp } from '@app/hooks/usePopUp';
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import { Checkbox, EmailServiceSetupModal } from "@app/components/v2";
|
||||
import { useFetchServerStatus } from "@app/hooks/api/serverDetails";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
|
||||
import { useGetUser } from '../../../../hooks/api';
|
||||
import { User } from '../../../../hooks/api/types';
|
||||
import updateMyMfaEnabled from '../../../../pages/api/user/updateMyMfaEnabled';
|
||||
import { useGetUser } from "../../../../hooks/api";
|
||||
import { User } from "../../../../hooks/api/types";
|
||||
import updateMyMfaEnabled from "../../../../pages/api/user/updateMyMfaEnabled";
|
||||
|
||||
export const SecuritySection = () => {
|
||||
const [isMfaEnabled, setIsMfaEnabled] = useState(false);
|
||||
const { data: user } = useGetUser();
|
||||
const { createNotification } = useNotificationContext();
|
||||
const { handlePopUpToggle, popUp, handlePopUpOpen } = usePopUp([
|
||||
'setUpEmail'
|
||||
"setUpEmail"
|
||||
] as const);
|
||||
|
||||
const {data: serverDetails } = useFetchServerStatus()
|
||||
|
||||
useEffect(() => {
|
||||
if (user && typeof user.isMfaEnabled !== 'undefined') {
|
||||
if (user && typeof user.isMfaEnabled !== "undefined") {
|
||||
setIsMfaEnabled(user.isMfaEnabled);
|
||||
}
|
||||
}, [user]);
|
||||
@@ -36,13 +36,13 @@ export const SecuritySection = () => {
|
||||
}
|
||||
|
||||
createNotification({
|
||||
text: `${newUser.isMfaEnabled ? 'Successfully turned on two-factor authentication.' : 'Successfully turned off two-factor authentication.'}`,
|
||||
type: 'success'
|
||||
text: `${newUser.isMfaEnabled ? "Successfully turned on two-factor authentication." : "Successfully turned off two-factor authentication."}`,
|
||||
type: "success"
|
||||
});
|
||||
} catch (err) {
|
||||
createNotification({
|
||||
text: 'Something went wrong while toggling the two-factor authentication.',
|
||||
type: 'error'
|
||||
text: "Something went wrong while toggling the two-factor authentication.",
|
||||
type: "error"
|
||||
});
|
||||
console.error(err);
|
||||
}
|
||||
@@ -63,7 +63,7 @@ export const SecuritySection = () => {
|
||||
if (serverDetails?.emailConfigured){
|
||||
toggleMfa(state as boolean);
|
||||
} else {
|
||||
handlePopUpOpen('setUpEmail');
|
||||
handlePopUpOpen("setUpEmail");
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -73,7 +73,7 @@ export const SecuritySection = () => {
|
||||
</form>
|
||||
<EmailServiceSetupModal
|
||||
isOpen={popUp.setUpEmail?.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle('setUpEmail', isOpen)}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("setUpEmail", isOpen)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { SecuritySection } from './SecuritySection';
|
||||
export { SecuritySection } from "./SecuritySection";
|
||||
@@ -1,20 +1,20 @@
|
||||
import crypto from 'crypto';
|
||||
import crypto from "crypto";
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import { useNotificationContext } from '@app/components/context/Notifications/NotificationProvider';
|
||||
import NavHeader from '@app/components/navigation/NavHeader';
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import NavHeader from "@app/components/navigation/NavHeader";
|
||||
// TODO(akhilmhdh):Refactor this into a better utility module package
|
||||
import {
|
||||
decryptAssymmetric,
|
||||
decryptSymmetric,
|
||||
encryptSymmetric
|
||||
} from '@app/components/utilities/cryptography/crypto';
|
||||
import { Button, FormControl, Input } from '@app/components/v2';
|
||||
import { useSubscription, useWorkspace } from '@app/context';
|
||||
import { useToggle } from '@app/hooks';
|
||||
} from "@app/components/utilities/cryptography/crypto";
|
||||
import { Button, FormControl, Input } from "@app/components/v2";
|
||||
import { useSubscription, useWorkspace } from "@app/context";
|
||||
import { useToggle } from "@app/hooks";
|
||||
import {
|
||||
useCreateServiceToken,
|
||||
useCreateWsEnvironment,
|
||||
@@ -32,10 +32,10 @@ import {
|
||||
useRenameWorkspace,
|
||||
useToggleAutoCapitalization,
|
||||
useUpdateWsEnvironment
|
||||
} from '@app/hooks/api';
|
||||
} from "@app/hooks/api";
|
||||
|
||||
import { AutoCapitalizationSection } from './components/AutoCapitalizationSection/AutoCapitalizationSection';
|
||||
import { SecretTagsSection } from './components/SecretTagsSection';
|
||||
import { AutoCapitalizationSection } from "./components/AutoCapitalizationSection/AutoCapitalizationSection";
|
||||
import { SecretTagsSection } from "./components/SecretTagsSection";
|
||||
import {
|
||||
CopyProjectIDSection,
|
||||
CreateServiceToken,
|
||||
@@ -45,17 +45,17 @@ import {
|
||||
EnvironmentSection,
|
||||
ProjectIndexSecretsSection,
|
||||
ProjectNameChangeSection,
|
||||
ServiceTokenSection} from './components';
|
||||
ServiceTokenSection} from "./components";
|
||||
|
||||
export const ProjectSettingsPage = () => {
|
||||
const { t } = useTranslation();
|
||||
const { currentWorkspace, workspaces, isLoading: isWorkspaceLoading } = useWorkspace();
|
||||
const router = useRouter();
|
||||
|
||||
const workspaceID = currentWorkspace?._id || '';
|
||||
const workspaceID = currentWorkspace?._id || "";
|
||||
const { createNotification } = useNotificationContext();
|
||||
// delete action worksapce
|
||||
const [deleteProjectInput, setDeleteProjectInput] = useState('');
|
||||
const [deleteProjectInput, setDeleteProjectInput] = useState("");
|
||||
const [isDeleting, setIsDeleting] = useToggle();
|
||||
|
||||
const renameWorkspace = useRenameWorkspace();
|
||||
@@ -73,7 +73,7 @@ export const ProjectSettingsPage = () => {
|
||||
|
||||
// service token
|
||||
const { data: serviceTokens, isLoading: isServiceTokenLoading } = useGetUserWsServiceTokens({
|
||||
workspaceID: currentWorkspace?._id || ''
|
||||
workspaceID: currentWorkspace?._id || ""
|
||||
});
|
||||
|
||||
const { data: latestFileKey } = useGetUserWsKey(workspaceID);
|
||||
@@ -96,14 +96,14 @@ export const ProjectSettingsPage = () => {
|
||||
try {
|
||||
await renameWorkspace.mutateAsync({ workspaceID, newWorkspaceName: name });
|
||||
createNotification({
|
||||
text: 'Successfully renamed workspace',
|
||||
type: 'success'
|
||||
text: "Successfully renamed workspace",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to rename workspace',
|
||||
type: 'error'
|
||||
text: "Failed to rename workspace",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -114,16 +114,16 @@ export const ProjectSettingsPage = () => {
|
||||
workspaceID,
|
||||
state
|
||||
});
|
||||
const text = `Successfully ${state ? 'enabled' : 'disabled'} auto capitalization`;
|
||||
const text = `Successfully ${state ? "enabled" : "disabled"} auto capitalization`;
|
||||
createNotification({
|
||||
text,
|
||||
type: 'success'
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to update auto capitalization',
|
||||
type: 'error'
|
||||
text: "Failed to update auto capitalization",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -135,18 +135,18 @@ export const ProjectSettingsPage = () => {
|
||||
// redirect user to first workspace user is part of
|
||||
const ws = workspaces.find(({ _id }) => _id !== workspaceID);
|
||||
if (!ws) {
|
||||
router.push('/noprojects');
|
||||
router.push("/noprojects");
|
||||
}
|
||||
router.push(`/dashboard/${ws?._id}`);
|
||||
createNotification({
|
||||
text: 'Successfully deleted workspace',
|
||||
type: 'success'
|
||||
text: "Successfully deleted workspace",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to delete workspace',
|
||||
type: 'error'
|
||||
text: "Failed to delete workspace",
|
||||
type: "error"
|
||||
});
|
||||
} finally {
|
||||
setIsDeleting.off();
|
||||
@@ -158,14 +158,14 @@ export const ProjectSettingsPage = () => {
|
||||
try {
|
||||
await createWsEnv.mutateAsync({ workspaceID, environmentName, environmentSlug });
|
||||
createNotification({
|
||||
text: 'Successfully created environment',
|
||||
type: 'success'
|
||||
text: "Successfully created environment",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to create environment',
|
||||
type: 'error'
|
||||
text: "Failed to create environment",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -182,14 +182,14 @@ export const ProjectSettingsPage = () => {
|
||||
oldEnvironmentSlug
|
||||
});
|
||||
createNotification({
|
||||
text: 'Successfully updated environment',
|
||||
type: 'success'
|
||||
text: "Successfully updated environment",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to update environment',
|
||||
type: 'error'
|
||||
text: "Failed to update environment",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -201,14 +201,14 @@ export const ProjectSettingsPage = () => {
|
||||
environmentSlug
|
||||
});
|
||||
createNotification({
|
||||
text: 'Successfully deleted environment',
|
||||
type: 'success'
|
||||
text: "Successfully deleted environment",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to delete environment',
|
||||
type: 'error'
|
||||
text: "Failed to delete environment",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -221,16 +221,16 @@ export const ProjectSettingsPage = () => {
|
||||
secretPath
|
||||
}: CreateServiceToken) => {
|
||||
// type guard
|
||||
if (!latestFileKey) return '';
|
||||
if (!latestFileKey) return "";
|
||||
try {
|
||||
const key = decryptAssymmetric({
|
||||
ciphertext: latestFileKey.encryptedKey,
|
||||
nonce: latestFileKey.nonce,
|
||||
publicKey: latestFileKey.sender.publicKey,
|
||||
privateKey: localStorage.getItem('PRIVATE_KEY') as string
|
||||
privateKey: localStorage.getItem("PRIVATE_KEY") as string
|
||||
});
|
||||
|
||||
const randomBytes = crypto.randomBytes(16).toString('hex');
|
||||
const randomBytes = crypto.randomBytes(16).toString("hex");
|
||||
|
||||
const { ciphertext, iv, tag } = encryptSymmetric({
|
||||
plaintext: key,
|
||||
@@ -253,18 +253,18 @@ export const ProjectSettingsPage = () => {
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: 'Successfully created a service token',
|
||||
type: 'success'
|
||||
text: "Successfully created a service token",
|
||||
type: "success"
|
||||
});
|
||||
return res.serviceToken;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to create a service token',
|
||||
type: 'error'
|
||||
text: "Failed to create a service token",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
return '';
|
||||
return "";
|
||||
};
|
||||
|
||||
const onCreateWsTag = async ({ name }: CreateWsTag) => {
|
||||
@@ -272,35 +272,35 @@ export const ProjectSettingsPage = () => {
|
||||
const res = await createWsTag.mutateAsync({
|
||||
workspaceID,
|
||||
tagName: name,
|
||||
tagSlug: name.replace(' ', '_')
|
||||
tagSlug: name.replace(" ", "_")
|
||||
});
|
||||
createNotification({
|
||||
text: 'Successfully created a tag',
|
||||
type: 'success'
|
||||
text: "Successfully created a tag",
|
||||
type: "success"
|
||||
});
|
||||
return res.name;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to create a tag',
|
||||
type: 'error'
|
||||
text: "Failed to create a tag",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
return '';
|
||||
return "";
|
||||
};
|
||||
|
||||
const onDeleteTag = async (tagID: string) => {
|
||||
try {
|
||||
await deleteWsTag.mutateAsync({ tagID });
|
||||
createNotification({
|
||||
text: 'Successfully deleted tag',
|
||||
type: 'success'
|
||||
text: "Successfully deleted tag",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to delete the tag',
|
||||
type: 'error'
|
||||
text: "Failed to delete the tag",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -309,14 +309,14 @@ export const ProjectSettingsPage = () => {
|
||||
try {
|
||||
await deleteServiceToken.mutateAsync(tokenID);
|
||||
createNotification({
|
||||
text: 'Successfully revoked service token',
|
||||
type: 'success'
|
||||
text: "Successfully revoked service token",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to delete service token',
|
||||
type: 'error'
|
||||
text: "Failed to delete service token",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -330,7 +330,7 @@ export const ProjectSettingsPage = () => {
|
||||
ciphertext: latestFileKey.encryptedKey,
|
||||
nonce: latestFileKey.nonce,
|
||||
publicKey: latestFileKey.sender.publicKey,
|
||||
privateKey: localStorage.getItem('PRIVATE_KEY') as string
|
||||
privateKey: localStorage.getItem("PRIVATE_KEY") as string
|
||||
});
|
||||
|
||||
const secretsToUpdate = encryptedSecrets.map((encryptedSecret) => {
|
||||
@@ -357,13 +357,13 @@ export const ProjectSettingsPage = () => {
|
||||
<div className="dark container mx-auto flex flex-col px-8 text-mineshaft-50 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 />
|
||||
<NavHeader pageName={t("settings.project.title")} isProjectRelated />
|
||||
</div>
|
||||
<div className="my-8 flex max-w-5xl flex-row items-center justify-between text-xl">
|
||||
<div className="flex flex-col items-start justify-start text-3xl">
|
||||
<p className="mr-4 font-semibold text-gray-200">{t('settings.project.title')}</p>
|
||||
<p className="mr-4 font-semibold text-gray-200">{t("settings.project.title")}</p>
|
||||
<p className="mr-4 text-base font-normal text-gray-400">
|
||||
{t('settings.project.description')}
|
||||
{t("settings.project.description")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -371,7 +371,7 @@ export const ProjectSettingsPage = () => {
|
||||
workspaceName={currentWorkspace?.name}
|
||||
onProjectNameChange={onRenameWorkspace}
|
||||
/>
|
||||
<CopyProjectIDSection workspaceID={currentWorkspace?._id || ''} />
|
||||
<CopyProjectIDSection workspaceID={currentWorkspace?._id || ""} />
|
||||
<EnvironmentSection
|
||||
isLoading={isWorkspaceLoading}
|
||||
environments={currentWorkspace?.environments || []}
|
||||
@@ -385,14 +385,14 @@ export const ProjectSettingsPage = () => {
|
||||
tokens={serviceTokens || []}
|
||||
environments={currentWorkspace?.environments || []}
|
||||
onDeleteToken={onDeleteServiceToken}
|
||||
workspaceName={currentWorkspace?.name || ''}
|
||||
workspaceName={currentWorkspace?.name || ""}
|
||||
onCreateToken={onCreateServiceToken}
|
||||
/>
|
||||
<SecretTagsSection
|
||||
isLoading={isTagLoading}
|
||||
tags={wsTags || []}
|
||||
onDeleteTag={onDeleteTag}
|
||||
workspaceName={currentWorkspace?.name || ''}
|
||||
workspaceName={currentWorkspace?.name || ""}
|
||||
onCreateTag={onCreateWsTag}
|
||||
/>
|
||||
<AutoCapitalizationSection
|
||||
@@ -405,11 +405,11 @@ export const ProjectSettingsPage = () => {
|
||||
/>
|
||||
)}
|
||||
<E2EESection
|
||||
workspaceId={currentWorkspace?._id || ''}
|
||||
workspaceId={currentWorkspace?._id || ""}
|
||||
/>
|
||||
<div className="mb-6 mt-4 flex w-full flex-col items-start rounded-md border-l border-red bg-mineshaft-900 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>
|
||||
<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>
|
||||
<div className="mr-auto mt-4 max-h-28 w-full max-w-md">
|
||||
<FormControl
|
||||
label={
|
||||
@@ -433,10 +433,10 @@ export const ProjectSettingsPage = () => {
|
||||
isDisabled={deleteProjectInput !== currentWorkspace?.name || isDeleting}
|
||||
isLoading={isDeleting}
|
||||
>
|
||||
{t('settings.project.delete-project')}
|
||||
{t("settings.project.delete-project")}
|
||||
</Button>
|
||||
<p className="mt-3 ml-0.5 text-xs text-gray-500">
|
||||
{t('settings.project.delete-project-note')}
|
||||
{t("settings.project.delete-project-note")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Checkbox } from '@app/components/v2';
|
||||
import { Checkbox } from "@app/components/v2";
|
||||
|
||||
type Props = {
|
||||
workspaceAutoCapitalization?: boolean;
|
||||
@@ -14,7 +14,7 @@ export const AutoCapitalizationSection = ({
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="mb-6 mt-4 flex w-full flex-col items-start rounded-md bg-mineshaft-900 px-6 pb-6 pt-2">
|
||||
<p className="mb-4 mt-2 text-xl font-semibold">{t('settings.project.auto-capitalization')}</p>
|
||||
<p className="mb-4 mt-2 text-xl font-semibold">{t("settings.project.auto-capitalization")}</p>
|
||||
<Checkbox
|
||||
className="data-[state=checked]:bg-primary"
|
||||
id="autoCapitalization"
|
||||
@@ -23,7 +23,7 @@ export const AutoCapitalizationSection = ({
|
||||
onAutoCapitalizationChange(state as boolean);
|
||||
}}
|
||||
>
|
||||
{t('settings.project.auto-capitalization-description')}
|
||||
{t("settings.project.auto-capitalization-description")}
|
||||
</Checkbox>
|
||||
</div>
|
||||
);
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export { AutoCapitalizationSection } from './AutoCapitalizationSection';
|
||||
export { AutoCapitalizationSection } from "./AutoCapitalizationSection";
|
||||
+10
-10
@@ -1,10 +1,10 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { faCheck, faCopy } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { faCheck, faCopy } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { IconButton } from '@app/components/v2';
|
||||
import { useToggle } from '@app/hooks';
|
||||
import { IconButton } from "@app/components/v2";
|
||||
import { useToggle } from "@app/hooks";
|
||||
|
||||
type Props = {
|
||||
workspaceID: string;
|
||||
@@ -29,10 +29,10 @@ export const CopyProjectIDSection = ({ workspaceID }: Props): JSX.Element => {
|
||||
|
||||
return (
|
||||
<div className="mb-6 mt-4 flex w-full flex-col items-start rounded-md bg-mineshaft-900 px-6 pt-4 pb-2">
|
||||
<p className="self-start text-xl font-semibold">{t('common.project-id')}</p>
|
||||
<p className="mt-4 text-sm text-bunker-300 mb-2">{t('settings.project.auto-generated')}</p>
|
||||
<p className="self-start text-xl font-semibold">{t("common.project-id")}</p>
|
||||
<p className="mt-4 text-sm text-bunker-300 mb-2">{t("settings.project.auto-generated")}</p>
|
||||
<div className="mt-2 mb-3 mr-2 flex items-center justify-end rounded-md bg-white/[0.07] text-base text-gray-400">
|
||||
<p className="mr-2 pl-4 font-bold">{`${t('common.project-id')}:`}</p>
|
||||
<p className="mr-2 pl-4 font-bold">{`${t("common.project-id")}:`}</p>
|
||||
<p className="mr-4">{workspaceID}</p>
|
||||
<IconButton
|
||||
ariaLabel="copy icon"
|
||||
@@ -42,7 +42,7 @@ export const CopyProjectIDSection = ({ workspaceID }: Props): JSX.Element => {
|
||||
>
|
||||
<FontAwesomeIcon icon={isProjectIdCopied ? faCheck : faCopy} />
|
||||
<span className="absolute -left-8 -top-20 hidden w-28 translate-y-full rounded-md bg-bunker-800 py-2 pl-3 text-center text-sm text-gray-400 group-hover:flex group-hover:animate-fadeIn">
|
||||
{t('common.click-to-copy')}
|
||||
{t("common.click-to-copy")}
|
||||
</span>
|
||||
</IconButton>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export { CopyProjectIDSection } from './CopyProjectIDSection';
|
||||
export { CopyProjectIDSection } from "./CopyProjectIDSection";
|
||||
|
||||
+5
-5
@@ -8,9 +8,9 @@ import {
|
||||
Checkbox
|
||||
} from "@app/components/v2";
|
||||
|
||||
import getBot from '../../../../../pages/api/bot/getBot';
|
||||
import setBotActiveStatus from '../../../../../pages/api/bot/setBotActiveStatus';
|
||||
import getLatestFileKey from '../../../../../pages/api/workspace/getLatestFileKey';
|
||||
import getBot from "../../../../../pages/api/bot/getBot";
|
||||
import setBotActiveStatus from "../../../../../pages/api/bot/setBotActiveStatus";
|
||||
import getLatestFileKey from "../../../../../pages/api/workspace/getLatestFileKey";
|
||||
|
||||
type Props = {
|
||||
workspaceId: string;
|
||||
@@ -44,10 +44,10 @@ export const E2EESection = ({
|
||||
if (!bot.isActive) {
|
||||
// bot is not active -> activate bot
|
||||
const key = await getLatestFileKey({ workspaceId });
|
||||
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY');
|
||||
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY");
|
||||
|
||||
if (!PRIVATE_KEY) {
|
||||
throw new Error('Private Key missing');
|
||||
throw new Error("Private Key missing");
|
||||
}
|
||||
|
||||
const WORKSPACE_KEY = decryptAssymmetric({
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export {
|
||||
E2EESection
|
||||
} from './E2EESection';
|
||||
} from "./E2EESection";
|
||||
+25
-25
@@ -1,8 +1,8 @@
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { faPencil, faPlus, faXmark } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import * as yup from 'yup';
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { faPencil, faPlus, faXmark } 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,
|
||||
@@ -22,8 +22,8 @@ import {
|
||||
THead,
|
||||
Tr,
|
||||
UpgradePlanModal
|
||||
} from '@app/components/v2';
|
||||
import { usePopUp } from '@app/hooks/usePopUp';
|
||||
} from "@app/components/v2";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
|
||||
type Props = {
|
||||
environments: Array<{ name: string; slug: string }>;
|
||||
@@ -35,8 +35,8 @@ type Props = {
|
||||
};
|
||||
|
||||
const createUpdateEnvSchema = yup.object({
|
||||
environmentName: yup.string().label('Environment Name').required(),
|
||||
environmentSlug: yup.string().label('Environment Slug').required()
|
||||
environmentName: yup.string().label("Environment Name").required(),
|
||||
environmentSlug: yup.string().label("Environment Slug").required()
|
||||
});
|
||||
|
||||
export type CreateUpdateEnvFormData = yup.InferType<typeof createUpdateEnvSchema>;
|
||||
@@ -50,9 +50,9 @@ export const EnvironmentSection = ({
|
||||
onUpdate
|
||||
}: Props): JSX.Element => {
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||
'createUpdateEnv',
|
||||
'deleteEnv',
|
||||
'upgradePlan'
|
||||
"createUpdateEnv",
|
||||
"deleteEnv",
|
||||
"upgradePlan"
|
||||
] as const);
|
||||
|
||||
const {
|
||||
@@ -73,12 +73,12 @@ export const EnvironmentSection = ({
|
||||
} else {
|
||||
await onCreate(data);
|
||||
}
|
||||
handlePopUpClose('createUpdateEnv');
|
||||
handlePopUpClose("createUpdateEnv");
|
||||
};
|
||||
|
||||
const onEnvDeleteSubmit = async (envSlug: string) => {
|
||||
await onDelete(envSlug);
|
||||
handlePopUpClose('deleteEnv');
|
||||
handlePopUpClose("deleteEnv");
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -98,9 +98,9 @@ export const EnvironmentSection = ({
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (isEnvServiceAllowed) {
|
||||
handlePopUpOpen('createUpdateEnv');
|
||||
handlePopUpOpen("createUpdateEnv");
|
||||
} else {
|
||||
handlePopUpOpen('upgradePlan');
|
||||
handlePopUpOpen("upgradePlan");
|
||||
}
|
||||
}}
|
||||
colorSchema="primary"
|
||||
@@ -130,7 +130,7 @@ export const EnvironmentSection = ({
|
||||
<IconButton
|
||||
className="mr-3 py-2"
|
||||
onClick={() => {
|
||||
handlePopUpOpen('createUpdateEnv', { name, slug });
|
||||
handlePopUpOpen("createUpdateEnv", { name, slug });
|
||||
reset({ environmentName: name, environmentSlug: slug });
|
||||
}}
|
||||
colorSchema="primary"
|
||||
@@ -141,7 +141,7 @@ export const EnvironmentSection = ({
|
||||
</IconButton>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
handlePopUpOpen('deleteEnv', { name, slug });
|
||||
handlePopUpOpen("deleteEnv", { name, slug });
|
||||
}}
|
||||
size="lg"
|
||||
colorSchema="danger"
|
||||
@@ -166,11 +166,11 @@ export const EnvironmentSection = ({
|
||||
<Modal
|
||||
isOpen={popUp?.createUpdateEnv?.isOpen}
|
||||
onOpenChange={(isOpen) => {
|
||||
handlePopUpToggle('createUpdateEnv', isOpen);
|
||||
handlePopUpToggle("createUpdateEnv", isOpen);
|
||||
reset();
|
||||
}}
|
||||
>
|
||||
<ModalContent title={isEnvUpdate ? 'Update environment' : 'Create a new environment'}>
|
||||
<ModalContent title={isEnvUpdate ? "Update environment" : "Create a new environment"}>
|
||||
<form onSubmit={handleSubmit(onEnvModalSubmit)}>
|
||||
<Controller
|
||||
control={control}
|
||||
@@ -209,7 +209,7 @@ export const EnvironmentSection = ({
|
||||
isLoading={isSubmitting}
|
||||
isDisabled={isSubmitting}
|
||||
>
|
||||
{isEnvUpdate ? 'Update' : 'Create'}
|
||||
{isEnvUpdate ? "Update" : "Create"}
|
||||
</Button>
|
||||
|
||||
<Button colorSchema="secondary" variant="plain">
|
||||
@@ -222,17 +222,17 @@ export const EnvironmentSection = ({
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.deleteEnv.isOpen}
|
||||
title={`Are you sure want to delete ${
|
||||
(popUp?.deleteEnv?.data as { name: string })?.name || ' '
|
||||
(popUp?.deleteEnv?.data as { name: string })?.name || " "
|
||||
}?`}
|
||||
onChange={(isOpen) => handlePopUpToggle('deleteEnv', isOpen)}
|
||||
deleteKey={(popUp?.deleteEnv?.data as { slug: string })?.slug || ''}
|
||||
onChange={(isOpen) => handlePopUpToggle("deleteEnv", isOpen)}
|
||||
deleteKey={(popUp?.deleteEnv?.data as { slug: string })?.slug || ""}
|
||||
onDeleteApproved={() =>
|
||||
onEnvDeleteSubmit((popUp?.deleteEnv?.data as { slug: string })?.slug)
|
||||
}
|
||||
/>
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle('upgradePlan', isOpen)}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
text="You can add custom environments if you switch to Infisical's Team plan."
|
||||
/>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export { EnvironmentSection } from './EnvironmentSection';
|
||||
export { EnvironmentSection } from "./EnvironmentSection";
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { Button } from '@app/components/v2';
|
||||
import { Button } from "@app/components/v2";
|
||||
|
||||
// TODO: add check so that this only shows up if user is
|
||||
// an admin in the workspace
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export { ProjectIndexSecretsSection } from './ProjectIndexSecretsSection';
|
||||
export { ProjectIndexSecretsSection } from "./ProjectIndexSecretsSection";
|
||||
+11
-11
@@ -1,12 +1,12 @@
|
||||
import { useEffect } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { faCheck } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import * as yup from 'yup';
|
||||
import { useEffect } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { faCheck } 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, FormControl, Input } from '@app/components/v2';
|
||||
import { Button, FormControl, Input } from "@app/components/v2";
|
||||
|
||||
type Props = {
|
||||
workspaceName?: string;
|
||||
@@ -14,7 +14,7 @@ type Props = {
|
||||
};
|
||||
|
||||
const formSchema = yup.object({
|
||||
name: yup.string().required().label('Project Name')
|
||||
name: yup.string().required().label("Project Name")
|
||||
});
|
||||
|
||||
type FormData = yup.InferType<typeof formSchema>;
|
||||
@@ -42,7 +42,7 @@ export const ProjectNameChangeSection = ({
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||
<div className="mb-6 flex w-full flex-col items-start rounded-md bg-mineshaft-900 px-6 pb-6 pt-3">
|
||||
<p className="mb-4 mt-2 text-xl font-semibold">{t('common.display-name')}</p>
|
||||
<p className="mb-4 mt-2 text-xl font-semibold">{t("common.display-name")}</p>
|
||||
<div className="mb-2 w-full max-w-lg">
|
||||
<Controller
|
||||
defaultValue=""
|
||||
@@ -64,7 +64,7 @@ export const ProjectNameChangeSection = ({
|
||||
isDisabled={!isDirty || isSubmitting}
|
||||
leftIcon={<FontAwesomeIcon icon={faCheck} />}
|
||||
>
|
||||
{t('common.save-changes')}
|
||||
{t("common.save-changes")}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export { ProjectNameChangeSection } from './ProjectNameChangeSection';
|
||||
export { ProjectNameChangeSection } from "./ProjectNameChangeSection";
|
||||
|
||||
+18
-18
@@ -1,8 +1,8 @@
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { faPlus, faTags, 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 { Controller, useForm } from "react-hook-form";
|
||||
import { faPlus, faTags, 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,
|
||||
@@ -22,12 +22,12 @@ import {
|
||||
Td,
|
||||
Th,
|
||||
THead,
|
||||
Tr} from '@app/components/v2';
|
||||
import { usePopUp } from '@app/hooks';
|
||||
import { WorkspaceTag } from '@app/hooks/api/types';
|
||||
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')
|
||||
name: yup.string().required().label("Tag Name")
|
||||
});
|
||||
|
||||
export type CreateWsTag = yup.InferType<typeof createTagSchema>;
|
||||
@@ -50,8 +50,8 @@ export const SecretTagsSection = ({
|
||||
onCreateTag
|
||||
}: Props): JSX.Element => {
|
||||
const { popUp, handlePopUpToggle, handlePopUpClose, handlePopUpOpen } = usePopUp([
|
||||
'CreateSecretTag',
|
||||
'deleteTagConfirmation'
|
||||
"CreateSecretTag",
|
||||
"deleteTagConfirmation"
|
||||
] as const);
|
||||
|
||||
const {
|
||||
@@ -65,12 +65,12 @@ export const SecretTagsSection = ({
|
||||
|
||||
const onFormSubmit = async (data: CreateWsTag) => {
|
||||
await onCreateTag(data);
|
||||
handlePopUpClose('CreateSecretTag');
|
||||
handlePopUpClose("CreateSecretTag");
|
||||
};
|
||||
|
||||
const onDeleteApproved = async () => {
|
||||
await onDeleteTag((popUp?.deleteTagConfirmation?.data as DeleteModalData)?.id);
|
||||
handlePopUpClose('deleteTagConfirmation');
|
||||
handlePopUpClose("deleteTagConfirmation");
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -87,7 +87,7 @@ export const SecretTagsSection = ({
|
||||
<Modal
|
||||
isOpen={popUp?.CreateSecretTag?.isOpen}
|
||||
onOpenChange={(open) => {
|
||||
handlePopUpToggle('CreateSecretTag', open);
|
||||
handlePopUpToggle("CreateSecretTag", open);
|
||||
reset();
|
||||
}}
|
||||
>
|
||||
@@ -154,7 +154,7 @@ export const SecretTagsSection = ({
|
||||
<Td className="flex items-center justify-end">
|
||||
<IconButton
|
||||
onClick={() =>
|
||||
handlePopUpOpen('deleteTagConfirmation', {
|
||||
handlePopUpOpen("deleteTagConfirmation", {
|
||||
name,
|
||||
id: _id
|
||||
})
|
||||
@@ -180,11 +180,11 @@ export const SecretTagsSection = ({
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.deleteTagConfirmation.isOpen}
|
||||
title={`Delete ${
|
||||
(popUp?.deleteTagConfirmation?.data as DeleteModalData)?.name || ' '
|
||||
(popUp?.deleteTagConfirmation?.data as DeleteModalData)?.name || " "
|
||||
} api key?`}
|
||||
onChange={(isOpen) => handlePopUpToggle('deleteTagConfirmation', isOpen)}
|
||||
onChange={(isOpen) => handlePopUpToggle("deleteTagConfirmation", isOpen)}
|
||||
deleteKey={(popUp?.deleteTagConfirmation?.data as DeleteModalData)?.name}
|
||||
onClose={() => handlePopUpClose('deleteTagConfirmation')}
|
||||
onClose={() => handlePopUpClose("deleteTagConfirmation")}
|
||||
onDeleteApproved={onDeleteApproved}
|
||||
/>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export {SecretTagsSection} from './SecretTagsSection'
|
||||
export {SecretTagsSection} from "./SecretTagsSection"
|
||||
+43
-43
@@ -1,10 +1,10 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { faCheck, faCopy, faKey, 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 { useEffect, useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { faCheck, faCopy, faKey, 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,
|
||||
@@ -28,24 +28,24 @@ import {
|
||||
Th,
|
||||
THead,
|
||||
Tr
|
||||
} from '@app/components/v2';
|
||||
import { usePopUp, useToggle } from '@app/hooks';
|
||||
import { ServiceToken, WorkspaceEnv } from '@app/hooks/api/types';
|
||||
} from "@app/components/v2";
|
||||
import { usePopUp, useToggle } from "@app/hooks";
|
||||
import { ServiceToken, WorkspaceEnv } from "@app/hooks/api/types";
|
||||
|
||||
const apiTokenExpiry = [
|
||||
{ label: '1 Day', value: 86400 },
|
||||
{ label: '7 Days', value: 604800 },
|
||||
{ label: '1 Month', value: 2592000 },
|
||||
{ label: '6 months', value: 15552000 },
|
||||
{ label: '12 months', value: 31104000 },
|
||||
{ label: 'Never', value: null }
|
||||
{ label: "1 Day", value: 86400 },
|
||||
{ label: "7 Days", value: 604800 },
|
||||
{ label: "1 Month", value: 2592000 },
|
||||
{ label: "6 months", value: 15552000 },
|
||||
{ label: "12 months", value: 31104000 },
|
||||
{ label: "Never", value: null }
|
||||
];
|
||||
|
||||
const createServiceTokenSchema = yup.object({
|
||||
name: yup.string().max(100).required().label('Service Token Name'),
|
||||
environment: yup.string().max(50).required().label('Environment'),
|
||||
secretPath: yup.string().required().default('/').label('Secret Path'),
|
||||
expiresIn: yup.string().optional().label('Service Token Expiration'),
|
||||
name: yup.string().max(100).required().label("Service Token Name"),
|
||||
environment: yup.string().max(50).required().label("Environment"),
|
||||
secretPath: yup.string().required().default("/").label("Secret Path"),
|
||||
expiresIn: yup.string().optional().label("Service Token Expiration"),
|
||||
permissions: yup
|
||||
.object()
|
||||
.shape({
|
||||
@@ -77,7 +77,7 @@ export const ServiceTokenSection = ({
|
||||
environments = [],
|
||||
onCreateToken
|
||||
}: Props): JSX.Element => {
|
||||
const [newToken, setToken] = useState('');
|
||||
const [newToken, setToken] = useState("");
|
||||
const { t } = useTranslation();
|
||||
const [isTokenCopied, setIsTokenCopied] = useToggle(false);
|
||||
|
||||
@@ -95,8 +95,8 @@ export const ServiceTokenSection = ({
|
||||
};
|
||||
|
||||
const { popUp, handlePopUpToggle, handlePopUpClose, handlePopUpOpen } = usePopUp([
|
||||
'createAPIToken',
|
||||
'deleteAPITokenConfirmation'
|
||||
"createAPIToken",
|
||||
"deleteAPITokenConfirmation"
|
||||
] as const);
|
||||
|
||||
const {
|
||||
@@ -117,37 +117,37 @@ export const ServiceTokenSection = ({
|
||||
|
||||
const onDeleteApproved = async () => {
|
||||
await onDeleteToken((popUp?.deleteAPITokenConfirmation?.data as DeleteModalData)?.id);
|
||||
handlePopUpClose('deleteAPITokenConfirmation');
|
||||
handlePopUpClose("deleteAPITokenConfirmation");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mt-4 mb-4 flex w-full flex-col items-start rounded-md bg-mineshaft-900 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">{t('section.token.service-tokens')}</p>
|
||||
<p className="text-sm text-gray-400 mb-4">{t('section.token.service-tokens-description')}</p>
|
||||
<p className="mb-3 text-xl font-semibold">{t("section.token.service-tokens")}</p>
|
||||
<p className="text-sm text-gray-400 mb-4">{t("section.token.service-tokens-description")}</p>
|
||||
</div>
|
||||
<div>
|
||||
<Modal
|
||||
isOpen={popUp?.createAPIToken?.isOpen}
|
||||
onOpenChange={(open) => {
|
||||
handlePopUpToggle('createAPIToken', open);
|
||||
handlePopUpToggle("createAPIToken", open);
|
||||
reset();
|
||||
setToken('');
|
||||
setToken("");
|
||||
}}
|
||||
>
|
||||
<ModalTrigger asChild>
|
||||
<Button color="mineshaft" leftIcon={<FontAwesomeIcon icon={faPlus} />}>
|
||||
{t('section.token.add-new')}
|
||||
{t("section.token.add-new")}
|
||||
</Button>
|
||||
</ModalTrigger>
|
||||
<ModalContent
|
||||
title={
|
||||
t('section.token.add-dialog.title', {
|
||||
t("section.token.add-dialog.title", {
|
||||
target: workspaceName
|
||||
}) as string
|
||||
}
|
||||
subTitle={t('section.token.add-dialog.description') as string}
|
||||
subTitle={t("section.token.add-dialog.description") as string}
|
||||
>
|
||||
{!hasServiceToken ? (
|
||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||
@@ -157,7 +157,7 @@ export const ServiceTokenSection = ({
|
||||
defaultValue=""
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label={t('section.token.add-dialog.name')}
|
||||
label={t("section.token.add-dialog.name")}
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
@@ -223,7 +223,7 @@ export const ServiceTokenSection = ({
|
||||
className="w-full"
|
||||
>
|
||||
{apiTokenExpiry.map(({ label, value }) => (
|
||||
<SelectItem value={String(value || '')} key={label}>
|
||||
<SelectItem value={String(value || "")} key={label}>
|
||||
{label}
|
||||
</SelectItem>
|
||||
))}
|
||||
@@ -241,12 +241,12 @@ export const ServiceTokenSection = ({
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => {
|
||||
const options = [
|
||||
{
|
||||
label: 'Read (default)',
|
||||
value: 'read'
|
||||
label: "Read (default)",
|
||||
value: "read"
|
||||
},
|
||||
{
|
||||
label: 'Write (optional)',
|
||||
value: 'write'
|
||||
label: "Write (optional)",
|
||||
value: "write"
|
||||
}
|
||||
];
|
||||
|
||||
@@ -265,7 +265,7 @@ export const ServiceTokenSection = ({
|
||||
key={optionValue}
|
||||
className="data-[state=checked]:bg-primary"
|
||||
isChecked={value[optionValue]}
|
||||
isDisabled={optionValue === 'read'}
|
||||
isDisabled={optionValue === "read"}
|
||||
onCheckedChange={(state) => {
|
||||
onChange({
|
||||
...value,
|
||||
@@ -309,7 +309,7 @@ export const ServiceTokenSection = ({
|
||||
>
|
||||
<FontAwesomeIcon icon={isTokenCopied ? faCheck : faCopy} />
|
||||
<span className="absolute -left-8 -top-20 hidden w-28 translate-y-full rounded-md bg-bunker-800 py-2 pl-3 text-center text-sm text-gray-400 group-hover:flex group-hover:animate-fadeIn">
|
||||
{t('common.click-to-copy')}
|
||||
{t("common.click-to-copy")}
|
||||
</span>
|
||||
</IconButton>
|
||||
</div>
|
||||
@@ -321,11 +321,11 @@ export const ServiceTokenSection = ({
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.deleteAPITokenConfirmation.isOpen}
|
||||
title={`Delete ${
|
||||
(popUp?.deleteAPITokenConfirmation?.data as DeleteModalData)?.name || ' '
|
||||
(popUp?.deleteAPITokenConfirmation?.data as DeleteModalData)?.name || " "
|
||||
} service token?`}
|
||||
onChange={(isOpen) => handlePopUpToggle('deleteAPITokenConfirmation', isOpen)}
|
||||
onChange={(isOpen) => handlePopUpToggle("deleteAPITokenConfirmation", isOpen)}
|
||||
deleteKey={(popUp?.deleteAPITokenConfirmation?.data as DeleteModalData)?.name}
|
||||
onClose={() => handlePopUpClose('deleteAPITokenConfirmation')}
|
||||
onClose={() => handlePopUpClose("deleteAPITokenConfirmation")}
|
||||
onDeleteApproved={onDeleteApproved}
|
||||
/>
|
||||
<TableContainer>
|
||||
@@ -351,7 +351,7 @@ export const ServiceTokenSection = ({
|
||||
<Td className="flex items-center justify-end">
|
||||
<IconButton
|
||||
onClick={() =>
|
||||
handlePopUpOpen('deleteAPITokenConfirmation', {
|
||||
handlePopUpOpen("deleteAPITokenConfirmation", {
|
||||
name: row.name,
|
||||
id: row._id
|
||||
})
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export {ServiceTokenSection} from './ServiceTokenSection'
|
||||
export {ServiceTokenSection} from "./ServiceTokenSection"
|
||||
@@ -1,9 +1,9 @@
|
||||
export { CopyProjectIDSection } from './CopyProjectIDSection';
|
||||
export { EnvironmentSection } from './EnvironmentSection';
|
||||
export type { CreateUpdateEnvFormData } from './EnvironmentSection/EnvironmentSection';
|
||||
export { ProjectIndexSecretsSection } from './ProjectIndexSecretsSection';
|
||||
export { ProjectNameChangeSection } from './ProjectNameChangeSection';
|
||||
export type { CreateWsTag } from './SecretTagsSection/SecretTagsSection';
|
||||
export { ServiceTokenSection } from './ServiceTokenSection';
|
||||
export type { CreateServiceToken } from './ServiceTokenSection/ServiceTokenSection';
|
||||
export { E2EESection } from './E2EESection';
|
||||
export { CopyProjectIDSection } from "./CopyProjectIDSection";
|
||||
export { E2EESection } from "./E2EESection";
|
||||
export { EnvironmentSection } from "./EnvironmentSection";
|
||||
export type { CreateUpdateEnvFormData } from "./EnvironmentSection/EnvironmentSection";
|
||||
export { ProjectIndexSecretsSection } from "./ProjectIndexSecretsSection";
|
||||
export { ProjectNameChangeSection } from "./ProjectNameChangeSection";
|
||||
export type { CreateWsTag } from "./SecretTagsSection/SecretTagsSection";
|
||||
export { ServiceTokenSection } from "./ServiceTokenSection";
|
||||
export type { CreateServiceToken } from "./ServiceTokenSection/ServiceTokenSection";
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { ProjectSettingsPage } from './ProjectSettingsPage';
|
||||
export { ProjectSettingsPage } from "./ProjectSettingsPage";
|
||||
|
||||
Reference in New Issue
Block a user