mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 14:34:09 +00:00
Merge branch 'main' into fix/user-stuck-on-invited
This commit is contained in:
@@ -78,11 +78,14 @@ export default function CodeInputStep({
|
||||
const resendVerificationEmail = async () => {
|
||||
setIsResendingVerificationEmail(true);
|
||||
setIsLoading(true);
|
||||
await mutateAsync({ email });
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
setIsResendingVerificationEmail(false);
|
||||
}, 2000);
|
||||
try {
|
||||
await mutateAsync({ email });
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
setIsResendingVerificationEmail(false);
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -8,6 +8,11 @@ export const formatReservedPaths = (secretPath: string) => {
|
||||
return secretPath;
|
||||
};
|
||||
|
||||
export const parsePathFromReplicatedPath = (secretPath: string) => {
|
||||
const i = secretPath.indexOf(ReservedFolders.SecretReplication);
|
||||
return secretPath.slice(0, i);
|
||||
};
|
||||
|
||||
export const camelCaseToSpaces = (input: string) => {
|
||||
return input.replace(/([a-z])([A-Z])/g, "$1 $2");
|
||||
};
|
||||
|
||||
@@ -22,8 +22,12 @@ export const VerifyEmailPage = () => {
|
||||
*/
|
||||
const sendVerificationEmail = async () => {
|
||||
if (email) {
|
||||
await mutateAsync({ email });
|
||||
setStep(2);
|
||||
try {
|
||||
await mutateAsync({ email });
|
||||
setStep(2);
|
||||
} catch {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+1
@@ -159,6 +159,7 @@ export const AddOrgMemberModal = ({
|
||||
text: "Failed to invite user to org",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (serverDetails?.emailConfigured) {
|
||||
|
||||
+101
-11
@@ -1,8 +1,11 @@
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import {
|
||||
faArrowDown,
|
||||
faArrowUp,
|
||||
faCheckCircle,
|
||||
faChevronRight,
|
||||
faEllipsis,
|
||||
faFilter,
|
||||
faMagnifyingGlass,
|
||||
faSearch,
|
||||
faUsers
|
||||
@@ -19,7 +22,11 @@ import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuTrigger,
|
||||
DropdownSubMenu,
|
||||
DropdownSubMenuContent,
|
||||
DropdownSubMenuTrigger,
|
||||
EmptyState,
|
||||
IconButton,
|
||||
Input,
|
||||
@@ -75,6 +82,10 @@ enum OrgMembersOrderBy {
|
||||
Email = "email"
|
||||
}
|
||||
|
||||
type Filter = {
|
||||
roles: string[];
|
||||
};
|
||||
|
||||
export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLinks }: Props) => {
|
||||
const navigate = useNavigate();
|
||||
const { subscription } = useSubscription();
|
||||
@@ -184,17 +195,29 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLinks }: Pro
|
||||
setUserTablePreference("orgMembersTable", PreferenceKey.PerPage, newPerPage);
|
||||
};
|
||||
|
||||
const [filter, setFilter] = useState<Filter>({
|
||||
roles: []
|
||||
});
|
||||
|
||||
const filteredUsers = useMemo(
|
||||
() =>
|
||||
members
|
||||
?.filter(
|
||||
({ user: u, inviteEmail }) =>
|
||||
?.filter(({ user: u, inviteEmail, role, roleId }) => {
|
||||
if (
|
||||
filter.roles.length &&
|
||||
!filter.roles.includes(role === "custom" ? findRoleFromId(roleId)!.slug : role)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
u?.firstName?.toLowerCase().includes(search.toLowerCase()) ||
|
||||
u?.lastName?.toLowerCase().includes(search.toLowerCase()) ||
|
||||
u?.username?.toLowerCase().includes(search.toLowerCase()) ||
|
||||
u?.email?.toLowerCase().includes(search.toLowerCase()) ||
|
||||
inviteEmail?.toLowerCase().includes(search.toLowerCase())
|
||||
)
|
||||
);
|
||||
})
|
||||
.sort((a, b) => {
|
||||
const [memberOne, memberTwo] = orderDirection === OrderByDirection.ASC ? [a, b] : [b, a];
|
||||
|
||||
@@ -217,7 +240,7 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLinks }: Pro
|
||||
|
||||
return valueOne.toLowerCase().localeCompare(valueTwo.toLowerCase());
|
||||
}),
|
||||
[members, search, orderDirection, orderBy]
|
||||
[members, search, orderDirection, orderBy, filter]
|
||||
);
|
||||
|
||||
const handleSort = (column: OrgMembersOrderBy) => {
|
||||
@@ -236,14 +259,81 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLinks }: Pro
|
||||
setPage
|
||||
});
|
||||
|
||||
const handleRoleToggle = useCallback(
|
||||
(roleSlug: string) =>
|
||||
setFilter((state) => {
|
||||
const currentRoles = state.roles || [];
|
||||
|
||||
if (currentRoles.includes(roleSlug)) {
|
||||
return { ...state, roles: currentRoles.filter((role) => role !== roleSlug) };
|
||||
}
|
||||
return { ...state, roles: [...currentRoles, roleSlug] };
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const isTableFiltered = Boolean(filter.roles.length);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Input
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />}
|
||||
placeholder="Search members..."
|
||||
/>
|
||||
<div className="flex gap-2">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<IconButton
|
||||
ariaLabel="Filter Users"
|
||||
variant="plain"
|
||||
size="sm"
|
||||
className={twMerge(
|
||||
"flex h-10 w-11 items-center justify-center overflow-hidden border border-mineshaft-600 bg-mineshaft-800 p-0 transition-all hover:border-primary/60 hover:bg-primary/10",
|
||||
isTableFiltered && "border-primary/50 text-primary"
|
||||
)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faFilter} />
|
||||
</IconButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" className="p-0">
|
||||
<DropdownMenuLabel>Filter By</DropdownMenuLabel>
|
||||
<DropdownSubMenu>
|
||||
<DropdownSubMenuTrigger
|
||||
iconPos="right"
|
||||
icon={<FontAwesomeIcon icon={faChevronRight} size="sm" />}
|
||||
>
|
||||
Roles
|
||||
</DropdownSubMenuTrigger>
|
||||
<DropdownSubMenuContent className="thin-scrollbar max-h-[20rem] overflow-y-auto rounded-l-none">
|
||||
<DropdownMenuLabel className="sticky top-0 bg-mineshaft-900">
|
||||
Apply Roles to Filter Users
|
||||
</DropdownMenuLabel>
|
||||
{roles?.map(({ id, slug, name }) => (
|
||||
<DropdownMenuItem
|
||||
onClick={(evt) => {
|
||||
evt.preventDefault();
|
||||
handleRoleToggle(slug);
|
||||
}}
|
||||
key={id}
|
||||
icon={filter.roles.includes(slug) && <FontAwesomeIcon icon={faCheckCircle} />}
|
||||
iconPos="right"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<div
|
||||
className="mr-2 h-2 w-2 rounded-full"
|
||||
style={{ background: "#bec2c8" }}
|
||||
/>
|
||||
{name}
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownSubMenuContent>
|
||||
</DropdownSubMenu>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<Input
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />}
|
||||
placeholder="Search members..."
|
||||
/>
|
||||
</div>
|
||||
<TableContainer className="mt-4">
|
||||
<Table>
|
||||
<THead>
|
||||
|
||||
+1
@@ -163,6 +163,7 @@ export const AddMemberModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
text: "Failed to add user to project",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
handlePopUpToggle("addMember", false);
|
||||
reset();
|
||||
|
||||
+17
@@ -226,6 +226,23 @@ const ConditionSchema = z
|
||||
: el.rhs.trim().startsWith("/")
|
||||
),
|
||||
{ message: "Invalid Secret Path. Must start with '/'" }
|
||||
)
|
||||
.refine(
|
||||
(val) =>
|
||||
val
|
||||
.filter((el) => el.operator === PermissionConditionOperators.$EQ)
|
||||
.every((el) => !el.rhs.includes(",")),
|
||||
{ message: '"Equal" checks cannot contain comma separated values. Use "IN" operator instead.' }
|
||||
)
|
||||
.refine(
|
||||
(val) =>
|
||||
val
|
||||
.filter((el) => el.operator === PermissionConditionOperators.$NEQ)
|
||||
.every((el) => !el.rhs.includes(",")),
|
||||
{
|
||||
message:
|
||||
'"Not Equal" checks cannot contain comma separated values. Use "IN" operator with "Forbid" instead.'
|
||||
}
|
||||
);
|
||||
|
||||
export const projectRoleFormSchema = z.object({
|
||||
|
||||
+5
-4
@@ -224,8 +224,7 @@ export const SecretApprovalRequest = () => {
|
||||
createdAt,
|
||||
reviewers,
|
||||
status,
|
||||
committerUser,
|
||||
isReplicated: isReplication
|
||||
committerUser
|
||||
} = secretApproval;
|
||||
const isReviewed = reviewers.some(
|
||||
({ status: reviewStatus, userId }) =>
|
||||
@@ -244,13 +243,15 @@ export const SecretApprovalRequest = () => {
|
||||
>
|
||||
<div className="mb-1">
|
||||
<FontAwesomeIcon icon={faCodeBranch} className="mr-2" />
|
||||
{generateCommitText(commits)}
|
||||
{secretApproval.isReplicated
|
||||
? `${commits.length} secret pending import`
|
||||
: generateCommitText(commits)}
|
||||
<span className="text-xs text-bunker-300"> #{secretApproval.slug}</span>
|
||||
</div>
|
||||
<span className="text-xs text-gray-500">
|
||||
Opened {formatDistance(new Date(createdAt), new Date())} ago by{" "}
|
||||
{committerUser?.firstName || ""} {committerUser?.lastName || ""} (
|
||||
{committerUser?.email}){isReplication && " via replication"}
|
||||
{committerUser?.email})
|
||||
{!isReviewed && status === "open" && " - Review required"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
+34
-45
@@ -12,7 +12,7 @@ import {
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { Tag, Tooltip } from "@app/components/v2";
|
||||
import { SecretInput, Tag, Tooltip } from "@app/components/v2";
|
||||
import { CommitType, SecretV3Raw, TSecretApprovalSecChange, WsTag } from "@app/hooks/api/types";
|
||||
|
||||
export type Props = {
|
||||
@@ -86,10 +86,10 @@ export const SecretApprovalRequestChangeItem = ({
|
||||
{op === CommitType.UPDATE || op === CommitType.DELETE ? (
|
||||
<div className="flex w-full cursor-default flex-col rounded-md border border-red-600/60 bg-red-600/10 p-4 xl:w-1/2">
|
||||
<div className="mb-4 flex flex-row justify-between">
|
||||
<span className="text-md font-medium">Legacy Secret</span>
|
||||
<span className="text-md font-medium">Previous Secret</span>
|
||||
<div className="rounded-full bg-red px-2 pb-[0.14rem] pt-[0.2rem] text-xs font-medium">
|
||||
<FontAwesomeIcon icon={faCircleXmark} className="pr-1 text-white" />
|
||||
Deprecated
|
||||
Previous
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
@@ -104,34 +104,29 @@ export const SecretApprovalRequestChangeItem = ({
|
||||
Rotated Secret value will not be affected
|
||||
</span>
|
||||
) : (
|
||||
<div
|
||||
onClick={() => setIsOldSecretValueVisible(!isOldSecretValueVisible)}
|
||||
className="flex flex-row items-center justify-between rounded-md border border-mineshaft-500 bg-mineshaft-900 pl-2"
|
||||
>
|
||||
<div className="relative">
|
||||
<SecretInput
|
||||
isReadOnly
|
||||
isVisible={isOldSecretValueVisible}
|
||||
value={secretVersion?.secretValue}
|
||||
containerClassName="text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-bunker-700 px-2 py-1.5"
|
||||
/>
|
||||
<div
|
||||
className={`flex font-mono ${isOldSecretValueVisible || !secretVersion?.secretValue ? "text-md py-[0.55rem]" : "text-lg"}`}
|
||||
className="absolute right-1 top-1"
|
||||
onClick={() => setIsOldSecretValueVisible(!isOldSecretValueVisible)}
|
||||
>
|
||||
{isOldSecretValueVisible
|
||||
? secretVersion?.secretValue || "EMPTY"
|
||||
: secretVersion?.secretValue
|
||||
? secretVersion?.secretValue?.split("").map(() => "•")
|
||||
: "EMPTY"}{" "}
|
||||
<FontAwesomeIcon
|
||||
icon={isOldSecretValueVisible ? faEyeSlash : faEye}
|
||||
className="cursor-pointer rounded-md border border-mineshaft-500 bg-mineshaft-800 p-1.5 text-mineshaft-300 hover:bg-mineshaft-700"
|
||||
/>
|
||||
</div>
|
||||
{secretVersion?.secretValue && (
|
||||
<div className="flex h-10 w-10 items-center justify-center">
|
||||
<FontAwesomeIcon
|
||||
icon={isOldSecretValueVisible ? faEyeSlash : faEye}
|
||||
className="cursor-pointer rounded-md border border-mineshaft-500 bg-mineshaft-800 p-1.5 text-mineshaft-300 hover:bg-mineshaft-700"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
<div className="text-sm font-medium text-mineshaft-300">Comment</div>
|
||||
<div className="text-sm">
|
||||
<div className="max-h-[5rem] overflow-y-auto text-sm">
|
||||
{secretVersion?.secretComment || (
|
||||
<span className="text-sm text-mineshaft-300">-</span>
|
||||
)}{" "}
|
||||
@@ -191,8 +186,7 @@ export const SecretApprovalRequestChangeItem = ({
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-md flex w-full items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-800 text-mineshaft-300 xl:w-1/2">
|
||||
{" "}
|
||||
Secret not existent in the previous version.
|
||||
Secret did not exist in the previous version.
|
||||
</div>
|
||||
)}
|
||||
{op === CommitType.UPDATE || op === CommitType.CREATE ? (
|
||||
@@ -201,7 +195,7 @@ export const SecretApprovalRequestChangeItem = ({
|
||||
<span className="text-md font-medium">New Secret</span>
|
||||
<div className="rounded-full bg-green-600 px-2 pb-[0.14rem] pt-[0.2rem] text-xs font-medium">
|
||||
<FontAwesomeIcon icon={faCircleXmark} className="pr-1 text-white" />
|
||||
Current
|
||||
New
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
@@ -216,34 +210,29 @@ export const SecretApprovalRequestChangeItem = ({
|
||||
Rotated Secret value will not be affected
|
||||
</span>
|
||||
) : (
|
||||
<div
|
||||
onClick={() => setIsNewSecretValueVisible(!isNewSecretValueVisible)}
|
||||
className="flex flex-row items-center justify-between rounded-md border border-mineshaft-500 bg-mineshaft-900 pl-2"
|
||||
>
|
||||
<div className="relative">
|
||||
<SecretInput
|
||||
isReadOnly
|
||||
isVisible={isNewSecretValueVisible}
|
||||
value={newVersion?.secretValue}
|
||||
containerClassName="text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-bunker-700 px-2 py-1.5"
|
||||
/>
|
||||
<div
|
||||
className={`flex font-mono ${isNewSecretValueVisible || !newVersion?.secretValue ? "text-md py-[0.55rem]" : "text-lg"}`}
|
||||
className="absolute right-1 top-1"
|
||||
onClick={() => setIsNewSecretValueVisible(!isNewSecretValueVisible)}
|
||||
>
|
||||
{isNewSecretValueVisible
|
||||
? newVersion?.secretValue || "EMPTY"
|
||||
: newVersion?.secretValue
|
||||
? newVersion?.secretValue?.split("").map(() => "•")
|
||||
: "EMPTY"}{" "}
|
||||
<FontAwesomeIcon
|
||||
icon={isNewSecretValueVisible ? faEyeSlash : faEye}
|
||||
className="cursor-pointer rounded-md border border-mineshaft-500 bg-mineshaft-800 p-1.5 text-mineshaft-300 hover:bg-mineshaft-700"
|
||||
/>
|
||||
</div>
|
||||
{newVersion?.secretValue && (
|
||||
<div className="flex h-10 w-10 items-center justify-center">
|
||||
<FontAwesomeIcon
|
||||
icon={isNewSecretValueVisible ? faEyeSlash : faEye}
|
||||
className="cursor-pointer rounded-md border border-mineshaft-500 bg-mineshaft-800 p-1.5 text-mineshaft-300 hover:bg-mineshaft-700"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
<div className="text-sm font-medium text-mineshaft-300">Comment</div>
|
||||
<div className="text-sm">
|
||||
<div className="max-h-[5rem] overflow-y-auto text-sm">
|
||||
{newVersion?.secretComment || (
|
||||
<span className="text-sm text-mineshaft-300">-</span>
|
||||
)}{" "}
|
||||
@@ -302,7 +291,7 @@ export const SecretApprovalRequestChangeItem = ({
|
||||
) : (
|
||||
<div className="text-md flex w-full items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-800 text-mineshaft-300 xl:w-1/2">
|
||||
{" "}
|
||||
Secret not existent in the new version.
|
||||
Secret did not exist in the previous version.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
+107
-30
@@ -30,19 +30,24 @@ import {
|
||||
TextArea,
|
||||
Tooltip
|
||||
} from "@app/components/v2";
|
||||
import { useUser } from "@app/context";
|
||||
import { useUser, useWorkspace } from "@app/context";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import {
|
||||
useGetSecretApprovalRequestDetails,
|
||||
useGetSecretImports,
|
||||
useUpdateSecretApprovalReviewStatus
|
||||
} from "@app/hooks/api";
|
||||
import { ApprovalStatus, CommitType } from "@app/hooks/api/types";
|
||||
import { formatReservedPaths } from "@app/lib/fn/string";
|
||||
import { formatReservedPaths, parsePathFromReplicatedPath } from "@app/lib/fn/string";
|
||||
|
||||
import { SecretApprovalRequestAction } from "./SecretApprovalRequestAction";
|
||||
import { SecretApprovalRequestChangeItem } from "./SecretApprovalRequestChangeItem";
|
||||
|
||||
export const generateCommitText = (commits: { op: CommitType }[] = []) => {
|
||||
export const generateCommitText = (commits: { op: CommitType }[] = [], isReplicated = false) => {
|
||||
if (isReplicated) {
|
||||
return <span>{commits.length} secret pending import</span>;
|
||||
}
|
||||
|
||||
const score: Record<string, number> = {};
|
||||
commits.forEach(({ op }) => {
|
||||
score[op] = (score?.[op] || 0) + 1;
|
||||
@@ -74,7 +79,6 @@ export const generateCommitText = (commits: { op: CommitType }[] = []) => {
|
||||
<span style={{ color: "#F83030" }}> deleted</span>
|
||||
</span>
|
||||
);
|
||||
|
||||
return text;
|
||||
};
|
||||
|
||||
@@ -105,6 +109,7 @@ export const SecretApprovalRequestChanges = ({
|
||||
workspaceId
|
||||
}: Props) => {
|
||||
const { user: userSession } = useUser();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const {
|
||||
data: secretApprovalRequestDetails,
|
||||
isSuccess: isSecretApprovalRequestSuccess,
|
||||
@@ -112,6 +117,20 @@ export const SecretApprovalRequestChanges = ({
|
||||
} = useGetSecretApprovalRequestDetails({
|
||||
id: approvalRequestId
|
||||
});
|
||||
const approvalSecretPath = parsePathFromReplicatedPath(
|
||||
secretApprovalRequestDetails?.secretPath || ""
|
||||
);
|
||||
const { data: secretImports } = useGetSecretImports({
|
||||
environment: secretApprovalRequestDetails?.environment || "",
|
||||
projectId: currentWorkspace.id,
|
||||
path: approvalSecretPath
|
||||
});
|
||||
|
||||
const replicatedImport = secretApprovalRequestDetails?.isReplicated
|
||||
? secretImports?.find(
|
||||
(el) => secretApprovalRequestDetails?.secretPath?.includes(el.id) && el.isReplication
|
||||
)
|
||||
: undefined;
|
||||
|
||||
const {
|
||||
mutateAsync: updateSecretApprovalRequestStatus,
|
||||
@@ -226,34 +245,16 @@ export const SecretApprovalRequestChanges = ({
|
||||
: secretApprovalRequestDetails.status}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-grow flex-col">
|
||||
<div className="text-lg">
|
||||
{generateCommitText(secretApprovalRequestDetails.commits)}
|
||||
{secretApprovalRequestDetails.isReplicated && (
|
||||
<span className="text-sm text-bunker-300"> (replication)</span>
|
||||
<div className="flex-grow flex-col">
|
||||
<div className="text-xl">
|
||||
{generateCommitText(
|
||||
secretApprovalRequestDetails.commits,
|
||||
secretApprovalRequestDetails.isReplicated
|
||||
)}
|
||||
</div>
|
||||
<div className="text-sm text-bunker-300">
|
||||
<p className="inline">
|
||||
{secretApprovalRequestDetails?.committerUser?.firstName || ""}
|
||||
{secretApprovalRequestDetails?.committerUser?.lastName || ""} (
|
||||
{secretApprovalRequestDetails?.committerUser?.email}) wants to change{" "}
|
||||
{secretApprovalRequestDetails.commits.length} secret values in
|
||||
</p>
|
||||
<p className="mx-1 inline rounded bg-primary-600/40 px-1 py-1 text-primary-300">
|
||||
{secretApprovalRequestDetails.environment}
|
||||
</p>
|
||||
<div className="inline-flex w-min items-center rounded border border-mineshaft-500 pl-1 pr-2">
|
||||
<p className="cursor-default border-r border-mineshaft-500 pr-1">
|
||||
<FontAwesomeIcon icon={faFolder} className="text-primary" size="sm" />
|
||||
</p>
|
||||
<p
|
||||
className="cursor-default truncate pb-0.5 pl-2 text-sm"
|
||||
style={{ maxWidth: "10rem" }}
|
||||
>
|
||||
{formatReservedPaths(secretApprovalRequestDetails.secretPath)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 text-xs text-gray-400">
|
||||
By {secretApprovalRequestDetails?.committerUser?.firstName} (
|
||||
{secretApprovalRequestDetails?.committerUser?.email})
|
||||
</div>
|
||||
</div>
|
||||
{!hasMerged &&
|
||||
@@ -367,6 +368,82 @@ export const SecretApprovalRequestChanges = ({
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</div>
|
||||
<div className="mb-4 flex flex-col rounded-r border-l-2 border-l-primary bg-mineshaft-300/5 px-4 py-2.5">
|
||||
<div>
|
||||
{secretApprovalRequestDetails.isReplicated ? (
|
||||
<div className="text-sm text-bunker-300">
|
||||
A secret import in
|
||||
<p
|
||||
className="mx-1 inline rounded bg-primary-600/40 text-primary-300"
|
||||
style={{ padding: "2px 4px" }}
|
||||
>
|
||||
{secretApprovalRequestDetails?.environment}
|
||||
</p>
|
||||
<div className="mr-2 inline-flex w-min items-center rounded border border-mineshaft-500 pl-1 pr-2">
|
||||
<p className="cursor-default border-r border-mineshaft-500 pr-1">
|
||||
<FontAwesomeIcon icon={faFolder} className="text-primary" size="sm" />
|
||||
</p>
|
||||
<Tooltip content={approvalSecretPath}>
|
||||
<p
|
||||
className="cursor-default truncate pb-0.5 pl-2 text-sm"
|
||||
style={{ maxWidth: "15rem" }}
|
||||
>
|
||||
{approvalSecretPath}
|
||||
</p>
|
||||
</Tooltip>
|
||||
</div>
|
||||
has pending changes to be accepted from its source at{" "}
|
||||
<p
|
||||
className="mx-1 inline rounded bg-primary-600/40 text-primary-300"
|
||||
style={{ padding: "2px 4px" }}
|
||||
>
|
||||
{replicatedImport?.importEnv?.slug}
|
||||
</p>
|
||||
<div className="inline-flex w-min items-center rounded border border-mineshaft-500 pl-1 pr-2">
|
||||
<p className="cursor-default border-r border-mineshaft-500 pr-1">
|
||||
<FontAwesomeIcon icon={faFolder} className="text-primary" size="sm" />
|
||||
</p>
|
||||
<Tooltip content={replicatedImport?.importPath}>
|
||||
<p
|
||||
className="cursor-default truncate pb-0.5 pl-2 text-sm"
|
||||
style={{ maxWidth: "15rem" }}
|
||||
>
|
||||
{replicatedImport?.importPath}
|
||||
</p>
|
||||
</Tooltip>
|
||||
</div>
|
||||
. Approving these changes will add them to that import.
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-sm text-bunker-300">
|
||||
<p className="inline">Secret(s) in</p>
|
||||
<p
|
||||
className="mx-1 inline rounded bg-primary-600/40 text-primary-300"
|
||||
style={{ padding: "2px 4px" }}
|
||||
>
|
||||
{secretApprovalRequestDetails?.environment}
|
||||
</p>
|
||||
<div className="mr-1 inline-flex w-min items-center rounded border border-mineshaft-500 pl-1 pr-2">
|
||||
<p className="cursor-default border-r border-mineshaft-500 pr-1">
|
||||
<FontAwesomeIcon icon={faFolder} className="text-primary" size="sm" />
|
||||
</p>
|
||||
<Tooltip content={formatReservedPaths(secretApprovalRequestDetails.secretPath)}>
|
||||
<p
|
||||
className="cursor-default truncate pb-0.5 pl-2 text-sm"
|
||||
style={{ maxWidth: "20rem" }}
|
||||
>
|
||||
{formatReservedPaths(secretApprovalRequestDetails.secretPath)}
|
||||
</p>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<p className="inline">
|
||||
have pending changes. Approving these changes will add them to that environment
|
||||
and path.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col space-y-4">
|
||||
{secretApprovalRequestDetails.commits.map(
|
||||
({ op, secretVersion, secret, ...newVersion }, index) => (
|
||||
|
||||
Reference in New Issue
Block a user