Add eslint rule and fix as many issues as possible

This commit is contained in:
Khoa Le
2023-06-19 23:42:04 -04:00
parent e9eacc445d
commit 9d33e4756b
689 changed files with 9446 additions and 21821 deletions
@@ -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 && (
+128 -128
View File
@@ -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}
@@ -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">
@@ -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
View File
@@ -1 +1 @@
export { DashboardPage } from './DashboardPage';
export { DashboardPage } from "./DashboardPage";