mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge pull request #304 from Infisical/secret-tagging
Revamped the dashboard look
This commit is contained in:
39
frontend/package-lock.json
generated
39
frontend/package-lock.json
generated
@@ -20,6 +20,7 @@
|
|||||||
"@radix-ui/react-checkbox": "^1.0.1",
|
"@radix-ui/react-checkbox": "^1.0.1",
|
||||||
"@radix-ui/react-dialog": "^1.0.2",
|
"@radix-ui/react-dialog": "^1.0.2",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.0.2",
|
"@radix-ui/react-dropdown-menu": "^2.0.2",
|
||||||
|
"@radix-ui/react-hover-card": "^1.0.3",
|
||||||
"@radix-ui/react-label": "^2.0.0",
|
"@radix-ui/react-label": "^2.0.0",
|
||||||
"@radix-ui/react-popover": "^1.0.3",
|
"@radix-ui/react-popover": "^1.0.3",
|
||||||
"@radix-ui/react-progress": "^1.0.1",
|
"@radix-ui/react-progress": "^1.0.1",
|
||||||
@@ -3858,6 +3859,27 @@
|
|||||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-rr2+DxPlMhR57IPcNvZ85X8chytdfj7kyVToyR5Ge0r4IJEFiyPs0Cs8/K8oe5zt+yo0F8f29vtC8tNNK+ZIkA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@radix-ui/primitive": "1.0.0",
|
||||||
|
"@radix-ui/react-compose-refs": "1.0.0",
|
||||||
|
"@radix-ui/react-context": "1.0.0",
|
||||||
|
"@radix-ui/react-dismissable-layer": "1.0.2",
|
||||||
|
"@radix-ui/react-popper": "1.1.0",
|
||||||
|
"@radix-ui/react-portal": "1.0.1",
|
||||||
|
"@radix-ui/react-presence": "1.0.0",
|
||||||
|
"@radix-ui/react-primitive": "1.0.1",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-id": {
|
"node_modules/@radix-ui/react-id": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.0.tgz",
|
||||||
@@ -25050,6 +25072,23 @@
|
|||||||
"@radix-ui/react-use-callback-ref": "1.0.0"
|
"@radix-ui/react-use-callback-ref": "1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@radix-ui/react-hover-card": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-rr2+DxPlMhR57IPcNvZ85X8chytdfj7kyVToyR5Ge0r4IJEFiyPs0Cs8/K8oe5zt+yo0F8f29vtC8tNNK+ZIkA==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@radix-ui/primitive": "1.0.0",
|
||||||
|
"@radix-ui/react-compose-refs": "1.0.0",
|
||||||
|
"@radix-ui/react-context": "1.0.0",
|
||||||
|
"@radix-ui/react-dismissable-layer": "1.0.2",
|
||||||
|
"@radix-ui/react-popper": "1.1.0",
|
||||||
|
"@radix-ui/react-portal": "1.0.1",
|
||||||
|
"@radix-ui/react-presence": "1.0.0",
|
||||||
|
"@radix-ui/react-primitive": "1.0.1",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@radix-ui/react-id": {
|
"@radix-ui/react-id": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.0.tgz",
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"@radix-ui/react-checkbox": "^1.0.1",
|
"@radix-ui/react-checkbox": "^1.0.1",
|
||||||
"@radix-ui/react-dialog": "^1.0.2",
|
"@radix-ui/react-dialog": "^1.0.2",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.0.2",
|
"@radix-ui/react-dropdown-menu": "^2.0.2",
|
||||||
|
"@radix-ui/react-hover-card": "^1.0.3",
|
||||||
"@radix-ui/react-label": "^2.0.0",
|
"@radix-ui/react-label": "^2.0.0",
|
||||||
"@radix-ui/react-popover": "^1.0.3",
|
"@radix-ui/react-popover": "^1.0.3",
|
||||||
"@radix-ui/react-progress": "^1.0.1",
|
"@radix-ui/react-progress": "^1.0.1",
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
"save-changes": "Save Changes",
|
"save-changes": "Save Changes",
|
||||||
"saved": "Saved",
|
"saved": "Saved",
|
||||||
"drop-zone": "Drag and drop a .env or .yml file here.",
|
"drop-zone": "Drag and drop a .env or .yml file here.",
|
||||||
"drop-zone-keys": "Drag and drop a .env or .yml file here to add more keys.",
|
"drop-zone-keys": "Drag and drop a .env or .yml file here to add more secrets.",
|
||||||
"role": "Role",
|
"role": "Role",
|
||||||
"role_admin": "admin",
|
"role_admin": "admin",
|
||||||
"display-name": "Display Name",
|
"display-name": "Display Name",
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const ListBox = ({
|
|||||||
leaveFrom="opacity-100"
|
leaveFrom="opacity-100"
|
||||||
leaveTo="opacity-0"
|
leaveTo="opacity-0"
|
||||||
>
|
>
|
||||||
<Listbox.Options className="border border-mineshaft-700 z-50 p-2 absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-bunker text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
|
<Listbox.Options className="border border-mineshaft-700 z-[70] p-2 absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-bunker text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm no-scrollbar no-scrollbar::-webkit-scrollbar">
|
||||||
{data.map((person, personIdx) => (
|
{data.map((person, personIdx) => (
|
||||||
<Listbox.Option
|
<Listbox.Option
|
||||||
key={`${person}.${personIdx + 1}`}
|
key={`${person}.${personIdx + 1}`}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const DeleteEnvVar = ({ isOpen, onClose, onSubmit }: Props) => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Transition appear show={isOpen} as={Fragment}>
|
<Transition appear show={isOpen} as={Fragment}>
|
||||||
<Dialog as="div" className="relative z-10" onClose={() => {}}>
|
<Dialog as="div" className="relative z-[80]" onClose={() => {}}>
|
||||||
<div className="fixed inset-0 overflow-y-auto">
|
<div className="fixed inset-0 overflow-y-auto">
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { memo, SyntheticEvent, useRef } from 'react';
|
import { memo, SyntheticEvent, useRef } from 'react';
|
||||||
import { faCircle } from '@fortawesome/free-solid-svg-icons';
|
import { faCircle, faExclamationCircle, faEye, faLayerGroup } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
import guidGenerator from '../utilities/randomId';
|
import guidGenerator from '../utilities/randomId';
|
||||||
|
import { Button } from '../v2';
|
||||||
|
import { HoverObject } from '../v2/HoverCard';
|
||||||
|
|
||||||
const REGEX = /([$]{.*?})/g;
|
const REGEX = /([$]{.*?})/g;
|
||||||
|
|
||||||
@@ -10,10 +12,12 @@ interface DashboardInputFieldProps {
|
|||||||
position: number;
|
position: number;
|
||||||
onChangeHandler: (value: string, position: number) => void;
|
onChangeHandler: (value: string, position: number) => void;
|
||||||
value: string | undefined;
|
value: string | undefined;
|
||||||
type: 'varName' | 'value';
|
type: 'varName' | 'value' | 'comment';
|
||||||
blurred?: boolean;
|
blurred?: boolean;
|
||||||
isDuplicate?: boolean;
|
isDuplicate?: boolean;
|
||||||
override?: boolean;
|
overrideEnabled?: boolean;
|
||||||
|
modifyValueOverride?: (value: string | undefined, position: number) => void;
|
||||||
|
isSideBarOpen?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,6 +30,8 @@ interface DashboardInputFieldProps {
|
|||||||
* @param {boolean} obj.blurred - whether the input field should be blurred (behind the gray dots) or not; this can be turned on/off in the dashboard
|
* @param {boolean} obj.blurred - whether the input field should be blurred (behind the gray dots) or not; this can be turned on/off in the dashboard
|
||||||
* @param {boolean} obj.isDuplicate - if the key name is duplicated
|
* @param {boolean} obj.isDuplicate - if the key name is duplicated
|
||||||
* @param {boolean} obj.override - whether a secret/row should be displalyed as overriden
|
* @param {boolean} obj.override - whether a secret/row should be displalyed as overriden
|
||||||
|
*
|
||||||
|
*
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -36,7 +42,9 @@ const DashboardInputField = ({
|
|||||||
value,
|
value,
|
||||||
blurred,
|
blurred,
|
||||||
isDuplicate,
|
isDuplicate,
|
||||||
override
|
overrideEnabled,
|
||||||
|
modifyValueOverride,
|
||||||
|
isSideBarOpen
|
||||||
}: DashboardInputFieldProps) => {
|
}: DashboardInputFieldProps) => {
|
||||||
const ref = useRef<HTMLDivElement | null>(null);
|
const ref = useRef<HTMLDivElement | null>(null);
|
||||||
const syncScroll = (e: SyntheticEvent<HTMLDivElement>) => {
|
const syncScroll = (e: SyntheticEvent<HTMLDivElement>) => {
|
||||||
@@ -51,41 +59,97 @@ const DashboardInputField = ({
|
|||||||
const error = startsWithNumber || isDuplicate;
|
const error = startsWithNumber || isDuplicate;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-col w-full">
|
<div className={`relative flex-col w-full h-10 ${
|
||||||
|
error && value !== '' ? 'bg-red/[0.15]' : ''
|
||||||
|
} ${
|
||||||
|
isSideBarOpen && 'bg-mineshaft-700 duration-200'
|
||||||
|
}`}>
|
||||||
<div
|
<div
|
||||||
className={`group relative flex flex-col justify-center w-full border ${
|
className={`group relative flex flex-col justify-center items-center h-full ${
|
||||||
error ? 'border-red' : 'border-mineshaft-500'
|
error ? 'w-max' : 'w-full'
|
||||||
} rounded-md`}
|
}`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
onChange={(e) => onChangeHandler(e.target.value.toUpperCase(), position)}
|
onChange={(e) => onChangeHandler(e.target.value.toUpperCase(), position)}
|
||||||
type={type}
|
type={type}
|
||||||
value={value}
|
value={value}
|
||||||
className={`z-10 peer font-mono ph-no-capture bg-bunker-800 rounded-md caret-white text-gray-400 text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-2 ${
|
className={`z-10 peer font-mono ph-no-capture bg-transparent h-full caret-bunker-200 text-sm px-2 w-full min-w-16 outline-none ${
|
||||||
error ? 'focus:ring-red/50' : 'focus:ring-primary/50'
|
error ? 'text-red-600 focus:text-red-500' : 'text-bunker-300 focus:text-bunker-100'
|
||||||
} duration-200`}
|
} duration-200`}
|
||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{startsWithNumber && (
|
{startsWithNumber && (
|
||||||
<p className="text-red text-xs mt-0.5 mx-1 mb-2 max-w-xs">
|
<div className='absolute right-2 top-2 text-red z-50'>
|
||||||
Should not start with a number
|
<HoverObject
|
||||||
</p>
|
text="Secret names should not start with a number"
|
||||||
|
icon={faExclamationCircle}
|
||||||
|
color="red"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
{isDuplicate && !startsWithNumber && (
|
{isDuplicate && value !== '' && !startsWithNumber && (
|
||||||
<p className="text-red text-xs mt-0.5 mx-1 mb-2 max-w-xs">
|
<div className='absolute right-2 top-2 text-red z-50'>
|
||||||
Secret names should be unique
|
<HoverObject
|
||||||
</p>
|
text="Secret names should be unique"
|
||||||
|
icon={faExclamationCircle}
|
||||||
|
color="red"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{!error && <div className={`absolute right-0 top-0 text-red z-50 ${
|
||||||
|
overrideEnabled ? 'visible group-hover:bg-mineshaft-700' : 'invisible group-hover:visible bg-mineshaft-700'
|
||||||
|
} cursor-pointer duration-0`}>
|
||||||
|
<Button variant="plain" onClick={() => {
|
||||||
|
if (modifyValueOverride) {
|
||||||
|
if (overrideEnabled === false) {
|
||||||
|
modifyValueOverride('', position);
|
||||||
|
} else {
|
||||||
|
modifyValueOverride(undefined, position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<HoverObject
|
||||||
|
text={overrideEnabled ? 'This secret is overriden with your personal value' : 'You can override this secret with a personal value'}
|
||||||
|
icon={faLayerGroup}
|
||||||
|
color={overrideEnabled ? 'primary' : 'bunker-400'}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</div>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (type === 'comment') {
|
||||||
|
const startsWithNumber = !Number.isNaN(Number(value?.charAt(0))) && value !== '';
|
||||||
|
const error = startsWithNumber || isDuplicate;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`relative flex-col w-full h-10 ${
|
||||||
|
isSideBarOpen && 'bg-mineshaft-700 duration-200'
|
||||||
|
}`}>
|
||||||
|
<div
|
||||||
|
className={`group relative flex flex-col justify-center items-center ${
|
||||||
|
error ? 'w-max' : 'w-full'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
onChange={(e) => onChangeHandler(e.target.value, position)}
|
||||||
|
type={type}
|
||||||
|
value={value}
|
||||||
|
className='z-10 peer font-mono ph-no-capture bg-transparent py-2.5 caret-bunker-200 text-sm px-2 w-full min-w-16 outline-none text-bunker-300 focus:text-bunker-100 placeholder:text-bunker-400 placeholder:focus:text-transparent placeholder duration-200'
|
||||||
|
spellCheck="false"
|
||||||
|
placeholder='–'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (type === 'value') {
|
if (type === 'value') {
|
||||||
return (
|
return (
|
||||||
<div className="flex-col w-full">
|
<div className="flex-col w-full">
|
||||||
<div className="group relative whitespace-pre flex flex-col justify-center w-full border border-mineshaft-500 rounded-md">
|
<div className="group relative whitespace-pre flex flex-col justify-center w-full">
|
||||||
{override === true && (
|
{overrideEnabled === true && (
|
||||||
<div className="bg-primary-300 absolute top-[0.1rem] right-[0.1rem] z-10 w-min text-xxs px-1 text-black opacity-80 rounded-md">
|
<div className="bg-primary-500 rounded-sm absolute top-[0.1rem] right-[0.1rem] z-0 w-min text-xxs px-1 text-black opacity-80">
|
||||||
Override enabled
|
Override enabled
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -95,19 +159,19 @@ const DashboardInputField = ({
|
|||||||
onScroll={syncScroll}
|
onScroll={syncScroll}
|
||||||
className={`${
|
className={`${
|
||||||
blurred
|
blurred
|
||||||
? 'text-transparent group-hover:text-transparent focus:text-transparent active:text-transparent'
|
? 'text-transparent focus:text-transparent active:text-transparent'
|
||||||
: ''
|
: ''
|
||||||
} z-10 peer font-mono ph-no-capture bg-transparent rounded-md caret-white text-transparent text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-2 focus:ring-primary/50 duration-200 no-scrollbar no-scrollbar::-webkit-scrollbar`}
|
} 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"
|
spellCheck="false"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={`${
|
className={`${
|
||||||
blurred && !override
|
blurred && !overrideEnabled
|
||||||
? 'text-bunker-800 group-hover:text-gray-400 peer-focus:text-gray-400 peer-active:text-gray-400'
|
? 'text-bunker-800 group-hover:text-gray-400 peer-focus:text-gray-100 peer-active:text-gray-400 duration-200'
|
||||||
: ''
|
: ''
|
||||||
} ${override ? 'text-primary-300' : 'text-gray-400'}
|
} ${overrideEnabled ? 'text-primary-300' : 'text-gray-400'}
|
||||||
absolute flex flex-row whitespace-pre font-mono z-0 ph-no-capture overflow-x-scroll bg-bunker-800 h-9 rounded-md text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-2 focus:ring-primary/50 duration-100 no-scrollbar no-scrollbar::-webkit-scrollbar`}
|
absolute flex flex-row whitespace-pre font-mono z-0 ${blurred ? '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`}
|
||||||
>
|
>
|
||||||
{value?.split(REGEX).map((word, id) => {
|
{value?.split(REGEX).map((word, id) => {
|
||||||
if (word.match(REGEX) !== null) {
|
if (word.match(REGEX) !== null) {
|
||||||
@@ -137,7 +201,9 @@ const DashboardInputField = ({
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
{blurred && (
|
{blurred && (
|
||||||
<div className="absolute flex flex-row items-center z-20 peer pr-2 bg-bunker-800 group-hover:hidden peer-hover:hidden peer-focus:hidden peer-active:invisible h-9 w-full rounded-md text-gray-400/50 text-clip">
|
<div className={`absolute flex flex-row justify-between items-center z-0 peer pr-2 ${
|
||||||
|
isSideBarOpen ? 'bg-mineshaft-700 duration-200' : 'bg-mineshaft-800'
|
||||||
|
} 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">
|
<div className="px-2 flex flex-row items-center overflow-x-scroll no-scrollbar no-scrollbar::-webkit-scrollbar">
|
||||||
{value?.split('').map(() => (
|
{value?.split('').map(() => (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
@@ -146,7 +212,9 @@ const DashboardInputField = ({
|
|||||||
icon={faCircle}
|
icon={faCircle}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
{value?.split('').length === 0 && <span className='text-bunker-400/80'>EMPTY</span>}
|
||||||
</div>
|
</div>
|
||||||
|
<div className='invisible group-hover:visible cursor-pointer'><FontAwesomeIcon icon={faEye} /></div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -163,8 +231,8 @@ function inputPropsAreEqual(prev: DashboardInputFieldProps, next: DashboardInput
|
|||||||
prev.type === next.type &&
|
prev.type === next.type &&
|
||||||
prev.position === next.position &&
|
prev.position === next.position &&
|
||||||
prev.blurred === next.blurred &&
|
prev.blurred === next.blurred &&
|
||||||
prev.override === next.override &&
|
prev.overrideEnabled === next.overrideEnabled &&
|
||||||
prev.isDuplicate === next.isDuplicate
|
prev.isDuplicate === next.isDuplicate
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,42 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { faXmark } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
import Button from '../basic/buttons/Button';
|
import Button from '../basic/buttons/Button';
|
||||||
import { DeleteEnvVar } from '../basic/dialog/DeleteEnvVar';
|
import { DeleteEnvVar } from '../basic/dialog/DeleteEnvVar';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onSubmit: () => void
|
onSubmit: () => void;
|
||||||
|
isPlain?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DeleteActionButton = ({ onSubmit }: Props) => {
|
export const DeleteActionButton = ({ onSubmit, isPlain }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-[#9B3535] opacity-70 hover:opacity-100 w-[4.5rem] h-[2.5rem] rounded-md duration-200 ml-2">
|
<div className={`${
|
||||||
<Button
|
!isPlain
|
||||||
|
? 'bg-[#9B3535] opacity-70 hover:opacity-100 w-[4.5rem] h-[2.5rem] rounded-md duration-200 ml-2'
|
||||||
|
: 'cursor-pointer w-[2.35rem] h-[2.35rem] flex items-center justfy-center'}`}>
|
||||||
|
{isPlain
|
||||||
|
? <div
|
||||||
|
onKeyDown={() => null}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
onClick={() => setOpen(true)}
|
||||||
|
className="invisible group-hover:visible"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon className="text-bunker-300 hover:text-red pl-2 pr-6 text-lg mt-0.5" icon={faXmark} />
|
||||||
|
</div>
|
||||||
|
: <Button
|
||||||
text={String(t("Delete"))}
|
text={String(t("Delete"))}
|
||||||
// onButtonPressed={onSubmit}
|
// onButtonPressed={onSubmit}
|
||||||
color="red"
|
color="red"
|
||||||
size="md"
|
size="md"
|
||||||
onButtonPressed={() => setOpen(true)}
|
onButtonPressed={() => setOpen(true)}
|
||||||
/>
|
/>}
|
||||||
<DeleteEnvVar
|
<DeleteEnvVar
|
||||||
isOpen={open}
|
isOpen={open}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const DownloadSecretMenu = ({ data, env }: { data: SecretDataProps[]; env: strin
|
|||||||
leaveFrom="transform opacity-100 scale-100"
|
leaveFrom="transform opacity-100 scale-100"
|
||||||
leaveTo="transform opacity-0 scale-95"
|
leaveTo="transform opacity-0 scale-95"
|
||||||
>
|
>
|
||||||
<Menu.Items className="absolute z-50 drop-shadow-xl right-0 mt-0.5 w-[12rem] origin-top-right rounded-md bg-bunker border border-mineshaft-500 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none p-2 space-y-2">
|
<Menu.Items className="absolute z-[90] drop-shadow-xl right-0 mt-0.5 w-[12rem] origin-top-right rounded-md bg-bunker border border-mineshaft-500 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none p-2 space-y-2">
|
||||||
<Menu.Item>
|
<Menu.Item>
|
||||||
<Button
|
<Button
|
||||||
color="mineshaft"
|
color="mineshaft"
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ const DropZone = ({
|
|||||||
</div>
|
</div>
|
||||||
) : keysExist ? (
|
) : keysExist ? (
|
||||||
<div
|
<div
|
||||||
className="opacity-60 hover:opacity-100 duration-200 relative bg-mineshaft-900 max-w-[calc(100%-1rem)] w-full outline-dashed outline-chicago-600 rounded-md outline-2 flex flex-col items-center justify-center mb-16 mx-auto mt-1 py-8 px-2"
|
className="opacity-60 hover:opacity-100 duration-200 relative bg-mineshaft-900 max-w-[calc(100%-1rem)] w-full outline-dashed outline-chicago-600 rounded-md outline-2 flex flex-col items-center justify-center mb-4 mx-auto mt-1 py-8 px-2"
|
||||||
onDragEnter={handleDragEnter}
|
onDragEnter={handleDragEnter}
|
||||||
onDragOver={handleDragOver}
|
onDragOver={handleDragOver}
|
||||||
onDragLeave={handleDragLeave}
|
onDragLeave={handleDragLeave}
|
||||||
|
|||||||
@@ -3,17 +3,25 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|||||||
import { SecretDataProps } from 'public/data/frequentInterfaces';
|
import { SecretDataProps } from 'public/data/frequentInterfaces';
|
||||||
|
|
||||||
import DashboardInputField from './DashboardInputField';
|
import DashboardInputField from './DashboardInputField';
|
||||||
|
import { DeleteActionButton } from './DeleteActionButton';
|
||||||
|
|
||||||
interface KeyPairProps {
|
interface KeyPairProps {
|
||||||
keyPair: SecretDataProps;
|
keyPair: SecretDataProps;
|
||||||
modifyKey: (value: string, position: number) => void;
|
modifyKey: (value: string, position: number) => void;
|
||||||
modifyValue: (value: string, position: number) => void;
|
modifyValue: (value: string, position: number) => void;
|
||||||
modifyValueOverride: (value: string, position: number) => void;
|
modifyValueOverride: (value: string | undefined, position: number) => void;
|
||||||
|
modifyComment: (value: string, position: number) => void;
|
||||||
isBlurred: boolean;
|
isBlurred: boolean;
|
||||||
isDuplicate: boolean;
|
isDuplicate: boolean;
|
||||||
toggleSidebar: (id: string) => void;
|
toggleSidebar: (id: string) => void;
|
||||||
sidebarSecretId: string;
|
sidebarSecretId: string;
|
||||||
isSnapshot: boolean;
|
isSnapshot: boolean;
|
||||||
|
deleteRow?: (props: DeleteRowFunctionProps) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeleteRowFunctionProps {
|
||||||
|
ids: string[];
|
||||||
|
secretName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,11 +31,13 @@ interface KeyPairProps {
|
|||||||
* @param {function} obj.modifyKey - modify the key of a certain environment variable
|
* @param {function} obj.modifyKey - modify the key of a certain environment variable
|
||||||
* @param {function} obj.modifyValue - modify the value of a certain environment variable
|
* @param {function} obj.modifyValue - modify the value of a certain environment variable
|
||||||
* @param {function} obj.modifyValueOverride - modify the value of a certain environment variable if it is overriden
|
* @param {function} obj.modifyValueOverride - modify the value of a certain environment variable if it is overriden
|
||||||
|
* @param {function} obj.modifyComment - modify the comment of a certain environment variable
|
||||||
* @param {boolean} obj.isBlurred - if the blurring setting is turned on
|
* @param {boolean} obj.isBlurred - if the blurring setting is turned on
|
||||||
* @param {boolean} obj.isDuplicate - list of all the duplicates secret names on the dashboard
|
* @param {boolean} obj.isDuplicate - list of all the duplicates secret names on the dashboard
|
||||||
* @param {function} obj.toggleSidebar - open/close/switch sidebar
|
* @param {function} obj.toggleSidebar - open/close/switch sidebar
|
||||||
* @param {string} obj.sidebarSecretId - the id of a secret for the side bar is displayed
|
* @param {string} obj.sidebarSecretId - the id of a secret for the side bar is displayed
|
||||||
* @param {boolean} obj.isSnapshot - whether this keyPair is in a snapshot. If so, it won't have some features like sidebar
|
* @param {boolean} obj.isSnapshot - whether this keyPair is in a snapshot. If so, it won't have some features like sidebar
|
||||||
|
* @param {function} obj.deleteRow - a function to delete a certain keyPair
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const KeyPair = ({
|
const KeyPair = ({
|
||||||
@@ -35,50 +45,59 @@ const KeyPair = ({
|
|||||||
modifyKey,
|
modifyKey,
|
||||||
modifyValue,
|
modifyValue,
|
||||||
modifyValueOverride,
|
modifyValueOverride,
|
||||||
|
modifyComment,
|
||||||
isBlurred,
|
isBlurred,
|
||||||
isDuplicate,
|
isDuplicate,
|
||||||
toggleSidebar,
|
toggleSidebar,
|
||||||
sidebarSecretId,
|
sidebarSecretId,
|
||||||
isSnapshot
|
isSnapshot,
|
||||||
|
deleteRow
|
||||||
}: KeyPairProps) => (
|
}: KeyPairProps) => (
|
||||||
<div
|
<div
|
||||||
className={`mx-1 flex flex-col items-center ml-1 ${isSnapshot && 'pointer-events-none'} ${
|
className={`group flex flex-col items-center border-b border-mineshaft-500 hover:bg-white/[0.03] duration-100 ${isSnapshot && 'pointer-events-none'} ${
|
||||||
keyPair.id === sidebarSecretId && 'bg-mineshaft-500 duration-200'
|
keyPair.id === sidebarSecretId && 'bg-mineshaft-700 duration-200'
|
||||||
} rounded-md`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="relative flex flex-row justify-between w-full max-w-5xl mr-auto max-h-14 my-1 items-start px-1">
|
<div className="relative flex flex-row justify-between w-full mr-auto max-h-14 items-center">
|
||||||
{keyPair.valueOverride && (
|
<div className='text-bunker-400 text-xs flex items-center justify-center w-14 h-10 cursor-default'>{keyPair.pos + 1}</div>
|
||||||
<div className="group font-normal group absolute top-[1rem] left-[0.2rem] z-40 inline-block text-gray-300 underline hover:text-primary duration-200">
|
<div className="w-80 border-r border-mineshaft-600">
|
||||||
<div className="w-1 h-1 rounded-full bg-primary z-40" />
|
<div className="flex items-center max-h-16">
|
||||||
<span className="absolute z-50 hidden group-hover:flex group-hover:animate-popdown duration-200 w-[10.5rem] -left-[0.4rem] -top-[1.7rem] translate-y-full px-2 py-2 bg-mineshaft-500 rounded-b-md rounded-r-md text-center text-gray-100 text-sm after:content-[''] after:absolute after:left-0 after:bottom-[100%] after:-translate-x-0 after:border-8 after:border-x-transparent after:border-t-transparent after:border-b-mineshaft-500">
|
|
||||||
This secret is overriden
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="min-w-xl w-96">
|
|
||||||
<div className="flex pr-1.5 items-center rounded-lg mt-4 md:mt-0 max-h-16">
|
|
||||||
<DashboardInputField
|
<DashboardInputField
|
||||||
onChangeHandler={modifyKey}
|
onChangeHandler={modifyKey}
|
||||||
type="varName"
|
type="varName"
|
||||||
position={keyPair.pos}
|
position={keyPair.pos}
|
||||||
value={keyPair.key}
|
value={keyPair.key}
|
||||||
isDuplicate={isDuplicate}
|
isDuplicate={isDuplicate}
|
||||||
|
overrideEnabled={keyPair.valueOverride !== undefined}
|
||||||
|
modifyValueOverride={modifyValueOverride}
|
||||||
|
isSideBarOpen={keyPair.id === sidebarSecretId}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full min-w-xl">
|
<div className="w-full border-r border-mineshaft-600">
|
||||||
<div
|
<div
|
||||||
className={`flex min-w-xl items-center ${
|
className='flex items-center rounded-lg mt-4 md:mt-0 max-h-10'
|
||||||
!isSnapshot && 'pr-1.5'
|
|
||||||
} rounded-lg mt-4 md:mt-0 max-h-10`}
|
|
||||||
>
|
>
|
||||||
<DashboardInputField
|
<DashboardInputField
|
||||||
onChangeHandler={keyPair.valueOverride ? modifyValueOverride : modifyValue}
|
onChangeHandler={keyPair.valueOverride !== undefined ? modifyValueOverride : modifyValue}
|
||||||
type="value"
|
type="value"
|
||||||
position={keyPair.pos}
|
position={keyPair.pos}
|
||||||
value={keyPair.valueOverride ? keyPair.valueOverride : keyPair.value}
|
value={keyPair.valueOverride !== undefined ? keyPair.valueOverride : keyPair.value}
|
||||||
blurred={isBlurred}
|
blurred={isBlurred}
|
||||||
override={Boolean(keyPair.valueOverride)}
|
overrideEnabled={keyPair.valueOverride !== undefined}
|
||||||
|
isSideBarOpen={keyPair.id === sidebarSecretId}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="w-96 border-r border-mineshaft-600">
|
||||||
|
<div className="flex items-center max-h-16">
|
||||||
|
<DashboardInputField
|
||||||
|
onChangeHandler={modifyComment}
|
||||||
|
type="comment"
|
||||||
|
position={keyPair.pos}
|
||||||
|
value={keyPair.comment}
|
||||||
|
isDuplicate={isDuplicate}
|
||||||
|
isSideBarOpen={keyPair.id === sidebarSecretId}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -88,11 +107,19 @@ const KeyPair = ({
|
|||||||
role="button"
|
role="button"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onClick={() => toggleSidebar(keyPair.id)}
|
onClick={() => toggleSidebar(keyPair.id)}
|
||||||
className="cursor-pointer w-[2.35rem] h-[2.35rem] bg-mineshaft-700 hover:bg-chicago-700 rounded-md flex flex-row justify-center items-center duration-200"
|
className="cursor-pointer w-[2.35rem] h-[2.35rem] px-6 rounded-md invisible group-hover:visible flex flex-row justify-center items-center"
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon className="text-gray-300 px-2.5 text-lg mt-0.5" icon={faEllipsis} />
|
<FontAwesomeIcon className="text-bunker-300 hover:text-primary text-lg" icon={faEllipsis} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{!isSnapshot && (
|
||||||
|
<DeleteActionButton
|
||||||
|
onSubmit={() => { if (deleteRow) {
|
||||||
|
deleteRow({ ids: [keyPair.id], secretName: keyPair?.key })
|
||||||
|
}}}
|
||||||
|
isPlain
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { faX } from '@fortawesome/free-solid-svg-icons';
|
import { faXmark } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
import SecretVersionList from '@app/ee/components/SecretVersionList';
|
import SecretVersionList from '@app/ee/components/SecretVersionList';
|
||||||
@@ -80,7 +80,7 @@ const SideBar = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="absolute border-l border-mineshaft-500 bg-bunker h-full w-96 right-0 z-40 shadow-xl flex flex-col justify-between">
|
<div className="absolute border-l border-mineshaft-500 bg-bunker h-full w-96 right-0 z-[70] shadow-xl flex flex-col justify-between">
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex items-center justify-center h-full">
|
<div className="flex items-center justify-center h-full">
|
||||||
<Image
|
<Image
|
||||||
@@ -101,19 +101,21 @@ const SideBar = ({
|
|||||||
className="p-1"
|
className="p-1"
|
||||||
onClick={() => toggleSidebar('None')}
|
onClick={() => toggleSidebar('None')}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faX} className="w-4 h-4 text-bunker-300 cursor-pointer" />
|
<FontAwesomeIcon icon={faXmark} className="w-4 h-4 text-bunker-300 cursor-pointer" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 px-4 pointer-events-none">
|
<div className="mt-4 px-4 pointer-events-none">
|
||||||
<p className="text-sm text-bunker-300">{t('dashboard:sidebar.key')}</p>
|
<p className="text-sm text-bunker-300">{t('dashboard:sidebar.key')}</p>
|
||||||
<DashboardInputField
|
<div className='rounded-md border overflow-hidden border-mineshaft-600 bg-white/5'>
|
||||||
onChangeHandler={modifyKey}
|
<DashboardInputField
|
||||||
type="varName"
|
onChangeHandler={modifyKey}
|
||||||
position={data[0]?.pos}
|
type="varName"
|
||||||
value={data[0]?.key}
|
position={data[0]?.pos}
|
||||||
isDuplicate={false}
|
value={data[0]?.key}
|
||||||
blurred={false}
|
isDuplicate={false}
|
||||||
/>
|
blurred={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{(data[0]?.value || data[0]?.value === "") ? (
|
{(data[0]?.value || data[0]?.value === "") ? (
|
||||||
<div
|
<div
|
||||||
@@ -122,14 +124,16 @@ const SideBar = ({
|
|||||||
} duration-200`}
|
} duration-200`}
|
||||||
>
|
>
|
||||||
<p className="text-sm text-bunker-300">{t('dashboard:sidebar.value')}</p>
|
<p className="text-sm text-bunker-300">{t('dashboard:sidebar.value')}</p>
|
||||||
<DashboardInputField
|
<div className='rounded-md border overflow-hidden border-mineshaft-600 bg-white/5'>
|
||||||
onChangeHandler={modifyValue}
|
<DashboardInputField
|
||||||
type="value"
|
onChangeHandler={modifyValue}
|
||||||
position={data[0].pos}
|
type="value"
|
||||||
value={data[0]?.value}
|
position={data[0].pos}
|
||||||
isDuplicate={false}
|
value={data[0]?.value}
|
||||||
blurred
|
isDuplicate={false}
|
||||||
/>
|
blurred
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="absolute bg-bunker-800 right-[1.07rem] top-[1.6rem] z-50">
|
<div className="absolute bg-bunker-800 right-[1.07rem] top-[1.6rem] z-50">
|
||||||
<GenerateSecretMenu modifyValue={modifyValue} position={data[0]?.pos} />
|
<GenerateSecretMenu modifyValue={modifyValue} position={data[0]?.pos} />
|
||||||
</div>
|
</div>
|
||||||
@@ -159,14 +163,16 @@ const SideBar = ({
|
|||||||
!overrideEnabled && 'opacity-40 pointer-events-none'
|
!overrideEnabled && 'opacity-40 pointer-events-none'
|
||||||
} duration-200`}
|
} duration-200`}
|
||||||
>
|
>
|
||||||
<DashboardInputField
|
<div className='rounded-md border overflow-hidden border-mineshaft-600 bg-white/5'>
|
||||||
onChangeHandler={modifyValueOverride}
|
<DashboardInputField
|
||||||
type="value"
|
onChangeHandler={modifyValueOverride}
|
||||||
position={data[0]?.pos}
|
type="value"
|
||||||
value={overrideEnabled ? data[0]?.valueOverride : data[0]?.value}
|
position={data[0]?.pos}
|
||||||
isDuplicate={false}
|
value={overrideEnabled ? data[0]?.valueOverride : data[0]?.value}
|
||||||
blurred
|
isDuplicate={false}
|
||||||
/>
|
blurred
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="absolute right-[0.57rem] top-[0.3rem] z-50">
|
<div className="absolute right-[0.57rem] top-[0.3rem] z-50">
|
||||||
<GenerateSecretMenu modifyValue={modifyValueOverride} position={data[0]?.pos} />
|
<GenerateSecretMenu modifyValue={modifyValueOverride} position={data[0]?.pos} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
42
frontend/src/components/v2/HoverCard/HoverCard.tsx
Normal file
42
frontend/src/components/v2/HoverCard/HoverCard.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { IconProp } from '@fortawesome/fontawesome-svg-core';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import * as HoverCard from '@radix-ui/react-hover-card';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
text: string;
|
||||||
|
icon: IconProp;
|
||||||
|
color: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type HoverCardProps = Props;
|
||||||
|
|
||||||
|
export const HoverObject = ({
|
||||||
|
text,
|
||||||
|
icon,
|
||||||
|
color
|
||||||
|
}: Props): JSX.Element => (
|
||||||
|
<HoverCard.Root openDelay={50}>
|
||||||
|
<HoverCard.Trigger asChild>
|
||||||
|
<a
|
||||||
|
className="ImageTrigger z-20"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={icon} className={`text-${color}`} />
|
||||||
|
</a>
|
||||||
|
</HoverCard.Trigger>
|
||||||
|
<HoverCard.Portal>
|
||||||
|
<HoverCard.Content className="HoverCardContent z-[50]" sideOffset={5}>
|
||||||
|
<div className='bg-bunker-700 border border-mineshaft-600 p-2 rounded-md drop-shadow-xl text-bunker-300'>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 15 }}>
|
||||||
|
<div>
|
||||||
|
<div className="Text bold">{text}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<HoverCard.Arrow className="border-mineshaft-600" />
|
||||||
|
</HoverCard.Content>
|
||||||
|
</HoverCard.Portal>
|
||||||
|
</HoverCard.Root>
|
||||||
|
);
|
||||||
|
|
||||||
|
HoverObject.displayName = 'HoverCard';
|
||||||
2
frontend/src/components/v2/HoverCard/index.tsx
Normal file
2
frontend/src/components/v2/HoverCard/index.tsx
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export type { HoverCardProps } from './HoverCard';
|
||||||
|
export { HoverObject } from './HoverCard';
|
||||||
@@ -80,7 +80,7 @@ const iconButtonVariants = cva(
|
|||||||
{
|
{
|
||||||
colorSchema: ['danger', 'primary', 'secondary'],
|
colorSchema: ['danger', 'primary', 'secondary'],
|
||||||
variant: ['plain'],
|
variant: ['plain'],
|
||||||
className: 'bg-transparent py-1 px-1'
|
className: 'bg-transparent py-1 px-1 text-bunker-300'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ export const ModalContent = forwardRef<HTMLDivElement, ModalContentProps>(
|
|||||||
({ children, title, subTitle, className, footerContent, onClose, ...props }, forwardedRef) => (
|
({ children, title, subTitle, className, footerContent, onClose, ...props }, forwardedRef) => (
|
||||||
<DialogPrimitive.Portal>
|
<DialogPrimitive.Portal>
|
||||||
<DialogPrimitive.Overlay
|
<DialogPrimitive.Overlay
|
||||||
className="fixed inset-0 h-full w-full animate-fadeIn"
|
className="fixed inset-0 h-full w-full animate-fadeIn z-[70]"
|
||||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.7)' }}
|
style={{ backgroundColor: 'rgba(0, 0, 0, 0.7)' }}
|
||||||
/>
|
/>
|
||||||
<DialogPrimitive.Content {...props} ref={forwardedRef}>
|
<DialogPrimitive.Content {...props} ref={forwardedRef}>
|
||||||
<Card
|
<Card
|
||||||
isRounded
|
isRounded
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
'fixed top-1/2 left-1/2 max-w-lg -translate-y-2/4 -translate-x-2/4 animate-popIn drop-shadow-2xl z-50',
|
'fixed top-1/2 left-1/2 max-w-lg -translate-y-2/4 -translate-x-2/4 animate-popIn drop-shadow-2xl z-[90]',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useEffect, useState } from 'react';
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { faX } from '@fortawesome/free-solid-svg-icons';
|
import { faXmark } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
import Button from '@app/components/basic/buttons/Button';
|
import Button from '@app/components/basic/buttons/Button';
|
||||||
@@ -155,7 +155,7 @@ const PITRecoverySidebar = ({ toggleSidebar, setSnapshotData, chosenSnapshot }:
|
|||||||
<div
|
<div
|
||||||
className={`absolute border-l border-mineshaft-500 ${
|
className={`absolute border-l border-mineshaft-500 ${
|
||||||
isLoading ? 'bg-bunker-800' : 'bg-bunker'
|
isLoading ? 'bg-bunker-800' : 'bg-bunker'
|
||||||
} fixed h-full w-96 top-14 right-0 z-40 shadow-xl flex flex-col justify-between`}
|
} fixed h-full w-96 right-0 z-[70] shadow-xl flex flex-col justify-between`}
|
||||||
>
|
>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex items-center justify-center h-full mb-8">
|
<div className="flex items-center justify-center h-full mb-8">
|
||||||
@@ -177,7 +177,7 @@ const PITRecoverySidebar = ({ toggleSidebar, setSnapshotData, chosenSnapshot }:
|
|||||||
className="p-1"
|
className="p-1"
|
||||||
onClick={() => toggleSidebar(false)}
|
onClick={() => toggleSidebar(false)}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faX} className="w-4 h-4 text-bunker-300 cursor-pointer" />
|
<FontAwesomeIcon icon={faXmark} className="w-4 h-4 text-bunker-300 cursor-pointer" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col px-2 py-2 overflow-y-auto h-[92vh]">
|
<div className="flex flex-col px-2 py-2 overflow-y-auto h-[92vh]">
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ const SecretVersionList = ({ secretId }: { secretId: string }) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="h-48 overflow-y-auto overflow-x-none">
|
<div className="h-48 overflow-y-auto overflow-x-none dark:[color-scheme:dark]">
|
||||||
{secretVersions ? (
|
{secretVersions ? (
|
||||||
secretVersions
|
secretVersions
|
||||||
?.sort((a, b) => b.createdAt.localeCompare(a.createdAt))
|
?.sort((a, b) => b.createdAt.localeCompare(a.createdAt))
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import Image from 'next/image';
|
|||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import {
|
import {
|
||||||
faArrowDownAZ,
|
faArrowDown,
|
||||||
faArrowDownZA,
|
|
||||||
faArrowLeft,
|
faArrowLeft,
|
||||||
|
faArrowUp,
|
||||||
faCheck,
|
faCheck,
|
||||||
faClockRotateLeft,
|
faClockRotateLeft,
|
||||||
faEye,
|
faEye,
|
||||||
@@ -31,6 +31,7 @@ import guidGenerator from '@app/components/utilities/randomId';
|
|||||||
import encryptSecrets from '@app/components/utilities/secrets/encryptSecrets';
|
import encryptSecrets from '@app/components/utilities/secrets/encryptSecrets';
|
||||||
import getSecretsForProject from '@app/components/utilities/secrets/getSecretsForProject';
|
import getSecretsForProject from '@app/components/utilities/secrets/getSecretsForProject';
|
||||||
import { getTranslatedServerSideProps } from '@app/components/utilities/withTranslateProps';
|
import { getTranslatedServerSideProps } from '@app/components/utilities/withTranslateProps';
|
||||||
|
import { IconButton } from '@app/components/v2';
|
||||||
import getProjectSercetSnapshotsCount from '@app/ee/api/secrets/GetProjectSercetSnapshotsCount';
|
import getProjectSercetSnapshotsCount from '@app/ee/api/secrets/GetProjectSercetSnapshotsCount';
|
||||||
import performSecretRollback from '@app/ee/api/secrets/PerformSecretRollback';
|
import performSecretRollback from '@app/ee/api/secrets/PerformSecretRollback';
|
||||||
import PITRecoverySidebar from '@app/ee/components/PITRecoverySidebar';
|
import PITRecoverySidebar from '@app/ee/components/PITRecoverySidebar';
|
||||||
@@ -113,7 +114,7 @@ export default function Dashboard() {
|
|||||||
const [blurred, setBlurred] = useState(true);
|
const [blurred, setBlurred] = useState(true);
|
||||||
const [isKeyAvailable, setIsKeyAvailable] = useState(true);
|
const [isKeyAvailable, setIsKeyAvailable] = useState(true);
|
||||||
const [isNew, setIsNew] = useState(false);
|
const [isNew, setIsNew] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [searchKeys, setSearchKeys] = useState('');
|
const [searchKeys, setSearchKeys] = useState('');
|
||||||
const [errorDragAndDrop, setErrorDragAndDrop] = useState(false);
|
const [errorDragAndDrop, setErrorDragAndDrop] = useState(false);
|
||||||
const [sortMethod, setSortMethod] = useState('alphabetical');
|
const [sortMethod, setSortMethod] = useState('alphabetical');
|
||||||
@@ -133,11 +134,7 @@ export default function Dashboard() {
|
|||||||
const [workspaceEnvs, setWorkspaceEnvs] = useState<WorkspaceEnv[]>([]);
|
const [workspaceEnvs, setWorkspaceEnvs] = useState<WorkspaceEnv[]>([]);
|
||||||
|
|
||||||
const [selectedSnapshotEnv, setSelectedSnapshotEnv] = useState<WorkspaceEnv>();
|
const [selectedSnapshotEnv, setSelectedSnapshotEnv] = useState<WorkspaceEnv>();
|
||||||
const [selectedEnv, setSelectedEnv] = useState<WorkspaceEnv>({
|
const [selectedEnv, setSelectedEnv] = useState<WorkspaceEnv>();
|
||||||
name: '',
|
|
||||||
slug: '',
|
|
||||||
isWriteDenied: false
|
|
||||||
});
|
|
||||||
const [atSecretAreaTop, setAtSecretsAreaTop] = useState(true);
|
const [atSecretAreaTop, setAtSecretsAreaTop] = useState(true);
|
||||||
const secretsTop = useRef<HTMLDivElement>(null);
|
const secretsTop = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@@ -193,15 +190,18 @@ export default function Dashboard() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
setData(sortedData);
|
setData(sortedData);
|
||||||
|
setIsLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reorder rows alphabetically or in the opprosite order
|
* Reorder rows alphabetically or in the opprosite order
|
||||||
*/
|
*/
|
||||||
const reorderRows = (dataToReorder: SecretDataProps[] | 1) => {
|
const reorderRows = (dataToReorder: SecretDataProps[] | 1) => {
|
||||||
setSortMethod((prevSort) => (prevSort === 'alphabetical' ? '-alphabetical' : 'alphabetical'));
|
if (dataToReorder) {
|
||||||
|
setSortMethod((prevSort) => (prevSort === 'alphabetical' ? '-alphabetical' : 'alphabetical'));
|
||||||
|
|
||||||
sortValuesHandler(dataToReorder, undefined);
|
sortValuesHandler(dataToReorder, undefined);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -246,16 +246,17 @@ export default function Dashboard() {
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setBlurred(true);
|
setBlurred(true);
|
||||||
// ENV
|
// ENV
|
||||||
const dataToSort = await getSecretsForProject({
|
let dataToSort;
|
||||||
env: selectedEnv.slug,
|
if (selectedEnv) {
|
||||||
setIsKeyAvailable,
|
dataToSort = await getSecretsForProject({
|
||||||
setData,
|
env: selectedEnv.slug,
|
||||||
workspaceId
|
setIsKeyAvailable,
|
||||||
});
|
setData,
|
||||||
setInitialData(dataToSort);
|
workspaceId
|
||||||
reorderRows(dataToSort);
|
});
|
||||||
|
setInitialData(dataToSort);
|
||||||
setTimeout(() => setIsLoading(false), 700);
|
reorderRows(dataToSort);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('Error', error);
|
console.log('Error', error);
|
||||||
setData(undefined);
|
setData(undefined);
|
||||||
@@ -283,6 +284,22 @@ export default function Dashboard() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addRowToBottom = () => {
|
||||||
|
setIsNew(false);
|
||||||
|
setData([
|
||||||
|
...data!,
|
||||||
|
{
|
||||||
|
id: guidGenerator(),
|
||||||
|
idOverride: guidGenerator(),
|
||||||
|
pos: data!.length,
|
||||||
|
key: '',
|
||||||
|
value: '',
|
||||||
|
valueOverride: undefined,
|
||||||
|
comment: ''
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
const deleteRow = ({ ids, secretName }: { ids: string[]; secretName: string }) => {
|
const deleteRow = ({ ids, secretName }: { ids: string[]; secretName: string }) => {
|
||||||
setButtonReady(true);
|
setButtonReady(true);
|
||||||
toggleSidebar('None');
|
toggleSidebar('None');
|
||||||
@@ -370,7 +387,7 @@ export default function Dashboard() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedEnv.isWriteDenied) {
|
if (selectedEnv?.isWriteDenied) {
|
||||||
setSaveLoading(false);
|
setSaveLoading(false);
|
||||||
return createNotification({
|
return createNotification({
|
||||||
text: 'You are not allowed to edit this environment',
|
text: 'You are not allowed to edit this environment',
|
||||||
@@ -473,7 +490,7 @@ export default function Dashboard() {
|
|||||||
if (secretsToBeDeleted.concat(overridesToBeDeleted).length > 0) {
|
if (secretsToBeDeleted.concat(overridesToBeDeleted).length > 0) {
|
||||||
await deleteSecrets({ secretIds: secretsToBeDeleted.concat(overridesToBeDeleted) });
|
await deleteSecrets({ secretIds: secretsToBeDeleted.concat(overridesToBeDeleted) });
|
||||||
}
|
}
|
||||||
if (secretsToBeAdded.concat(overridesToBeAdded).length > 0) {
|
if (selectedEnv && secretsToBeAdded.concat(overridesToBeAdded).length > 0) {
|
||||||
const secrets = await encryptSecrets({
|
const secrets = await encryptSecrets({
|
||||||
secretsToEncrypt: secretsToBeAdded.concat(overridesToBeAdded),
|
secretsToEncrypt: secretsToBeAdded.concat(overridesToBeAdded),
|
||||||
workspaceId,
|
workspaceId,
|
||||||
@@ -481,7 +498,7 @@ export default function Dashboard() {
|
|||||||
});
|
});
|
||||||
if (secrets) await addSecrets({ secrets, env: selectedEnv.slug, workspaceId });
|
if (secrets) await addSecrets({ secrets, env: selectedEnv.slug, workspaceId });
|
||||||
}
|
}
|
||||||
if (secretsToBeUpdated.concat(overridesToBeUpdated).length > 0) {
|
if (selectedEnv && secretsToBeUpdated.concat(overridesToBeUpdated).length > 0) {
|
||||||
const secrets = await encryptSecrets({
|
const secrets = await encryptSecrets({
|
||||||
secretsToEncrypt: secretsToBeUpdated.concat(overridesToBeUpdated),
|
secretsToEncrypt: secretsToBeUpdated.concat(overridesToBeUpdated),
|
||||||
workspaceId,
|
workspaceId,
|
||||||
@@ -518,7 +535,7 @@ export default function Dashboard() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return data ? (
|
return data ? (
|
||||||
<div className="bg-bunker-800 max-h-screen h-full relative flex flex-col justify-between text-white">
|
<div className="bg-bunker-800 max-h-screen h-full relative flex flex-col justify-between text-white dark">
|
||||||
<Head>
|
<Head>
|
||||||
<title>{t('common:head-title', { title: t('dashboard:title') })}</title>
|
<title>{t('common:head-title', { title: t('dashboard:title') })}</title>
|
||||||
<link rel="icon" href="/infisical.ico" />
|
<link rel="icon" href="/infisical.ico" />
|
||||||
@@ -526,7 +543,7 @@ export default function Dashboard() {
|
|||||||
<meta property="og:title" content={String(t('dashboard:og-title'))} />
|
<meta property="og:title" content={String(t('dashboard:og-title'))} />
|
||||||
<meta name="og:description" content={String(t('dashboard:og-description'))} />
|
<meta name="og:description" content={String(t('dashboard:og-description'))} />
|
||||||
</Head>
|
</Head>
|
||||||
<div className="flex flex-row h-full">
|
<div className="flex flex-row h-full dark:[color-scheme:dark]">
|
||||||
{sidebarSecretId !== 'None' && (
|
{sidebarSecretId !== 'None' && (
|
||||||
<SideBar
|
<SideBar
|
||||||
toggleSidebar={toggleSidebar}
|
toggleSidebar={toggleSidebar}
|
||||||
@@ -540,7 +557,7 @@ export default function Dashboard() {
|
|||||||
modifyComment={listenChangeComment}
|
modifyComment={listenChangeComment}
|
||||||
buttonReady={buttonReady}
|
buttonReady={buttonReady}
|
||||||
workspaceEnvs={workspaceEnvs}
|
workspaceEnvs={workspaceEnvs}
|
||||||
selectedEnv={selectedEnv}
|
selectedEnv={selectedEnv!}
|
||||||
workspaceId={workspaceId}
|
workspaceId={workspaceId}
|
||||||
savePush={savePush}
|
savePush={savePush}
|
||||||
sharedToHide={sharedToHide}
|
sharedToHide={sharedToHide}
|
||||||
@@ -555,7 +572,7 @@ export default function Dashboard() {
|
|||||||
setSnapshotData={setSnapshotData}
|
setSnapshotData={setSnapshotData}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className="w-full max-h-96 pb-2">
|
<div className="w-full max-h-96 pb-2 dark:[color-scheme:dark]">
|
||||||
<NavHeader pageName={t('dashboard:title')} isProjectRelated />
|
<NavHeader pageName={t('dashboard:title')} isProjectRelated />
|
||||||
{checkDocsPopUpVisible && (
|
{checkDocsPopUpVisible && (
|
||||||
<BottonRightPopup
|
<BottonRightPopup
|
||||||
@@ -568,7 +585,7 @@ export default function Dashboard() {
|
|||||||
setCheckDocsPopUpVisible={setCheckDocsPopUpVisible}
|
setCheckDocsPopUpVisible={setCheckDocsPopUpVisible}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className="flex flex-row justify-between items-center mx-6 mt-6 mb-3 text-xl max-w-5xl">
|
<div className="flex flex-row justify-between items-center mx-6 mt-6 mb-3 text-xl">
|
||||||
{snapshotData && (
|
{snapshotData && (
|
||||||
<div className="flex justify-start max-w-sm mt-1 mr-2">
|
<div className="flex justify-start max-w-sm mt-1 mr-2">
|
||||||
<Button
|
<Button
|
||||||
@@ -589,7 +606,7 @@ export default function Dashboard() {
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!snapshotData && data?.length === 0 && (
|
{!snapshotData && data?.length === 0 && selectedEnv && (
|
||||||
<ListBox
|
<ListBox
|
||||||
isSelected={selectedEnv.name}
|
isSelected={selectedEnv.name}
|
||||||
data={workspaceEnvs.map(({ name }) => name)}
|
data={workspaceEnvs.map(({ name }) => name)}
|
||||||
@@ -629,7 +646,7 @@ export default function Dashboard() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{snapshotData && (
|
{snapshotData && selectedEnv && (
|
||||||
<div className="flex justify-start max-w-sm mt-1">
|
<div className="flex justify-start max-w-sm mt-1">
|
||||||
<Button
|
<Button
|
||||||
text={String(t('Rollback to this snapshot'))}
|
text={String(t('Rollback to this snapshot'))}
|
||||||
@@ -656,6 +673,7 @@ export default function Dashboard() {
|
|||||||
text: `Rollback has been performed successfully.`,
|
text: `Rollback has been performed successfully.`,
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
|
setButtonReady(false);
|
||||||
}}
|
}}
|
||||||
color="primary"
|
color="primary"
|
||||||
size="md"
|
size="md"
|
||||||
@@ -666,9 +684,9 @@ export default function Dashboard() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx-6 w-full pr-12">
|
<div className="mx-6 w-full pr-12">
|
||||||
<div className="flex flex-col max-w-5xl pb-1">
|
<div className="flex flex-col pb-1">
|
||||||
<div className="w-full flex flex-row items-start">
|
<div className="w-full flex flex-row items-start">
|
||||||
{(snapshotData || data?.length !== 0) && (
|
{(snapshotData || data?.length !== 0) && selectedEnv && (
|
||||||
<>
|
<>
|
||||||
{!snapshotData ? (
|
{!snapshotData ? (
|
||||||
<ListBox
|
<ListBox
|
||||||
@@ -699,28 +717,18 @@ export default function Dashboard() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className="h-10 w-full bg-white/5 hover:bg-white/10 ml-2 rounded-md flex flex-row items-center">
|
<div className="h-10 w-full bg-mineshaft-700 hover:bg-white/10 ml-2 rounded-md flex flex-row items-center">
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
className="bg-white/5 rounded-l-md py-3 pl-4 pr-2 text-gray-400"
|
className="bg-transparent rounded-l-md py-[0.7rem] pl-4 pr-2 text-bunker-300 text-sm"
|
||||||
icon={faMagnifyingGlass}
|
icon={faMagnifyingGlass}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
className="pl-2 text-gray-400 rounded-r-md bg-white/5 w-full h-full outline-none"
|
className="pl-2 text-bunker-300 rounded-r-md bg-transparent w-full h-full outline-none text-sm placeholder:hover:text-bunker-200 placeholder:focus:text-transparent"
|
||||||
value={searchKeys}
|
value={searchKeys}
|
||||||
onChange={(e) => setSearchKeys(e.target.value)}
|
onChange={(e) => setSearchKeys(e.target.value)}
|
||||||
placeholder={String(t('dashboard:search-keys'))}
|
placeholder={String(t('dashboard:search-keys'))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{!snapshotData && (
|
|
||||||
<div className="ml-2 min-w-max flex flex-row items-start justify-start">
|
|
||||||
<Button
|
|
||||||
onButtonPressed={() => reorderRows(1)}
|
|
||||||
color="mineshaft"
|
|
||||||
size="icon-md"
|
|
||||||
icon={sortMethod === 'alphabetical' ? faArrowDownAZ : faArrowDownZA}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{!snapshotData && (
|
{!snapshotData && (
|
||||||
<div className="ml-2 min-w-max flex flex-row items-start justify-start">
|
<div className="ml-2 min-w-max flex flex-row items-start justify-start">
|
||||||
<DownloadSecretMenu data={data} env={selectedEnv.slug} />
|
<DownloadSecretMenu data={data} env={selectedEnv.slug} />
|
||||||
@@ -765,13 +773,30 @@ export default function Dashboard() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : data?.length !== 0 ? (
|
) : data?.length !== 0 ? (
|
||||||
<div className="flex flex-col w-full mt-1 mb-2">
|
<div className="flex flex-col w-full mt-1">
|
||||||
<div
|
<div
|
||||||
onScroll={onSecretsAreaScroll}
|
onScroll={onSecretsAreaScroll}
|
||||||
className="max-w-5xl mt-1 max-h-[calc(100vh-280px)] overflow-hidden overflow-y-scroll no-scrollbar no-scrollbar::-webkit-scrollbar"
|
className="mt-1 max-h-[calc(100vh-280px)] overflow-hidden overflow-y-scroll no-scrollbar no-scrollbar::-webkit-scrollbar border border-mineshaft-600 rounded-md"
|
||||||
>
|
>
|
||||||
<div ref={secretsTop} />
|
<div ref={secretsTop} />
|
||||||
<div className="px-1 pt-2 bg-mineshaft-800 rounded-md p-2">
|
<div className='bg-mineshaft-800 text-sm rounded-t-md h-10 w-full flex flex-row items-center border-b-2 border-mineshaft-500 sticky top-0 z-[60]'>
|
||||||
|
<div className='w-14'/>
|
||||||
|
<div className='text-bunker-300 relative font-semibold h-10 flex items-center w-80 pl-2 border-r border-mineshaft-600'>
|
||||||
|
<span>Key</span>
|
||||||
|
{!snapshotData && <IconButton
|
||||||
|
ariaLabel="copy icon"
|
||||||
|
variant="plain"
|
||||||
|
className="group relative ml-2"
|
||||||
|
onClick={() => reorderRows(1)}
|
||||||
|
>
|
||||||
|
{sortMethod === 'alphabetical' ? <FontAwesomeIcon icon={faArrowUp} /> : <FontAwesomeIcon icon={faArrowDown} />}
|
||||||
|
</IconButton>}
|
||||||
|
</div>
|
||||||
|
<div className='text-bunker-300 pl-2 font-semibold h-10 flex items-center w-full border-r border-mineshaft-600'>Value</div>
|
||||||
|
<div className='text-bunker-300 pl-2 font-semibold h-10 flex items-center w-96'>Comment</div>
|
||||||
|
{!snapshotData && <div className='w-[9.3rem]'/>}
|
||||||
|
</div>
|
||||||
|
<div className="bg-mineshaft-800 rounded-b-md border-bunker-600">
|
||||||
{!snapshotData &&
|
{!snapshotData &&
|
||||||
data
|
data
|
||||||
?.filter((row) => row.key?.toUpperCase().includes(searchKeys.toUpperCase()))
|
?.filter((row) => row.key?.toUpperCase().includes(searchKeys.toUpperCase()))
|
||||||
@@ -783,6 +808,7 @@ export default function Dashboard() {
|
|||||||
modifyValue={listenChangeValue}
|
modifyValue={listenChangeValue}
|
||||||
modifyValueOverride={listenChangeValueOverride}
|
modifyValueOverride={listenChangeValueOverride}
|
||||||
modifyKey={listenChangeKey}
|
modifyKey={listenChangeKey}
|
||||||
|
modifyComment={listenChangeComment}
|
||||||
isBlurred={blurred}
|
isBlurred={blurred}
|
||||||
isDuplicate={findDuplicates(data?.map((item) => item.key))?.includes(
|
isDuplicate={findDuplicates(data?.map((item) => item.key))?.includes(
|
||||||
keyPair.key
|
keyPair.key
|
||||||
@@ -790,6 +816,7 @@ export default function Dashboard() {
|
|||||||
toggleSidebar={toggleSidebar}
|
toggleSidebar={toggleSidebar}
|
||||||
sidebarSecretId={sidebarSecretId}
|
sidebarSecretId={sidebarSecretId}
|
||||||
isSnapshot={false}
|
isSnapshot={false}
|
||||||
|
deleteRow={deleteCertainRow}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{snapshotData &&
|
{snapshotData &&
|
||||||
@@ -820,6 +847,7 @@ export default function Dashboard() {
|
|||||||
modifyValue={listenChangeValue}
|
modifyValue={listenChangeValue}
|
||||||
modifyValueOverride={listenChangeValueOverride}
|
modifyValueOverride={listenChangeValueOverride}
|
||||||
modifyKey={listenChangeKey}
|
modifyKey={listenChangeKey}
|
||||||
|
modifyComment={listenChangeComment}
|
||||||
isBlurred={blurred}
|
isBlurred={blurred}
|
||||||
isDuplicate={findDuplicates(data?.map((item) => item.key))?.includes(
|
isDuplicate={findDuplicates(data?.map((item) => item.key))?.includes(
|
||||||
keyPair.key
|
keyPair.key
|
||||||
@@ -829,9 +857,20 @@ export default function Dashboard() {
|
|||||||
isSnapshot
|
isSnapshot
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
<div className='bg-mineshaft-800 text-sm rounded-t-md hover:bg-mineshaft-700 h-10 w-full flex flex-row items-center border-b-2 border-mineshaft-500 sticky top-0 z-[60]'>
|
||||||
|
<div className='w-10'/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className='text-bunker-300 relative font-normal h-10 flex items-center w-full cursor-pointer'
|
||||||
|
onClick={addRowToBottom}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faPlus} className='mr-3'/>
|
||||||
|
<span className='text-sm'>Add Secret</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!snapshotData && (
|
{!snapshotData && (
|
||||||
<div className="w-full max-w-5xl px-2 pt-3">
|
<div className="w-full px-2 pt-3">
|
||||||
<DropZone
|
<DropZone
|
||||||
setData={addData}
|
setData={addData}
|
||||||
setErrorDragAndDrop={setErrorDragAndDrop}
|
setErrorDragAndDrop={setErrorDragAndDrop}
|
||||||
@@ -846,7 +885,7 @@ export default function Dashboard() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col items-center justify-center h-full text-xl text-gray-400 max-w-5xl mt-28">
|
<div className="flex flex-col items-center justify-center h-full text-xl text-gray-400 mt-28">
|
||||||
{isKeyAvailable && !snapshotData && (
|
{isKeyAvailable && !snapshotData && (
|
||||||
<DropZone
|
<DropZone
|
||||||
setData={setData}
|
setData={setData}
|
||||||
|
|||||||
Reference in New Issue
Block a user