mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-16 17:30:06 +00:00
Fixing UI, UX, and bugs in the dashboard
This commit is contained in:
@@ -29,7 +29,7 @@ const SecretValue = ({ workspaceId, env, envName, secretKey }: SecretValueProps)
|
||||
return (
|
||||
<FormControl label={envName}>
|
||||
<Input
|
||||
className="w-full text-ellipsis font-mono focus:text-bunker-100 focus:ring-transparent"
|
||||
className={`w-full text-ellipsis font-mono focus:ring-transparent ${getValue(secret) === "Not found" && "text-mineshaft-500"}`}
|
||||
value={getValue(secret)}
|
||||
isReadOnly
|
||||
rightIcon={isSecretsLoading ? <Spinner /> : undefined}
|
||||
|
||||
@@ -45,6 +45,7 @@ export const PitDrawer = ({
|
||||
<Button
|
||||
key={_id}
|
||||
className="py-3 px-4 text-sm"
|
||||
isFullWidth
|
||||
variant={
|
||||
(i === 0 && index === 0 && snapshotId === null) || snapshotId === _id
|
||||
? 'solid'
|
||||
|
||||
+8
-8
@@ -74,10 +74,10 @@ export const SecretDetailDrawer = ({
|
||||
return (
|
||||
<Drawer onOpenChange={onOpenChange} isOpen={isDrawerOpen}>
|
||||
<DrawerContent
|
||||
className="border-l border-mineshaft-500 bg-bunker"
|
||||
className="border-l border-mineshaft-500 bg-bunker dark"
|
||||
title="Secret"
|
||||
footerContent={
|
||||
<div className="flex flex-col space-y-4 pt-4 shadow-md">
|
||||
<div className="flex flex-col space-y-2 pt-4 shadow-md">
|
||||
<div>
|
||||
<Button
|
||||
variant="star"
|
||||
@@ -103,7 +103,7 @@ export const SecretDetailDrawer = ({
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="">
|
||||
<div className="dark:[color-scheme:dark]">
|
||||
<FormControl label="Key">
|
||||
<Input isDisabled {...register(`secrets.${index}.key`)} />
|
||||
</FormControl>
|
||||
@@ -173,11 +173,11 @@ export const SecretDetailDrawer = ({
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</FormControl>
|
||||
<div className="mb-4 text-sm text-bunker-300">
|
||||
<div className="mb-4 text-sm text-bunker-300 dark">
|
||||
<div className="mb-2">Version History</div>
|
||||
<div className="flex h-52 flex-col space-y-4 overflow-y-auto overflow-x-hidden rounded-md bg-bunker-800 p-2">
|
||||
<div className="flex h-48 flex-col space-y-2 border border-mineshaft-600 overflow-y-auto overflow-x-hidden rounded-md bg-bunker-800 p-2 dark:[color-scheme:dark]">
|
||||
{secretVersion?.map(({ createdAt, value, id }, i) => (
|
||||
<div key={id} className="flex flex-col space-y-2">
|
||||
<div key={id} className="flex flex-col space-y-1">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div>
|
||||
<FontAwesomeIcon icon={i === 0 ? faCircleDot : faCircle} size="sm" />
|
||||
@@ -193,7 +193,7 @@ export const SecretDetailDrawer = ({
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-1 flex items-center space-x-2 border-l border-bunker-300 pl-4">
|
||||
<div className="ml-1.5 flex items-center space-x-2 border-l border-bunker-300 pl-4">
|
||||
<div className="rounded-sm bg-primary-500/30 px-1">Value:</div>
|
||||
<div className="font-mono">{value}</div>
|
||||
</div>
|
||||
@@ -202,7 +202,7 @@ export const SecretDetailDrawer = ({
|
||||
</div>
|
||||
</div>
|
||||
<FormControl label="Comments & Notes">
|
||||
<TextArea isDisabled={isReadOnly} {...register(`secrets.${index}.comment`)} rows={5} />
|
||||
<TextArea className="border border-mineshaft-600 text-sm" isDisabled={isReadOnly} {...register(`secrets.${index}.comment`)} rows={5} />
|
||||
</FormControl>
|
||||
</div>
|
||||
</DrawerContent>
|
||||
|
||||
@@ -84,15 +84,15 @@ export const SecretDropzone = ({ isSmaller, onParsedEnv, onAddNewSecret }: Props
|
||||
onDragOver={handleDrag}
|
||||
onDrop={handleDrop}
|
||||
className={twMerge(
|
||||
'relative mb-4 mt-4 flex w-full cursor-pointer items-center justify-center space-x-2 rounded-md bg-mineshaft-900 py-8 px-2 opacity-60 outline-dashed outline-2 outline-chicago-600 duration-200 hover:opacity-100',
|
||||
'relative mb-4 mt-4 max-w-[calc(100vw-292px)] flex w-full cursor-pointer text-mineshaft-200 items-center py-8 justify-center space-x-2 rounded-md bg-mineshaft-900 px-2 mx-0.5 opacity-60 outline-dashed outline-2 outline-chicago-600 duration-200 hover:opacity-100',
|
||||
isDragActive && 'opacity-100',
|
||||
!isSmaller && 'flex-col space-y-4',
|
||||
!isSmaller && 'flex-col space-y-4 max-w-3xl py-20',
|
||||
isLoading && 'bg-bunker-800'
|
||||
)}
|
||||
>
|
||||
{isLoading ? (
|
||||
<div className="mb-16 flex items-center justify-center pt-16">
|
||||
<img src="/images/loading/loading.gif" height={70} width={120} alt="google logo" />
|
||||
<img src="/images/loading/loading.gif" height={70} width={120} alt="loading animation" />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
@@ -112,9 +112,9 @@ export const SecretDropzone = ({ isSmaller, onParsedEnv, onAddNewSecret }: Props
|
||||
{!isSmaller && (
|
||||
<>
|
||||
<div className="flex w-full flex-row items-center justify-center py-4">
|
||||
<div className="w-1/5 border-t border-gray-700" />
|
||||
<p className="mx-4 text-xs text-gray-400">OR</p>
|
||||
<div className="w-1/5 border-t border-gray-700" />
|
||||
<div className="w-1/5 border-t border-mineshaft-700" />
|
||||
<p className="mx-4 text-xs text-mineshaft-400">OR</p>
|
||||
<div className="w-1/5 border-t border-mineshaft-700" />
|
||||
</div>
|
||||
<div>
|
||||
<Button variant="star" onClick={onAddNewSecret}>
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
/* eslint-disable react/jsx-no-useless-fragment */
|
||||
import { SyntheticEvent, useRef } from 'react';
|
||||
import { Controller, useFieldArray, useFormContext, useWatch } from 'react-hook-form';
|
||||
import {
|
||||
faCircle,
|
||||
faCodeBranch,
|
||||
faComment,
|
||||
faEllipsis,
|
||||
faInfoCircle,
|
||||
faPlus,
|
||||
faSquare,
|
||||
faSquareCheck,
|
||||
faTags,
|
||||
faTrash
|
||||
faXmark
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { cx } from 'cva';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
import guidGenerator from '@app/components/utilities/randomId';
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
FormControl,
|
||||
HoverCard,
|
||||
HoverCardContent,
|
||||
@@ -62,6 +64,7 @@ const tagColors = [
|
||||
{ bg: 'bg-[#C40B13]/40', text: 'text-[#FFDEDE]/70' },
|
||||
{ bg: 'bg-[#332FD0]/40', text: 'text-[#DFF6FF]/70' }
|
||||
];
|
||||
const REGEX = /([$]{.*?})/g;
|
||||
|
||||
export const SecretInputRow = ({
|
||||
index,
|
||||
@@ -75,8 +78,15 @@ export const SecretInputRow = ({
|
||||
onSecretDelete,
|
||||
searchTerm
|
||||
}: Props): JSX.Element => {
|
||||
const ref = useRef<HTMLDivElement | null>(null);
|
||||
const syncScroll = (e: SyntheticEvent<HTMLDivElement>) => {
|
||||
if (ref.current === null) return;
|
||||
|
||||
ref.current.scrollTop = e.currentTarget.scrollTop;
|
||||
ref.current.scrollLeft = e.currentTarget.scrollLeft;
|
||||
};
|
||||
const { register, setValue, control } = useFormContext<FormData>();
|
||||
const [canRevealSecret, setCanRevealSecret] = useToggle();
|
||||
const [canRevealSecret] = useToggle();
|
||||
// comment management in a row
|
||||
const {
|
||||
fields: secretTags,
|
||||
@@ -86,7 +96,6 @@ export const SecretInputRow = ({
|
||||
|
||||
// to get details on a secret
|
||||
const secret = useWatch({ name: `secrets.${index}`, control });
|
||||
|
||||
const hasComment = Boolean(secret.comment);
|
||||
const tags = secret.tags || [];
|
||||
const selectedTagIds = tags.reduce<Record<string, boolean>>(
|
||||
@@ -106,6 +115,7 @@ export const SecretInputRow = ({
|
||||
setValue(`secrets.${index}.valueOverride`, '');
|
||||
setValue(`secrets.${index}.overrideAction`, SecretActionType.Deleted, { shouldDirty: true });
|
||||
} else {
|
||||
setValue(`secrets.${index}.valueOverride`, '');
|
||||
setValue(
|
||||
`secrets.${index}.overrideAction`,
|
||||
secret?.idOverride ? SecretActionType.Modified : SecretActionType.Created,
|
||||
@@ -146,8 +156,8 @@ export const SecretInputRow = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<tr className="group min-w-full flex flex-row">
|
||||
<td className="w-10 px-4 flex items-center justify-center"><div className='text-center text-xs text-bunker-400'>{index + 1}</div></td>
|
||||
<tr className="group min-w-full flex flex-row items-center">
|
||||
<td className="w-10 h-10 px-4 flex items-center justify-center"><div className='text-center w-10 text-xs text-bunker-400'>{index + 1}</div></td>
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue=""
|
||||
@@ -156,30 +166,67 @@ export const SecretInputRow = ({
|
||||
<HoverCard openDelay={0} open={error?.message ? undefined : false}>
|
||||
<HoverCardTrigger asChild>
|
||||
<td className={cx(error?.message ? 'rounded ring ring-red/50' : null)}>
|
||||
<div className="min-w-[220px] relative flex items-center">
|
||||
<div className="min-w-[220px] lg:min-w-[240px] xl:min-w-[280px] relative flex items-center justify-end w-full">
|
||||
<Input
|
||||
autoComplete="off"
|
||||
variant="plain"
|
||||
isDisabled={isReadOnly || shouldBeBlockedInAddOnly || isRollbackMode}
|
||||
className="w-full text-ellipsis font-mono focus:text-bunker-100 focus:ring-transparent"
|
||||
className="w-full focus:text-bunker-100 focus:ring-transparent"
|
||||
{...field}
|
||||
/>
|
||||
{!isAddOnly && (
|
||||
<IconButton
|
||||
variant="plain"
|
||||
className={twMerge(
|
||||
'w-0 overflow-hidden p-0 group-hover:w-6',
|
||||
isOverridden && 'w-6 text-primary'
|
||||
)}
|
||||
onClick={onSecretOverride}
|
||||
isDisabled={isRollbackMode || isReadOnly}
|
||||
ariaLabel="info"
|
||||
>
|
||||
<div className="flex items-center space-x-1">
|
||||
<FontAwesomeIcon icon={faCodeBranch} className="text-base" />
|
||||
<div className="w-max flex flex-row items-center justify-end">
|
||||
<Tooltip content="Comment">
|
||||
<div className={`${hasComment ? "w-5" : "w-0"} overflow-hidden group-hover:w-5 mt-0.5`}>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<IconButton
|
||||
className={twMerge(
|
||||
'w-0 overflow-hidden p-0 group-hover:w-5',
|
||||
hasComment && 'w-5 text-primary'
|
||||
)}
|
||||
variant="plain"
|
||||
size="md"
|
||||
ariaLabel="add-tag"
|
||||
>
|
||||
<FontAwesomeIcon icon={faComment} />
|
||||
</IconButton>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto bg-mineshaft-800 border border-mineshaft-600 drop-shadow-2xl p-2">
|
||||
<FormControl label="Comment" className="mb-0">
|
||||
<TextArea
|
||||
isDisabled={isReadOnly || isRollbackMode || shouldBeBlockedInAddOnly}
|
||||
className="border border-mineshaft-600 text-sm"
|
||||
{...register(`secrets.${index}.comment`)}
|
||||
rows={8}
|
||||
cols={30}
|
||||
/>
|
||||
</FormControl>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
</IconButton>
|
||||
)}
|
||||
</Tooltip>
|
||||
{!isAddOnly && (
|
||||
<div>
|
||||
<Tooltip content="Override with a personal value">
|
||||
<IconButton
|
||||
variant="plain"
|
||||
className={twMerge(
|
||||
'w-0 overflow-hidden p-0 group-hover:w-6 group-hover:ml-1 mt-0.5',
|
||||
isOverridden && 'w-6 text-primary ml-1'
|
||||
)}
|
||||
onClick={onSecretOverride}
|
||||
size="md"
|
||||
isDisabled={isRollbackMode || isReadOnly}
|
||||
ariaLabel="info"
|
||||
>
|
||||
<div className="flex items-center space-x-1">
|
||||
<FontAwesomeIcon icon={faCodeBranch} className="text-base" />
|
||||
</div>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</HoverCardTrigger>
|
||||
@@ -194,36 +241,86 @@ export const SecretInputRow = ({
|
||||
</HoverCard>
|
||||
)}
|
||||
/>
|
||||
<div className="flex-grow w-full">
|
||||
{!isOverridden && (
|
||||
<Input
|
||||
variant="plain"
|
||||
isReadOnly={isReadOnly || shouldBeBlockedInAddOnly || isRollbackMode}
|
||||
className="w-full text-ellipsis font-mono focus:text-bunker-100 focus:ring-transparent"
|
||||
type={canRevealSecret || isSecretValueHidden ? 'text' : 'password'}
|
||||
{...register(`secrets.${index}.value`)}
|
||||
placeholder="EMPTY"
|
||||
onBlur={setCanRevealSecret.off}
|
||||
onFocus={setCanRevealSecret.on}
|
||||
autoComplete="off"
|
||||
/>
|
||||
)}
|
||||
{isOverridden && (
|
||||
<Input
|
||||
variant="plain"
|
||||
isReadOnly={isReadOnly || isAddOnly || isRollbackMode}
|
||||
className="w-full text-ellipsis font-mono focus:text-bunker-100 focus:ring-transparent"
|
||||
type={canRevealSecret || isSecretValueHidden ? 'text' : 'password'}
|
||||
placeholder="EMPTY"
|
||||
<td className="flex flex-row w-full justify-center h-8 items-center">
|
||||
<div className="group relative whitespace-pre flex flex-col justify-center w-full px-1.5">
|
||||
{isOverridden
|
||||
? <input
|
||||
{...register(`secrets.${index}.valueOverride`)}
|
||||
onBlur={setCanRevealSecret.off}
|
||||
onFocus={setCanRevealSecret.on}
|
||||
autoComplete="off"
|
||||
onScroll={syncScroll}
|
||||
readOnly={isReadOnly || isRollbackMode || (isOverridden ? isAddOnly : shouldBeBlockedInAddOnly)}
|
||||
className={`${
|
||||
(!canRevealSecret && isSecretValueHidden)
|
||||
? 'text-transparent focus:text-transparent active:text-transparent'
|
||||
: ''
|
||||
} z-10 peer font-mono ph-no-capture bg-transparent caret-white text-transparent text-sm px-2 py-2 w-full min-w-16 outline-none duration-200 no-scrollbar no-scrollbar::-webkit-scrollbar`}
|
||||
spellCheck="false"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<td className="flex items-center min-w-sm">
|
||||
<div className="flex items-center group-hover:mr-0 group-hover:border-r group-hover:border-mineshaft-600">
|
||||
: <input
|
||||
{...register(`secrets.${index}.value`)}
|
||||
onScroll={syncScroll}
|
||||
readOnly={isReadOnly || isRollbackMode || (isOverridden ? isAddOnly : shouldBeBlockedInAddOnly)}
|
||||
className={`${
|
||||
(!canRevealSecret && isSecretValueHidden)
|
||||
? 'text-transparent focus:text-transparent active:text-transparent'
|
||||
: ''
|
||||
} z-10 peer font-mono ph-no-capture bg-transparent caret-white text-transparent text-sm px-2 py-2 w-full min-w-16 outline-none duration-200 no-scrollbar no-scrollbar::-webkit-scrollbar`}
|
||||
spellCheck="false"
|
||||
/>}
|
||||
<div
|
||||
ref={ref}
|
||||
className={`${
|
||||
(!canRevealSecret && isSecretValueHidden) && !isOverridden
|
||||
? 'text-bunker-800 group-hover:text-gray-400 peer-focus:text-gray-100 peer-active:text-gray-400 duration-200'
|
||||
: ''
|
||||
} ${isOverridden ? 'text-primary-300' : 'text-gray-400'}
|
||||
absolute flex flex-row whitespace-pre font-mono z-0 ${(!canRevealSecret && isSecretValueHidden) ? 'invisible' : 'visible'} peer-focus:visible mt-0.5 ph-no-capture overflow-x-scroll bg-transparent h-10 text-sm px-2 py-2 w-full min-w-16 outline-none duration-100 no-scrollbar no-scrollbar::-webkit-scrollbar`}
|
||||
>
|
||||
{(isOverridden ? secret.valueOverride : secret.value)?.split('').length === 0 && <span className='text-bunker-400/80 font-sans'>EMPTY</span>}
|
||||
{(isOverridden ? secret.valueOverride : secret.value)?.split(REGEX).map((word) => {
|
||||
if (word.match(REGEX) !== null) {
|
||||
return (
|
||||
<span className="ph-no-capture text-yellow" key={index}>
|
||||
{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) === '}' ? (
|
||||
<span className="ph-no-capture text-yellow">
|
||||
{word.slice(word.length - 1, word.length)}
|
||||
</span>
|
||||
) : (
|
||||
<span className="ph-no-capture text-yellow-400">
|
||||
{word.slice(word.length - 1, word.length)}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span key={`${word}_${index + 1}`} className="ph-no-capture">
|
||||
{word}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{(!canRevealSecret && isSecretValueHidden) && (
|
||||
<div className='absolute flex flex-row justify-between items-center z-0 peer pr-2 peer-active:hidden peer-focus:hidden group-hover:bg-white/[0.00] duration-100 h-10 w-full text-bunker-400 text-clip'>
|
||||
<div className="px-2 flex flex-row items-center overflow-x-scroll no-scrollbar no-scrollbar::-webkit-scrollbar">
|
||||
{(isOverridden ? secret.valueOverride : secret.value)?.split('').map(() => (
|
||||
<FontAwesomeIcon
|
||||
key={guidGenerator()}
|
||||
className="text-xxs mr-0.5"
|
||||
icon={faCircle}
|
||||
/>
|
||||
))}
|
||||
{(isOverridden ? secret.valueOverride : secret.value)?.split('').length === 0 && <span className='text-bunker-400/80 text-sm'>EMPTY</span>}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="flex items-center min-w-sm h-10">
|
||||
<div className="flex items-center pl-2">
|
||||
{secretTags.map(({ id, slug }, i) => (
|
||||
<Tag
|
||||
className={cx(
|
||||
@@ -242,8 +339,8 @@ export const SecretInputRow = ({
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<div>
|
||||
<Tooltip content="Add tag">
|
||||
<IconButton variant="star" size="xs" ariaLabel="add-tag">
|
||||
<Tooltip content="Add tags">
|
||||
<IconButton variant="star" size="xs" ariaLabel="add-tag" className="py-[0.42rem]">
|
||||
<FontAwesomeIcon icon={faTags} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
@@ -251,25 +348,29 @@ export const SecretInputRow = ({
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
side="left"
|
||||
className="max-h-96 w-auto min-w-[240px] overflow-y-auto overflow-x-hidden p-2 text-bunker-100"
|
||||
className="max-h-96 w-auto min-w-[200px] overflow-y-auto overflow-x-hidden p-2 text-bunker-200 bg-mineshaft-800 border border-mineshaft-600"
|
||||
hideCloseBtn
|
||||
>
|
||||
<div className="mb-2 text-sm font-medium">Add tags to this secret</div>
|
||||
<div className="mb-2 text-sm font-medium text-center text-bunker-200 px-2">Add tags to {secret.key || "this secret"}</div>
|
||||
<div className="flex flex-col space-y-1">
|
||||
{wsTags?.map((wsTag) => (
|
||||
<Button
|
||||
variant="plain"
|
||||
size="sm"
|
||||
isFullWidth
|
||||
className={twMerge(
|
||||
'justify-start bg-mineshaft-800 text-bunker-100',
|
||||
'justify-start bg-mineshaft-600 text-bunker-100 hover:bg-mineshaft-500',
|
||||
selectedTagIds?.[wsTag.slug] && 'text-primary'
|
||||
)}
|
||||
onClick={() => onSelectTag(wsTag)}
|
||||
leftIcon={
|
||||
<FontAwesomeIcon
|
||||
icon={selectedTagIds?.[wsTag.slug] ? faSquareCheck : faSquare}
|
||||
/>
|
||||
<Checkbox
|
||||
className="data-[state=checked]:bg-primary mr-0"
|
||||
id="autoCapitalization"
|
||||
isChecked={selectedTagIds?.[wsTag.slug]}
|
||||
onCheckedChange={() => {}}
|
||||
>
|
||||
{}
|
||||
</Checkbox>
|
||||
}
|
||||
key={wsTag._id}
|
||||
>
|
||||
@@ -277,10 +378,10 @@ export const SecretInputRow = ({
|
||||
</Button>
|
||||
))}
|
||||
<Button
|
||||
variant="plain"
|
||||
variant="star"
|
||||
color="primary"
|
||||
size="sm"
|
||||
isFullWidth
|
||||
className="mt-4 justify-start bg-mineshaft-800 text-bunker-400 hover:text-primary"
|
||||
className="mt-4 justify-start bg-mineshaft-600 h-7 px-1"
|
||||
onClick={onCreateTagOpen}
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
>
|
||||
@@ -292,44 +393,30 @@ export const SecretInputRow = ({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={`${hasComment ? "w-8" : "w-0"} overflow-hidden group-hover:w-8`}>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<IconButton variant="star" size="xs" ariaLabel="add-tag">
|
||||
<FontAwesomeIcon icon={faComment} />
|
||||
</IconButton>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto">
|
||||
<FormControl label="Comment" className="mb-0">
|
||||
<TextArea
|
||||
isDisabled={isReadOnly || isRollbackMode || shouldBeBlockedInAddOnly}
|
||||
{...register(`secrets.${index}.comment`)}
|
||||
rows={8}
|
||||
cols={30}
|
||||
/>
|
||||
</FormControl>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<div className="flex w-0 items-center justify-end space-x-2 overflow-hidden transition-all group-hover:w-16">
|
||||
<Tooltip content="delete">
|
||||
<IconButton
|
||||
size="xs"
|
||||
colorSchema="danger"
|
||||
ariaLabel="delete"
|
||||
isDisabled={isReadOnly || isRollbackMode}
|
||||
onClick={() => onSecretDelete(index, secret._id, secret?.idOverride)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<div className="flex w-0 group-hover:w-14 invisible group-hover:visible duration-0 items-center justify-end space-x-2 overflow-hidden transition-all">
|
||||
{!isAddOnly && (
|
||||
<Tooltip content="more">
|
||||
<IconButton size="xs" variant="solid" onClick={onRowExpand} ariaLabel="expand">
|
||||
<FontAwesomeIcon icon={faEllipsis} />
|
||||
<div>
|
||||
<Tooltip content="Settings">
|
||||
<IconButton size="lg" colorSchema="primary" variant="plain" onClick={onRowExpand} ariaLabel="expand">
|
||||
<FontAwesomeIcon icon={faEllipsis} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<Tooltip content="Delete">
|
||||
<IconButton
|
||||
size="md"
|
||||
variant="plain"
|
||||
colorSchema="danger"
|
||||
ariaLabel="delete"
|
||||
isDisabled={isReadOnly || isRollbackMode}
|
||||
onClick={() => onSecretDelete(index, secret._id, secret?.idOverride)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faXmark} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
+56
-12
@@ -1,5 +1,8 @@
|
||||
import { faArrowDown, faArrowUp } from '@fortawesome/free-solid-svg-icons';
|
||||
import { Controller } from 'react-hook-form';
|
||||
import { faArrowDown, faArrowUp, faCodeBranch, faInfoCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@radix-ui/react-hover-card';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
import { IconButton } from '@app/components/v2';
|
||||
|
||||
@@ -13,17 +16,58 @@ export const SecretTableHeader = ({
|
||||
onSort
|
||||
}: Props): JSX.Element => (
|
||||
<thead>
|
||||
<tr className="flex flex-row">
|
||||
{/* <th className="w-10 text-center"/> */}
|
||||
<th className='w-1/5 min-w-[220px]'>
|
||||
<div className="inline-flex items-end">
|
||||
Key
|
||||
<IconButton variant="plain" className="ml-2" ariaLabel="sort" onClick={onSort}>
|
||||
<FontAwesomeIcon icon={sortDir === 'asc' ? faArrowDown : faArrowUp} />
|
||||
</IconButton>
|
||||
</div>
|
||||
</th>
|
||||
<th className='w-full'>Value</th>
|
||||
<tr className="absolute flex flex-row sticky top-0">
|
||||
<td className="w-10 px-4 flex items-center justify-center">
|
||||
<div className='text-center w-10 text-xs text-transparent'>{0}</div>
|
||||
</td>
|
||||
<Controller
|
||||
defaultValue=""
|
||||
name="na"
|
||||
render={({ fieldState: { error } }) => (
|
||||
<HoverCard openDelay={0} open={error?.message ? undefined : false}>
|
||||
<HoverCardTrigger asChild>
|
||||
<td className='flex items-center'>
|
||||
<div className="min-w-[220px] lg:min-w-[240px] xl:min-w-[280px] relative flex items-center justify-start pl-2.5 w-full">
|
||||
<div className="inline-flex items-end text-md font-medium">
|
||||
Key
|
||||
<IconButton variant="plain" className="ml-2" ariaLabel="sort" onClick={onSort}>
|
||||
<FontAwesomeIcon icon={sortDir === 'asc' ? faArrowDown : faArrowUp} />
|
||||
</IconButton>
|
||||
</div>
|
||||
<div className="w-max flex flex-row items-center justify-end">
|
||||
<div className="w-5 overflow-hidden group-hover:w-5 mt-1"/>
|
||||
{!true && (
|
||||
<IconButton
|
||||
variant="plain"
|
||||
className={twMerge(
|
||||
'w-0 overflow-hidden p-0 group-hover:w-6 group-hover:ml-1',
|
||||
true && 'w-6 text-primary ml-1'
|
||||
)}
|
||||
size="md"
|
||||
ariaLabel="info"
|
||||
>
|
||||
<div className="flex items-center space-x-1">
|
||||
<FontAwesomeIcon icon={faCodeBranch} className="text-base" />
|
||||
</div>
|
||||
</IconButton>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent className="w-auto py-2 pt-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div>
|
||||
<FontAwesomeIcon icon={faInfoCircle} className="text-red" />
|
||||
</div>
|
||||
<div className="text-sm">{error?.message}</div>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
)}
|
||||
/>
|
||||
<th className="flex flex-row w-full"><div className="text-sm font-medium">Value</div></th>
|
||||
</tr>
|
||||
<tr className='h-0 w-full border border-mineshaft-600'/>
|
||||
</thead>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user