mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 19:28:09 +00:00
Fix: Format entire frontend properly
This commit is contained in:
@@ -6,7 +6,7 @@ import { ENV, POSTHOG_API_KEY, POSTHOG_HOST } from "../utilities/config";
|
|||||||
|
|
||||||
export const initPostHog = () => {
|
export const initPostHog = () => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
console.log("Hi there 👋")
|
console.log("Hi there 👋");
|
||||||
try {
|
try {
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -19,7 +19,7 @@ export const initPostHog = () => {
|
|||||||
|
|
||||||
return posthog;
|
return posthog;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("posthog err", e)
|
console.log("posthog err", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||||||
|
|
||||||
const Error = ({ text }: { text: string }): JSX.Element => {
|
const Error = ({ text }: { text: string }): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<div className="relative flex flex-row justify-center m-auto items-center w-fit rounded-full">
|
<div className="relative m-auto flex w-fit flex-row items-center justify-center rounded-full">
|
||||||
<FontAwesomeIcon icon={faExclamationTriangle} className="text-red mt-1.5 mb-2 mx-2" />
|
<FontAwesomeIcon icon={faExclamationTriangle} className="mx-2 mt-1.5 mb-2 text-red" />
|
||||||
{text && <p className="relative top-0 text-red mr-2 text-sm py-1">{text}</p>}
|
{text && <p className="relative top-0 mr-2 py-1 text-sm text-red">{text}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,16 +39,16 @@ const InputField = ({
|
|||||||
|
|
||||||
if (isStatic === true) {
|
if (isStatic === true) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col my-2 md:my-4 justify-center w-full max-w-md">
|
<div className="my-2 flex w-full max-w-md flex-col justify-center md:my-4">
|
||||||
<p className="text-sm font-semibold text-gray-400 mb-0.5">{label}</p>
|
<p className="mb-0.5 text-sm font-semibold text-gray-400">{label}</p>
|
||||||
{text && <p className="text-xs text-gray-400 mb-2">{text}</p>}
|
{text && <p className="mb-2 text-xs text-gray-400">{text}</p>}
|
||||||
<input
|
<input
|
||||||
onChange={(e) => onChangeHandler(e.target.value)}
|
onChange={(e) => onChangeHandler(e.target.value)}
|
||||||
type={type}
|
type={type}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
value={value}
|
value={value}
|
||||||
required={isRequired}
|
required={isRequired}
|
||||||
className="bg-bunker-800 text-gray-400 border border-gray-600 rounded-md text-md p-2 w-full min-w-16 outline-none"
|
className="text-md min-w-16 w-full rounded-md border border-gray-600 bg-bunker-800 p-2 text-gray-400 outline-none"
|
||||||
name={name}
|
name={name}
|
||||||
readOnly
|
readOnly
|
||||||
autoComplete={autoComplete}
|
autoComplete={autoComplete}
|
||||||
@@ -58,12 +58,12 @@ const InputField = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="flex-col w-full">
|
<div className="w-full flex-col">
|
||||||
<div className="flex flex-row text-mineshaft-300 items-center mb-0.5">
|
<div className="mb-0.5 flex flex-row items-center text-mineshaft-300">
|
||||||
<p className="text-sm font-semibold mr-1">{label}</p>
|
<p className="mr-1 text-sm font-semibold">{label}</p>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`group relative flex flex-col justify-center w-full max-w-2xl border ${
|
className={`group relative flex w-full max-w-2xl flex-col justify-center border ${
|
||||||
error ? "border-red" : "border-mineshaft-500"
|
error ? "border-red" : "border-mineshaft-500"
|
||||||
} rounded-md`}
|
} rounded-md`}
|
||||||
>
|
>
|
||||||
@@ -75,11 +75,11 @@ const InputField = ({
|
|||||||
required={isRequired}
|
required={isRequired}
|
||||||
className={`${
|
className={`${
|
||||||
blurred
|
blurred
|
||||||
? "text-bunker-800 group-hover:text-gray-400 focus:text-gray-400 active:text-gray-400"
|
? "text-bunker-800 focus:text-gray-400 active:text-gray-400 group-hover:text-gray-400"
|
||||||
: ""
|
: ""
|
||||||
} ${
|
} ${
|
||||||
error ? "focus:ring-red/50" : "focus:ring-primary/50"
|
error ? "focus:ring-red/50" : "focus:ring-primary/50"
|
||||||
} relative peer bg-mineshaft-900 rounded-md text-gray-400 text-md p-2 w-full min-w-16 outline-none focus:ring-4 duration-200`}
|
} text-md min-w-16 peer relative w-full rounded-md bg-mineshaft-900 p-2 text-gray-400 outline-none duration-200 focus:ring-4`}
|
||||||
name={name}
|
name={name}
|
||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
autoComplete={autoComplete}
|
autoComplete={autoComplete}
|
||||||
@@ -91,7 +91,7 @@ const InputField = ({
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPasswordVisible(!passwordVisible);
|
setPasswordVisible(!passwordVisible);
|
||||||
}}
|
}}
|
||||||
className="absolute self-end mr-3 text-gray-400 cursor-pointer"
|
className="absolute mr-3 cursor-pointer self-end text-gray-400"
|
||||||
>
|
>
|
||||||
{passwordVisible ? (
|
{passwordVisible ? (
|
||||||
<FontAwesomeIcon icon={faEyeSlash} />
|
<FontAwesomeIcon icon={faEyeSlash} />
|
||||||
@@ -101,7 +101,7 @@ const InputField = ({
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{blurred && (
|
{blurred && (
|
||||||
<div className="peer group-hover:hidden peer-hover:hidden peer-focus:hidden peer-active:invisible absolute h-10 w-fit max-w-xl rounded-md flex items-center text-gray-400/50 text-clip overflow-hidden">
|
<div className="peer absolute flex h-10 w-fit max-w-xl items-center overflow-hidden text-clip rounded-md text-gray-400/50 group-hover:hidden peer-hover:hidden peer-focus:hidden peer-active:invisible">
|
||||||
<p className="ml-2" />
|
<p className="ml-2" />
|
||||||
{value
|
{value
|
||||||
.split("")
|
.split("")
|
||||||
@@ -109,7 +109,7 @@ const InputField = ({
|
|||||||
.map(() => (
|
.map(() => (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
key={guidGenerator()}
|
key={guidGenerator()}
|
||||||
className="text-xxs mx-0.5"
|
className="mx-0.5 text-xxs"
|
||||||
icon={faCircle}
|
icon={faCircle}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
@@ -121,7 +121,7 @@ const InputField = ({
|
|||||||
</div>
|
</div>
|
||||||
)} */}
|
)} */}
|
||||||
</div>
|
</div>
|
||||||
{error && <p className="text-red text-xs mt-0.5 mx-0 mb-2 max-w-xs">{errorText}</p>}
|
{error && <p className="mx-0 mt-0.5 mb-2 max-w-xs text-xs text-red">{errorText}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -34,19 +34,19 @@ const ListBox = ({
|
|||||||
<Listbox value={isSelected} onChange={onChange}>
|
<Listbox value={isSelected} onChange={onChange}>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Listbox.Button
|
<Listbox.Button
|
||||||
className={`text-gray-400 relative ${
|
className={`relative text-gray-400 ${
|
||||||
isFull ? "w-full" : "w-52"
|
isFull ? "w-full" : "w-52"
|
||||||
} cursor-default rounded-md bg-white/[0.07] hover:bg-white/[0.11] duration-200 py-2.5 pl-3 pr-10 text-left shadow-md focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm`}
|
} focus-visible:ring-offset-orange-300 cursor-default rounded-md bg-white/[0.07] py-2.5 pl-3 pr-10 text-left shadow-md duration-200 hover:bg-white/[0.11] focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 sm:text-sm`}
|
||||||
>
|
>
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
{text}
|
{text}
|
||||||
<span className="ml-1 cursor-pointer block truncate font-semibold text-gray-300">
|
<span className="ml-1 block cursor-pointer truncate font-semibold text-gray-300">
|
||||||
{" "}
|
{" "}
|
||||||
{isSelected}
|
{isSelected}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{data && (
|
{data && (
|
||||||
<div className="cursor-pointer pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
<div className="pointer-events-none absolute inset-y-0 right-0 flex cursor-pointer items-center pr-2">
|
||||||
<FontAwesomeIcon icon={faAngleDown} className="text-md mr-1.5" />
|
<FontAwesomeIcon icon={faAngleDown} className="text-md mr-1.5" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -58,16 +58,16 @@ const ListBox = ({
|
|||||||
leaveFrom="opacity-100"
|
leaveFrom="opacity-100"
|
||||||
leaveTo="opacity-0"
|
leaveTo="opacity-0"
|
||||||
>
|
>
|
||||||
<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">
|
<Listbox.Options className="no-scrollbar::-webkit-scrollbar absolute z-[70] mt-1 max-h-60 w-full overflow-auto rounded-md border border-mineshaft-700 bg-bunker p-2 text-base shadow-lg ring-1 ring-black ring-opacity-5 no-scrollbar focus:outline-none sm:text-sm">
|
||||||
{data.map((person, personIdx) => (
|
{data.map((person, personIdx) => (
|
||||||
<Listbox.Option
|
<Listbox.Option
|
||||||
key={`${person}.${personIdx + 1}`}
|
key={`${person}.${personIdx + 1}`}
|
||||||
className={({ active, selected }) =>
|
className={({ active, selected }) =>
|
||||||
`my-0.5 relative cursor-default select-none py-2 pl-10 pr-4 rounded-md ${
|
`relative my-0.5 cursor-default select-none rounded-md py-2 pl-10 pr-4 ${
|
||||||
selected ? "bg-white/10 text-gray-400 font-bold" : ""
|
selected ? "bg-white/10 font-bold text-gray-400" : ""
|
||||||
} ${
|
} ${
|
||||||
active && !selected
|
active && !selected
|
||||||
? "bg-white/5 text-mineshaft-200 cursor-pointer"
|
? "cursor-pointer bg-white/5 text-mineshaft-200"
|
||||||
: "text-gray-400"
|
: "text-gray-400"
|
||||||
} `
|
} `
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ const ListBox = ({
|
|||||||
{person}
|
{person}
|
||||||
</span>
|
</span>
|
||||||
{selected ? (
|
{selected ? (
|
||||||
<span className="text-primary rounded-lg absolute inset-y-0 left-0 flex items-center pl-3">
|
<span className="absolute inset-y-0 left-0 flex items-center rounded-lg pl-3 text-primary">
|
||||||
<FontAwesomeIcon icon={faCheck} className="text-md ml-1" />
|
<FontAwesomeIcon icon={faCheck} className="text-md ml-1" />
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -92,9 +92,9 @@ const ListBox = ({
|
|||||||
</Listbox.Option>
|
</Listbox.Option>
|
||||||
))}
|
))}
|
||||||
{buttonAction && (
|
{buttonAction && (
|
||||||
<button type="button" onClick={buttonAction} className="cursor-pointer w-full">
|
<button type="button" onClick={buttonAction} className="w-full cursor-pointer">
|
||||||
<div className="my-0.5 relative flex justify-start cursor-pointer select-none py-2 pl-10 pr-4 rounded-md text-gray-400 hover:bg-lime-300 duration-200 hover:text-black hover:font-semibold mt-2">
|
<div className="relative my-0.5 mt-2 flex cursor-pointer select-none justify-start rounded-md py-2 pl-10 pr-4 text-gray-400 duration-200 hover:bg-lime-300 hover:font-semibold hover:text-black">
|
||||||
<span className="rounded-lg absolute inset-y-0 left-0 flex items-center pl-3 pr-4">
|
<span className="absolute inset-y-0 left-0 flex items-center rounded-lg pl-3 pr-4">
|
||||||
<FontAwesomeIcon icon={faPlus} className="text-lg" />
|
<FontAwesomeIcon icon={faPlus} className="text-lg" />
|
||||||
</span>
|
</span>
|
||||||
Add Project
|
Add Project
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const Button = ({
|
|||||||
loading,
|
loading,
|
||||||
icon,
|
icon,
|
||||||
iconDisabled,
|
iconDisabled,
|
||||||
type = "button",
|
type = "button"
|
||||||
}: ButtonProps): JSX.Element => {
|
}: ButtonProps): JSX.Element => {
|
||||||
// Check if the button show always be 'active' - then true;
|
// Check if the button show always be 'active' - then true;
|
||||||
// or if it should switch between 'active' and 'disabled' - then give the status
|
// or if it should switch between 'active' and 'disabled' - then give the status
|
||||||
@@ -53,9 +53,13 @@ const Button = ({
|
|||||||
"group m-auto md:m-0 inline-block rounded-md duration-200",
|
"group m-auto md:m-0 inline-block rounded-md duration-200",
|
||||||
|
|
||||||
// Setting background colors and hover modes
|
// Setting background colors and hover modes
|
||||||
color === "mineshaft" && activityStatus && "bg-mineshaft-800 border border-mineshaft-600 hover:bg-primary/[0.15] hover:border-primary/60",
|
color === "mineshaft" &&
|
||||||
|
activityStatus &&
|
||||||
|
"bg-mineshaft-800 border border-mineshaft-600 hover:bg-primary/[0.15] hover:border-primary/60",
|
||||||
color === "mineshaft" && !activityStatus && "bg-mineshaft",
|
color === "mineshaft" && !activityStatus && "bg-mineshaft",
|
||||||
(color === "primary" || !color) && activityStatus && "bg-primary border border-primary-400 opacity-80 hover:opacity-100",
|
(color === "primary" || !color) &&
|
||||||
|
activityStatus &&
|
||||||
|
"bg-primary border border-primary-400 opacity-80 hover:opacity-100",
|
||||||
(color === "primary" || !color) && !activityStatus && "bg-primary",
|
(color === "primary" || !color) && !activityStatus && "bg-primary",
|
||||||
color === "red" && "bg-red-800 border border-red",
|
color === "red" && "bg-red-800 border border-red",
|
||||||
|
|
||||||
@@ -78,7 +82,9 @@ const Button = ({
|
|||||||
color !== "mineshaft" && color !== "red" && color !== "none" && "text-black",
|
color !== "mineshaft" && color !== "red" && color !== "none" && "text-black",
|
||||||
color === "red" && "text-gray-200",
|
color === "red" && "text-gray-200",
|
||||||
color === "none" && "text-gray-200 text-xl",
|
color === "none" && "text-gray-200 text-xl",
|
||||||
activityStatus && color !== "red" && color !== "mineshaft" && color !== "none" ? "group-hover:text-black" : "",
|
activityStatus && color !== "red" && color !== "mineshaft" && color !== "none"
|
||||||
|
? "group-hover:text-black"
|
||||||
|
: "",
|
||||||
|
|
||||||
size === "icon" && "flex items-center justify-center"
|
size === "icon" && "flex items-center justify-center"
|
||||||
);
|
);
|
||||||
@@ -103,7 +109,7 @@ const Button = ({
|
|||||||
<div
|
<div
|
||||||
className={`${
|
className={`${
|
||||||
loading === true ? "opacity-100" : "opacity-0"
|
loading === true ? "opacity-100" : "opacity-0"
|
||||||
} absolute flex items-center px-3 bg-primary duration-200 w-full`}
|
} absolute flex w-full items-center bg-primary px-3 duration-200`}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src="/images/loading/loadingblack.gif"
|
src="/images/loading/loadingblack.gif"
|
||||||
@@ -116,7 +122,7 @@ const Button = ({
|
|||||||
{icon && (
|
{icon && (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={icon}
|
icon={icon}
|
||||||
className={`flex my-auto font-extrabold ${size === "icon-sm" ? "text-sm" : "text-sm"} ${
|
className={`my-auto flex font-extrabold ${size === "icon-sm" ? "text-sm" : "text-sm"} ${
|
||||||
(text || textDisabled) && "mr-2"
|
(text || textDisabled) && "mr-2"
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
@@ -124,7 +130,7 @@ const Button = ({
|
|||||||
{iconDisabled && (
|
{iconDisabled && (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={iconDisabled as IconProp}
|
icon={iconDisabled as IconProp}
|
||||||
className={`flex my-auto font-extrabold ${size === "icon-sm" ? "text-sm" : "text-md"} ${
|
className={`my-auto flex font-extrabold ${size === "icon-sm" ? "text-sm" : "text-md"} ${
|
||||||
(text || textDisabled) && "mr-2"
|
(text || textDisabled) && "mr-2"
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ const AddProjectMemberDialog = ({
|
|||||||
) : (
|
) : (
|
||||||
<Dialog.Title
|
<Dialog.Title
|
||||||
as="h3"
|
as="h3"
|
||||||
className="z-50 text-lg font-medium text-mineshaft-300 mb-4"
|
className="z-50 mb-4 text-lg font-medium text-mineshaft-300"
|
||||||
>
|
>
|
||||||
{t("section.members.add-dialog.already-all-invited")}
|
{t("section.members.add-dialog.already-all-invited")}
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
@@ -127,7 +127,9 @@ const AddProjectMemberDialog = ({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
onButtonPressed={() => router.push(`/org/${localStorage.getItem("orgData.id")}/members`)}
|
onButtonPressed={() =>
|
||||||
|
router.push(`/org/${localStorage.getItem("orgData.id")}/members`)
|
||||||
|
}
|
||||||
color="mineshaft"
|
color="mineshaft"
|
||||||
text={t("section.members.add-dialog.add-user-to-org") as string}
|
text={t("section.members.add-dialog.add-user-to-org") as string}
|
||||||
size="md"
|
size="md"
|
||||||
|
|||||||
@@ -28,11 +28,11 @@ export const AddUpdateEnvironmentDialog = ({
|
|||||||
onCreateSubmit,
|
onCreateSubmit,
|
||||||
onEditSubmit,
|
onEditSubmit,
|
||||||
initialValues,
|
initialValues,
|
||||||
isEditMode,
|
isEditMode
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [formInput, setFormInput] = useState<FormFields>({
|
const [formInput, setFormInput] = useState<FormFields>({
|
||||||
name: "",
|
name: "",
|
||||||
slug: "",
|
slug: ""
|
||||||
});
|
});
|
||||||
|
|
||||||
// This use effect can be removed when the unmount is happening from outside the component
|
// This use effect can be removed when the unmount is happening from outside the component
|
||||||
@@ -50,7 +50,7 @@ export const AddUpdateEnvironmentDialog = ({
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const data = {
|
const data = {
|
||||||
name: formInput.name,
|
name: formInput.name,
|
||||||
slug: formInput.slug.toLowerCase(),
|
slug: formInput.slug.toLowerCase()
|
||||||
};
|
};
|
||||||
if (isEditMode) {
|
if (isEditMode) {
|
||||||
onEditSubmit(data);
|
onEditSubmit(data);
|
||||||
@@ -62,75 +62,70 @@ export const AddUpdateEnvironmentDialog = ({
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Transition appear show={isOpen} as={Fragment}>
|
<Transition appear show={isOpen} as={Fragment}>
|
||||||
<Dialog as='div' className='relative z-20' onClose={onClose}>
|
<Dialog as="div" className="relative z-20" onClose={onClose}>
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
enter='ease-out duration-300'
|
enter="ease-out duration-300"
|
||||||
enterFrom='opacity-0'
|
enterFrom="opacity-0"
|
||||||
enterTo='opacity-100'
|
enterTo="opacity-100"
|
||||||
leave='ease-out duration-150'
|
leave="ease-out duration-150"
|
||||||
leaveFrom='opacity-100'
|
leaveFrom="opacity-100"
|
||||||
leaveTo='opacity-0'
|
leaveTo="opacity-0"
|
||||||
>
|
>
|
||||||
<div className='fixed inset-0 bg-black bg-opacity-70' />
|
<div className="fixed inset-0 bg-black bg-opacity-70" />
|
||||||
</Transition.Child>
|
</Transition.Child>
|
||||||
|
|
||||||
<div className='fixed inset-0 overflow-y-auto z-50'>
|
<div className="fixed inset-0 z-50 overflow-y-auto">
|
||||||
<div className='flex min-h-full items-center justify-center p-4 text-center'>
|
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
enter='ease-out duration-300'
|
enter="ease-out duration-300"
|
||||||
enterFrom='opacity-0 scale-95'
|
enterFrom="opacity-0 scale-95"
|
||||||
enterTo='opacity-100 scale-100'
|
enterTo="opacity-100 scale-100"
|
||||||
leave='ease-in duration-200'
|
leave="ease-in duration-200"
|
||||||
leaveFrom='opacity-100 scale-100'
|
leaveFrom="opacity-100 scale-100"
|
||||||
leaveTo='opacity-0 scale-95'
|
leaveTo="opacity-0 scale-95"
|
||||||
>
|
>
|
||||||
<Dialog.Panel className='w-full max-w-md transform overflow-hidden rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all'>
|
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md border border-gray-700 bg-bunker-800 p-6 text-left align-middle shadow-xl transition-all">
|
||||||
<Dialog.Title
|
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-400">
|
||||||
as='h3'
|
{isEditMode ? "Update environment" : "Create a new environment"}
|
||||||
className='text-lg font-medium leading-6 text-gray-400'
|
|
||||||
>
|
|
||||||
{isEditMode
|
|
||||||
? "Update environment"
|
|
||||||
: "Create a new environment"}
|
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
<form onSubmit={onFormSubmit}>
|
<form onSubmit={onFormSubmit}>
|
||||||
<div className='max-h-28 mt-4'>
|
<div className="mt-4 max-h-28">
|
||||||
<InputField
|
<InputField
|
||||||
label='Environment Name'
|
label="Environment Name"
|
||||||
onChangeHandler={(val) => onInputChange("name", val)}
|
onChangeHandler={(val) => onInputChange("name", val)}
|
||||||
type='varName'
|
type="varName"
|
||||||
value={formInput.name}
|
value={formInput.name}
|
||||||
placeholder=''
|
placeholder=""
|
||||||
isRequired
|
isRequired
|
||||||
// error={error.length > 0}
|
// error={error.length > 0}
|
||||||
// errorText={error}
|
// errorText={error}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='max-h-28 mt-4'>
|
<div className="mt-4 max-h-28">
|
||||||
<InputField
|
<InputField
|
||||||
label='Environment Slug'
|
label="Environment Slug"
|
||||||
onChangeHandler={(val) => onInputChange("slug", val)}
|
onChangeHandler={(val) => onInputChange("slug", val)}
|
||||||
type='varName'
|
type="varName"
|
||||||
value={formInput.slug}
|
value={formInput.slug}
|
||||||
placeholder=''
|
placeholder=""
|
||||||
isRequired
|
isRequired
|
||||||
// error={error.length > 0}
|
// error={error.length > 0}
|
||||||
// errorText={error}
|
// errorText={error}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p className='text-xs text-gray-500 mt-2'>
|
<p className="mt-2 text-xs text-gray-500">
|
||||||
Slugs are shorthands used in cli to access environment
|
Slugs are shorthands used in cli to access environment
|
||||||
</p>
|
</p>
|
||||||
<div className='mt-4 max-w-min'>
|
<div className="mt-4 max-w-min">
|
||||||
<Button
|
<Button
|
||||||
onButtonPressed={() => null}
|
onButtonPressed={() => null}
|
||||||
type='submit'
|
type="submit"
|
||||||
color='mineshaft'
|
color="mineshaft"
|
||||||
text={isEditMode ? "Update" : "Create"}
|
text={isEditMode ? "Update" : "Create"}
|
||||||
active={formInput.name !== "" && formInput.slug !== ""}
|
active={formInput.name !== "" && formInput.slug !== ""}
|
||||||
size='md'
|
size="md"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -13,76 +13,63 @@ type Props = {
|
|||||||
orgName: string;
|
orgName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AddUserDialog = ({
|
const AddUserDialog = ({ isOpen, closeModal, submitModal, email, setEmail, orgName }: Props) => {
|
||||||
isOpen,
|
|
||||||
closeModal,
|
|
||||||
submitModal,
|
|
||||||
email,
|
|
||||||
setEmail,
|
|
||||||
orgName,
|
|
||||||
}: Props) => {
|
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
submitModal(email);
|
submitModal(email);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='z-50'>
|
<div className="z-50">
|
||||||
<Transition appear show={isOpen} as={Fragment}>
|
<Transition appear show={isOpen} as={Fragment}>
|
||||||
<Dialog as='div' className='relative' onClose={closeModal}>
|
<Dialog as="div" className="relative" onClose={closeModal}>
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
enter='ease-out duration-300'
|
enter="ease-out duration-300"
|
||||||
enterFrom='opacity-0'
|
enterFrom="opacity-0"
|
||||||
enterTo='opacity-100'
|
enterTo="opacity-100"
|
||||||
leave='ease-in duration-200'
|
leave="ease-in duration-200"
|
||||||
leaveFrom='opacity-100'
|
leaveFrom="opacity-100"
|
||||||
leaveTo='opacity-0'
|
leaveTo="opacity-0"
|
||||||
>
|
>
|
||||||
<div className='fixed inset-0 bg-black bg-opacity-70' />
|
<div className="fixed inset-0 bg-black bg-opacity-70" />
|
||||||
</Transition.Child>
|
</Transition.Child>
|
||||||
|
|
||||||
<div className='fixed inset-0 overflow-y-auto'>
|
<div className="fixed inset-0 overflow-y-auto">
|
||||||
<div className='flex min-h-full items-center justify-center p-4 text-center'>
|
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
enter='ease-out duration-300'
|
enter="ease-out duration-300"
|
||||||
enterFrom='opacity-0 scale-95'
|
enterFrom="opacity-0 scale-95"
|
||||||
enterTo='opacity-100 scale-100'
|
enterTo="opacity-100 scale-100"
|
||||||
leave='ease-in duration-200'
|
leave="ease-in duration-200"
|
||||||
leaveFrom='opacity-100 scale-100'
|
leaveFrom="opacity-100 scale-100"
|
||||||
leaveTo='opacity-0 scale-95'
|
leaveTo="opacity-0 scale-95"
|
||||||
>
|
>
|
||||||
<Dialog.Panel className='w-full max-w-lg transform overflow-hidden rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all'>
|
<Dialog.Panel className="w-full max-w-lg transform overflow-hidden rounded-md border border-gray-700 bg-bunker-800 p-6 text-left align-middle shadow-xl transition-all">
|
||||||
<Dialog.Title
|
<Dialog.Title
|
||||||
as='h3'
|
as="h3"
|
||||||
className='text-lg font-medium leading-6 text-gray-400 z-50'
|
className="z-50 text-lg font-medium leading-6 text-gray-400"
|
||||||
>
|
>
|
||||||
Invite others to {orgName}
|
Invite others to {orgName}
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
<div className='mt-2 mb-4'>
|
<div className="mt-2 mb-4">
|
||||||
<p className='text-sm text-gray-500'>
|
<p className="text-sm text-gray-500">
|
||||||
An invite is specific to an email address and expires
|
An invite is specific to an email address and expires after 1 day. For
|
||||||
after 1 day. For security reasons, you will need to
|
security reasons, you will need to separately add members to projects.
|
||||||
separately add members to projects.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className='max-h-28'>
|
<div className="max-h-28">
|
||||||
<InputField
|
<InputField
|
||||||
label='Email'
|
label="Email"
|
||||||
onChangeHandler={setEmail}
|
onChangeHandler={setEmail}
|
||||||
type='varName'
|
type="varName"
|
||||||
value={email}
|
value={email}
|
||||||
placeholder=''
|
placeholder=""
|
||||||
isRequired
|
isRequired
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-4 max-w-max'>
|
<div className="mt-4 max-w-max">
|
||||||
<Button
|
<Button onButtonPressed={submit} color="mineshaft" text="Invite" size="md" />
|
||||||
onButtonPressed={submit}
|
|
||||||
color='mineshaft'
|
|
||||||
text='Invite'
|
|
||||||
size='md'
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</Dialog.Panel>
|
</Dialog.Panel>
|
||||||
{/* <Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all">
|
{/* <Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all">
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import Button from "../buttons/Button";
|
|||||||
import InputField from "../InputField";
|
import InputField from "../InputField";
|
||||||
import { Checkbox } from "../table/Checkbox";
|
import { Checkbox } from "../table/Checkbox";
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
closeModal: () => void;
|
closeModal: () => void;
|
||||||
@@ -26,8 +25,8 @@ const AddWorkspaceDialog = ({
|
|||||||
workspaceName,
|
workspaceName,
|
||||||
setWorkspaceName,
|
setWorkspaceName,
|
||||||
error,
|
error,
|
||||||
loading,
|
loading
|
||||||
}:Props) => {
|
}: Props) => {
|
||||||
const [addAllUsers, setAddAllUsers] = useState(true);
|
const [addAllUsers, setAddAllUsers] = useState(true);
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
submitModal(workspaceName, addAllUsers);
|
submitModal(workspaceName, addAllUsers);
|
||||||
@@ -60,11 +59,8 @@ const AddWorkspaceDialog = ({
|
|||||||
leaveFrom="opacity-100 scale-100"
|
leaveFrom="opacity-100 scale-100"
|
||||||
leaveTo="opacity-0 scale-95"
|
leaveTo="opacity-0 scale-95"
|
||||||
>
|
>
|
||||||
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all">
|
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md border border-gray-700 bg-bunker-800 p-6 text-left align-middle shadow-xl transition-all">
|
||||||
<Dialog.Title
|
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-400">
|
||||||
as="h3"
|
|
||||||
className="text-lg font-medium leading-6 text-gray-400"
|
|
||||||
>
|
|
||||||
Create a new project
|
Create a new project
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
@@ -72,7 +68,7 @@ const AddWorkspaceDialog = ({
|
|||||||
This project will contain your secrets and configs.
|
This project will contain your secrets and configs.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="max-h-28 mt-4">
|
<div className="mt-4 max-h-28">
|
||||||
<InputField
|
<InputField
|
||||||
label="Project Name"
|
label="Project Name"
|
||||||
onChangeHandler={setWorkspaceName}
|
onChangeHandler={setWorkspaceName}
|
||||||
@@ -84,10 +80,7 @@ const AddWorkspaceDialog = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 ml-1">
|
<div className="mt-4 ml-1">
|
||||||
<Checkbox
|
<Checkbox addAllUsers={addAllUsers} setAddAllUsers={setAddAllUsers} />
|
||||||
addAllUsers={addAllUsers}
|
|
||||||
setAddAllUsers={setAddAllUsers}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 max-w-min">
|
<div className="mt-4 max-w-min">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -6,86 +6,73 @@ import InputField from "../InputField";
|
|||||||
// REFACTOR: Move all these modals into one reusable one
|
// REFACTOR: Move all these modals into one reusable one
|
||||||
type Props = {
|
type Props = {
|
||||||
isOpen?: boolean;
|
isOpen?: boolean;
|
||||||
onClose: ()=>void;
|
onClose: () => void;
|
||||||
title: string;
|
title: string;
|
||||||
onSubmit:()=>void;
|
onSubmit: () => void;
|
||||||
deleteKey?:string;
|
deleteKey?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
const DeleteActionModal = ({
|
const DeleteActionModal = ({ isOpen, onClose, title, onSubmit, deleteKey }: Props) => {
|
||||||
isOpen,
|
const [deleteInputField, setDeleteInputField] = useState("");
|
||||||
onClose,
|
|
||||||
title,
|
|
||||||
onSubmit,
|
|
||||||
deleteKey
|
|
||||||
}:Props) => {
|
|
||||||
const [deleteInputField, setDeleteInputField] = useState("")
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDeleteInputField("");
|
setDeleteInputField("");
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Transition appear show={isOpen} as={Fragment}>
|
<Transition appear show={isOpen} as={Fragment}>
|
||||||
<Dialog as='div' className='relative z-10' onClose={onClose}>
|
<Dialog as="div" className="relative z-10" onClose={onClose}>
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
enter='ease-out duration-300'
|
enter="ease-out duration-300"
|
||||||
enterFrom='opacity-0'
|
enterFrom="opacity-0"
|
||||||
enterTo='opacity-100'
|
enterTo="opacity-100"
|
||||||
leave='ease-in duration-150'
|
leave="ease-in duration-150"
|
||||||
leaveFrom='opacity-100'
|
leaveFrom="opacity-100"
|
||||||
leaveTo='opacity-0'
|
leaveTo="opacity-0"
|
||||||
>
|
>
|
||||||
<div className='fixed inset-0 bg-black bg-opacity-70' />
|
<div className="fixed inset-0 bg-black bg-opacity-70" />
|
||||||
</Transition.Child>
|
</Transition.Child>
|
||||||
<div className='fixed inset-0 overflow-y-auto'>
|
<div className="fixed inset-0 overflow-y-auto">
|
||||||
<div className='flex min-h-full items-center justify-center p-4 text-center'>
|
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
enter='ease-out duration-300'
|
enter="ease-out duration-300"
|
||||||
enterFrom='opacity-0 scale-95'
|
enterFrom="opacity-0 scale-95"
|
||||||
enterTo='opacity-100 scale-100'
|
enterTo="opacity-100 scale-100"
|
||||||
leave='ease-in duration-200'
|
leave="ease-in duration-200"
|
||||||
leaveFrom='opacity-100 scale-100'
|
leaveFrom="opacity-100 scale-100"
|
||||||
leaveTo='opacity-0 scale-95'
|
leaveTo="opacity-0 scale-95"
|
||||||
>
|
>
|
||||||
<Dialog.Panel className='w-full max-w-md transform overflow-hidden rounded-md bg-grey border border-gray-700 p-6 text-left align-middle shadow-xl transition-all'>
|
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md border border-gray-700 bg-grey p-6 text-left align-middle shadow-xl transition-all">
|
||||||
<Dialog.Title
|
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-400">
|
||||||
as='h3'
|
|
||||||
className='text-lg font-medium leading-6 text-gray-400'
|
|
||||||
>
|
|
||||||
{title}
|
{title}
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
<div className='mt-2'>
|
<div className="mt-2">
|
||||||
<p className='text-sm text-gray-500'>
|
<p className="text-sm text-gray-500">This action is irrevertible.</p>
|
||||||
This action is irrevertible.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-2'>
|
<div className="mt-2">
|
||||||
<InputField
|
<InputField
|
||||||
isRequired
|
isRequired
|
||||||
label={`Type ${deleteKey} to delete the resource`}
|
label={`Type ${deleteKey} to delete the resource`}
|
||||||
onChangeHandler={(val) => setDeleteInputField(val)}
|
onChangeHandler={(val) => setDeleteInputField(val)}
|
||||||
value={deleteInputField}
|
value={deleteInputField}
|
||||||
type='text'
|
type="text"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-6'>
|
<div className="mt-6">
|
||||||
<button
|
<button
|
||||||
type='button'
|
type="button"
|
||||||
className='inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 hover:bg-alizarin hover:text-white hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2'
|
className="hover:bg-alizarin hover:text-semibold inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 duration-200 hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
disabled={
|
disabled={Boolean(deleteKey) && deleteInputField !== deleteKey}
|
||||||
Boolean(deleteKey) && deleteInputField !== deleteKey
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type='button'
|
type="button"
|
||||||
className='ml-2 inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 hover:border-white hover:text-white hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2'
|
className="hover:text-semibold ml-2 inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 duration-200 hover:border-white hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import { Dialog, Transition } from "@headlessui/react";
|
|||||||
// #TODO: USE THIS. Currently it's not. Kinda complicated to set up because of state.
|
// #TODO: USE THIS. Currently it's not. Kinda complicated to set up because of state.
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isOpen: boolean
|
isOpen: boolean;
|
||||||
onClose: () => void
|
onClose: () => void;
|
||||||
onSubmit: () => void
|
onSubmit: () => void;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const DeleteEnvVar = ({ isOpen, onClose, onSubmit }: Props) => {
|
export const DeleteEnvVar = ({ isOpen, onClose, onSubmit }: Props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Transition appear show={isOpen} as={Fragment}>
|
<Transition appear show={isOpen} as={Fragment}>
|
||||||
@@ -45,7 +45,7 @@ export const DeleteEnvVar = ({ isOpen, onClose, onSubmit }: Props) => {
|
|||||||
leaveFrom="opacity-100 scale-100"
|
leaveFrom="opacity-100 scale-100"
|
||||||
leaveTo="opacity-0 scale-95"
|
leaveTo="opacity-0 scale-95"
|
||||||
>
|
>
|
||||||
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md bg-bunker border border-mineshaft-600 p-6 text-left align-middle shadow-xl transition-all">
|
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md border border-mineshaft-600 bg-bunker p-6 text-left align-middle shadow-xl transition-all">
|
||||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-bunker-200">
|
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-bunker-200">
|
||||||
{t("dashboard:sidebar.delete-key-dialog.title")}
|
{t("dashboard:sidebar.delete-key-dialog.title")}
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
@@ -57,14 +57,14 @@ export const DeleteEnvVar = ({ isOpen, onClose, onSubmit }: Props) => {
|
|||||||
<div className="mt-6 flex justify-start">
|
<div className="mt-6 flex justify-start">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="inline-flex justify-center rounded-md border border-transparent bg-red-500 opacity-80 hover:opacity-100 px-4 py-2 text-sm font-medium text-bunker-100 text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
className="text-semibold inline-flex justify-center rounded-md border border-transparent bg-red-500 px-4 py-2 text-sm font-medium text-bunker-100 opacity-80 duration-200 hover:opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="ml-2 inline-flex justify-center rounded-md border border-transparent bg-bunker-500 px-4 py-2 text-sm font-medium text-gray-400 hover:bg-mineshaft-500 hover:text-white hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
className="hover:text-semibold ml-2 inline-flex justify-center rounded-md border border-transparent bg-bunker-500 px-4 py-2 text-sm font-medium text-gray-400 duration-200 hover:bg-mineshaft-500 hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
|
|||||||
@@ -10,66 +10,55 @@ type Props = {
|
|||||||
userIdToBeDeleted: string;
|
userIdToBeDeleted: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DeleteUserDialog = ({
|
const DeleteUserDialog = ({ isOpen, closeModal, submitModal, userIdToBeDeleted }: Props) => {
|
||||||
isOpen,
|
|
||||||
closeModal,
|
|
||||||
submitModal,
|
|
||||||
userIdToBeDeleted,
|
|
||||||
}: Props) => {
|
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
submitModal(userIdToBeDeleted);
|
submitModal(userIdToBeDeleted);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Transition appear show={isOpen} as={Fragment}>
|
<Transition appear show={isOpen} as={Fragment}>
|
||||||
<Dialog as='div' className='relative z-10' onClose={closeModal}>
|
<Dialog as="div" className="relative z-10" onClose={closeModal}>
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
enter='ease-out duration-300'
|
enter="ease-out duration-300"
|
||||||
enterFrom='opacity-0'
|
enterFrom="opacity-0"
|
||||||
enterTo='opacity-100'
|
enterTo="opacity-100"
|
||||||
leave='ease-in duration-200'
|
leave="ease-in duration-200"
|
||||||
leaveFrom='opacity-100'
|
leaveFrom="opacity-100"
|
||||||
leaveTo='opacity-0'
|
leaveTo="opacity-0"
|
||||||
>
|
>
|
||||||
<div className='fixed inset-0 bg-black bg-opacity-25' />
|
<div className="fixed inset-0 bg-black bg-opacity-25" />
|
||||||
</Transition.Child>
|
</Transition.Child>
|
||||||
|
|
||||||
<div className='fixed inset-0 overflow-y-auto'>
|
<div className="fixed inset-0 overflow-y-auto">
|
||||||
<div className='flex min-h-full items-center justify-center p-4 text-center'>
|
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
enter='ease-out duration-300'
|
enter="ease-out duration-300"
|
||||||
enterFrom='opacity-0 scale-95'
|
enterFrom="opacity-0 scale-95"
|
||||||
enterTo='opacity-100 scale-100'
|
enterTo="opacity-100 scale-100"
|
||||||
leave='ease-in duration-200'
|
leave="ease-in duration-200"
|
||||||
leaveFrom='opacity-100 scale-100'
|
leaveFrom="opacity-100 scale-100"
|
||||||
leaveTo='opacity-0 scale-95'
|
leaveTo="opacity-0 scale-95"
|
||||||
>
|
>
|
||||||
<Dialog.Panel className='w-full max-w-md transform overflow-hidden rounded-2xl bg-grey border border-gray-700 p-6 text-left align-middle shadow-xl transition-all'>
|
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-2xl border border-gray-700 bg-grey p-6 text-left align-middle shadow-xl transition-all">
|
||||||
<Dialog.Title
|
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-400">
|
||||||
as='h3'
|
Are you sure you want to remove this user from the workspace?
|
||||||
className='text-lg font-medium leading-6 text-gray-400'
|
|
||||||
>
|
|
||||||
Are you sure you want to remove this user from the
|
|
||||||
workspace?
|
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
<div className='mt-2'>
|
<div className="mt-2">
|
||||||
<p className='text-sm text-gray-500'>
|
<p className="text-sm text-gray-500">This action is irrevertible.</p>
|
||||||
This action is irrevertible.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-6'>
|
<div className="mt-6">
|
||||||
<button
|
<button
|
||||||
type='button'
|
type="button"
|
||||||
className='inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 hover:bg-alizarin hover:text-white hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2'
|
className="hover:bg-alizarin hover:text-semibold inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 duration-200 hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||||
onClick={submit}
|
onClick={submit}
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type='button'
|
type="button"
|
||||||
className='ml-2 inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 hover:border-white hover:text-white hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2'
|
className="hover:text-semibold ml-2 inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 duration-200 hover:border-white hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||||
onClick={submit}
|
onClick={submit}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
|
|||||||
@@ -34,27 +34,27 @@ const BottonRightPopup = ({
|
|||||||
}: PopupProps): JSX.Element => {
|
}: PopupProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="z-[100] drop-shadow-xl border-gray-600/50 border flex flex-col items-start bg-bunker max-w-xl text-gray-200 pt-3 pb-4 rounded-md absolute bottom-0 right-0 mr-6 mb-6"
|
className="absolute bottom-0 right-0 z-[100] mr-6 mb-6 flex max-w-xl flex-col items-start rounded-md border border-gray-600/50 bg-bunker pt-3 pb-4 text-gray-200 drop-shadow-xl"
|
||||||
role="alert"
|
role="alert"
|
||||||
>
|
>
|
||||||
<div className="flex flex-row items-center justify-between w-full border-b border-gray-600/70 pb-3 px-6">
|
<div className="flex w-full flex-row items-center justify-between border-b border-gray-600/70 px-6 pb-3">
|
||||||
<div className="font-bold text-xl mr-2 mt-0.5 flex flex-row">
|
<div className="mr-2 mt-0.5 flex flex-row text-xl font-bold">
|
||||||
<div>{titleText}</div>
|
<div>{titleText}</div>
|
||||||
<div className="ml-2.5">{emoji}</div>
|
<div className="ml-2.5">{emoji}</div>
|
||||||
</div>
|
</div>
|
||||||
<button className="mt-1" onClick={() => setCheckDocsPopUpVisible(false)} type="button">
|
<button className="mt-1" onClick={() => setCheckDocsPopUpVisible(false)} type="button">
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faXmark}
|
icon={faXmark}
|
||||||
className="text-gray-400 text-2xl hover:text-red duration-200 cursor-pointer"
|
className="cursor-pointer text-2xl text-gray-400 duration-200 hover:text-red"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="block sm:inline px-6 mt-4 mb-0.5 text-gray-300">{textLine1}</div>
|
<div className="mt-4 mb-0.5 block px-6 text-gray-300 sm:inline">{textLine1}</div>
|
||||||
<div className="block sm:inline mb-4 px-6">{textLine2}</div>
|
<div className="mb-4 block px-6 sm:inline">{textLine2}</div>
|
||||||
<div className="flex flex-row px-6 w-full">
|
<div className="flex w-full flex-row px-6">
|
||||||
{/* eslint-disable-next-line react/jsx-no-target-blank */}
|
{/* eslint-disable-next-line react/jsx-no-target-blank */}
|
||||||
<a
|
<a
|
||||||
className="font-bold p-2 bg-white/10 rounded-md w-full hover:bg-primary duration-200 hover:text-black flex justify-center"
|
className="flex w-full justify-center rounded-md bg-white/10 p-2 font-bold duration-200 hover:bg-primary hover:text-black"
|
||||||
href={buttonLink}
|
href={buttonLink}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener"
|
rel="noopener"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const Checkbox = ({ addAllUsers, setAddAllUsers }: Props) => (
|
|||||||
{addAllUsers === true ? (
|
{addAllUsers === true ? (
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="accent-primary h-4 w-4"
|
className="h-4 w-4 accent-primary"
|
||||||
checked
|
checked
|
||||||
readOnly
|
readOnly
|
||||||
onClick={() => setAddAllUsers(!addAllUsers)}
|
onClick={() => setAddAllUsers(!addAllUsers)}
|
||||||
@@ -20,12 +20,12 @@ export const Checkbox = ({ addAllUsers, setAddAllUsers }: Props) => (
|
|||||||
role="button"
|
role="button"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
aria-label="add all users"
|
aria-label="add all users"
|
||||||
className="h-4 w-4 bg-bunker border border-gray-600 rounded-sm"
|
className="h-4 w-4 rounded-sm border border-gray-600 bg-bunker"
|
||||||
onClick={() => setAddAllUsers(!addAllUsers)}
|
onClick={() => setAddAllUsers(!addAllUsers)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<label className="ml-2 text-gray-500 text-sm">
|
<label className="ml-2 text-sm text-gray-500">
|
||||||
Add all members of my organization to this project.
|
Add all members of my organization to this project.
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -36,25 +36,28 @@ const Notification = ({ notification, clearNotification }: NotificationProps) =>
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="relative w-full flex items-center justify-between px-6 py-4 rounded-md border border-bunker-500 pointer-events-auto bg-mineshaft-700 mb-3 right-3"
|
className="pointer-events-auto relative right-3 mb-3 flex w-full items-center justify-between rounded-md border border-bunker-500 bg-mineshaft-700 px-6 py-4"
|
||||||
role="alert"
|
role="alert"
|
||||||
>
|
>
|
||||||
{notification.type === "error" && (
|
{notification.type === "error" && (
|
||||||
<div className="absolute w-full h-1 bg-red top-0 left-0 rounded-t-md" />
|
<div className="absolute top-0 left-0 h-1 w-full rounded-t-md bg-red" />
|
||||||
)}
|
)}
|
||||||
{notification.type === "success" && (
|
{notification.type === "success" && (
|
||||||
<div className="absolute w-full h-1 bg-green top-0 left-0 rounded-t-md" />
|
<div className="absolute top-0 left-0 h-1 w-full rounded-t-md bg-green" />
|
||||||
)}
|
)}
|
||||||
{notification.type === "info" && (
|
{notification.type === "info" && (
|
||||||
<div className="absolute w-full h-1 bg-yellow top-0 left-0 rounded-t-md" />
|
<div className="absolute top-0 left-0 h-1 w-full rounded-t-md bg-yellow" />
|
||||||
)}
|
)}
|
||||||
<p className="text-bunker-200 text-md font-base mt-0.5">{notification.text}</p>
|
<p className="text-md font-base mt-0.5 text-bunker-200">{notification.text}</p>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="rounded-lg"
|
className="rounded-lg"
|
||||||
onClick={() => clearNotification(notification.text)}
|
onClick={() => clearNotification(notification.text)}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon className="absolute right-2 top-3 text-bunker-300 pl-2 w-4 h-4 hover:text-white" icon={faXmark} />
|
<FontAwesomeIcon
|
||||||
|
className="absolute right-2 top-3 h-4 w-4 pl-2 text-bunker-300 hover:text-white"
|
||||||
|
icon={faXmark}
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const Notifications = ({ notifications, clearNotification }: NoticationsProps) =
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="hidden fixed z-50 md:flex md:flex-col-reverse gap-y-2 w-96 h-full right-2 bottom-2 pointer-events-none">
|
<div className="pointer-events-none fixed right-2 bottom-2 z-50 hidden h-full w-96 gap-y-2 md:flex md:flex-col-reverse">
|
||||||
{notifications.map((notif) => (
|
{notifications.map((notif) => (
|
||||||
<Notification key={notif.text} notification={notif} clearNotification={clearNotification} />
|
<Notification key={notif.text} notification={notif} clearNotification={clearNotification} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ const ConfirmEnvOverwriteModal = ({
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<p className='text-gray-400'>Your file contains the following duplicate secrets:</p>
|
<p className="text-gray-400">Your file contains the following duplicate secrets:</p>
|
||||||
<p className="text-sm text-gray-500">{duplicateKeys.join(", ")}</p>
|
<p className="text-sm text-gray-500">{duplicateKeys.join(", ")}</p>
|
||||||
<p className='text-md text-gray-400'>Are you sure you want to overwrite these secrets?</p>
|
<p className="text-md text-gray-400">Are you sure you want to overwrite these secrets?</p>
|
||||||
</div>
|
</div>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { memo, SyntheticEvent, useRef } from "react";
|
import { memo, SyntheticEvent, useRef } from "react";
|
||||||
import { faCircle, faCodeBranch, faExclamationCircle, faEye } from "@fortawesome/free-solid-svg-icons";
|
import {
|
||||||
|
faCircle,
|
||||||
|
faCodeBranch,
|
||||||
|
faExclamationCircle,
|
||||||
|
faEye
|
||||||
|
} 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";
|
||||||
@@ -61,28 +66,30 @@ const DashboardInputField = ({
|
|||||||
const error = startsWithNumber || isDuplicate;
|
const error = startsWithNumber || isDuplicate;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`relative flex-col w-full h-10 ${
|
<div
|
||||||
error && value !== "" ? "bg-red/[0.15]" : ""
|
className={`relative h-10 w-full flex-col ${error && value !== "" ? "bg-red/[0.15]" : ""} ${
|
||||||
} ${
|
isSideBarOpen && "bg-mineshaft-700 duration-200"
|
||||||
isSideBarOpen && "bg-mineshaft-700 duration-200"
|
}`}
|
||||||
}`}>
|
>
|
||||||
<div
|
<div
|
||||||
className={`group relative flex flex-col justify-center items-center h-full ${
|
className={`group relative flex h-full flex-col items-center justify-center ${
|
||||||
error ? "w-max" : "w-full"
|
error ? "w-max" : "w-full"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
onChange={(e) => onChangeHandler(isCapitalized ? e.target.value.toUpperCase() : e.target.value, id)}
|
onChange={(e) =>
|
||||||
|
onChangeHandler(isCapitalized ? e.target.value.toUpperCase() : e.target.value, id)
|
||||||
|
}
|
||||||
type={type}
|
type={type}
|
||||||
value={value}
|
value={value}
|
||||||
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 ${
|
className={`ph-no-capture min-w-16 peer z-10 h-full w-full bg-transparent px-2 font-mono text-sm caret-bunker-200 outline-none ${
|
||||||
error ? "text-red-600 focus:text-red-500" : "text-bunker-300 focus:text-bunker-100"
|
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 && (
|
||||||
<div className='absolute right-2 top-2 text-red z-50'>
|
<div className="absolute right-2 top-2 z-50 text-red">
|
||||||
<HoverObject
|
<HoverObject
|
||||||
text="Secret names should not start with a number"
|
text="Secret names should not start with a number"
|
||||||
icon={faExclamationCircle}
|
icon={faExclamationCircle}
|
||||||
@@ -91,7 +98,7 @@ const DashboardInputField = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isDuplicate && value !== "" && !startsWithNumber && (
|
{isDuplicate && value !== "" && !startsWithNumber && (
|
||||||
<div className='absolute right-2 top-2 text-red z-50'>
|
<div className="absolute right-2 top-2 z-50 text-red">
|
||||||
<HoverObject
|
<HoverObject
|
||||||
text="Secret names should be unique"
|
text="Secret names should be unique"
|
||||||
icon={faExclamationCircle}
|
icon={faExclamationCircle}
|
||||||
@@ -99,25 +106,36 @@ const DashboardInputField = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!error && <div className={`absolute right-0 top-0 text-red z-50 bg-mineshaft-800 group-hover:bg-mineshaft-700 ${
|
{!error && (
|
||||||
overrideEnabled ? "visible" : "invisible group-hover:visible"
|
<div
|
||||||
} cursor-pointer duration-0 h-10 flex items-center px-2`}>
|
className={`absolute right-0 top-0 z-50 bg-mineshaft-800 text-red group-hover:bg-mineshaft-700 ${
|
||||||
<button type="button" onClick={() => {
|
overrideEnabled ? "visible" : "invisible group-hover:visible"
|
||||||
if (modifyValueOverride) {
|
} duration-0 flex h-10 cursor-pointer items-center px-2`}
|
||||||
if (overrideEnabled === false) {
|
>
|
||||||
modifyValueOverride("", id);
|
<button
|
||||||
} else {
|
type="button"
|
||||||
modifyValueOverride(undefined, id);
|
onClick={() => {
|
||||||
}
|
if (modifyValueOverride) {
|
||||||
}
|
if (overrideEnabled === false) {
|
||||||
}}>
|
modifyValueOverride("", id);
|
||||||
<HoverObject
|
} else {
|
||||||
text={overrideEnabled ? "This secret is overriden with your personal value" : "You can override this secret with a personal value"}
|
modifyValueOverride(undefined, id);
|
||||||
icon={faCodeBranch}
|
}
|
||||||
color={overrideEnabled ? "primary" : "bunker-400"}
|
}
|
||||||
/>
|
}}
|
||||||
</button>
|
>
|
||||||
</div>}
|
<HoverObject
|
||||||
|
text={
|
||||||
|
overrideEnabled
|
||||||
|
? "This secret is overriden with your personal value"
|
||||||
|
: "You can override this secret with a personal value"
|
||||||
|
}
|
||||||
|
icon={faCodeBranch}
|
||||||
|
color={overrideEnabled ? "primary" : "bunker-400"}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -127,20 +145,29 @@ const DashboardInputField = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PopoverObject text={value || ""} onChangeHandler={onChangeHandler} id={id}>
|
<PopoverObject text={value || ""} onChangeHandler={onChangeHandler} id={id}>
|
||||||
<div title={value} className={`relative flex-col w-full h-10 overflow-hidden ${
|
<div
|
||||||
isSideBarOpen && "bg-mineshaft-700 duration-200"
|
title={value}
|
||||||
}`}>
|
className={`relative h-10 w-full flex-col overflow-hidden ${
|
||||||
|
isSideBarOpen && "bg-mineshaft-700 duration-200"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className={`group relative flex flex-col justify-center items-center h-full ${
|
className={`group relative flex h-full flex-col items-center justify-center ${
|
||||||
error ? "w-max" : "w-full"
|
error ? "w-max" : "w-full"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{value?.split("\n")[0] ? <span className='ph-no-capture truncate break-all bg-transparent leading-tight text-xs 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'>
|
{value?.split("\n")[0] ? (
|
||||||
{value?.split("\n")[0]}
|
<span className="ph-no-capture min-w-16 placeholder w-full truncate break-all bg-transparent px-2 text-xs leading-tight text-bunker-300 outline-none duration-200 placeholder:text-bunker-400 focus:text-bunker-100 placeholder:focus:text-transparent">
|
||||||
</span> : <span className='text-bunker-400'>-</span> }
|
{value?.split("\n")[0]}
|
||||||
{value?.split("\n")[1] && <span className='ph-no-capture truncate break-all bg-transparent leading-tight text-xs 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'>
|
</span>
|
||||||
{value?.split("\n")[1]}
|
) : (
|
||||||
</span>}
|
<span className="text-bunker-400">-</span>
|
||||||
|
)}
|
||||||
|
{value?.split("\n")[1] && (
|
||||||
|
<span className="ph-no-capture min-w-16 placeholder w-full truncate break-all bg-transparent px-2 text-xs leading-tight text-bunker-300 outline-none duration-200 placeholder:text-bunker-400 focus:text-bunker-100 placeholder:focus:text-transparent">
|
||||||
|
{value?.split("\n")[1]}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</PopoverObject>
|
</PopoverObject>
|
||||||
@@ -148,10 +175,10 @@ const DashboardInputField = ({
|
|||||||
}
|
}
|
||||||
if (type === "value") {
|
if (type === "value") {
|
||||||
return (
|
return (
|
||||||
<div className="flex-col w-full">
|
<div className="w-full flex-col">
|
||||||
<div className="group relative whitespace-pre flex flex-col justify-center w-full">
|
<div className="group relative flex w-full flex-col justify-center whitespace-pre">
|
||||||
{overrideEnabled === true && (
|
{overrideEnabled === true && (
|
||||||
<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">
|
<div className="absolute top-[0.1rem] right-[0.1rem] z-0 w-min rounded-sm bg-primary-500 px-1 text-xxs text-black opacity-80">
|
||||||
Override enabled
|
Override enabled
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -160,20 +187,20 @@ const DashboardInputField = ({
|
|||||||
onChange={(e) => onChangeHandler(e.target.value, id)}
|
onChange={(e) => onChangeHandler(e.target.value, id)}
|
||||||
onScroll={syncScroll}
|
onScroll={syncScroll}
|
||||||
className={`${
|
className={`${
|
||||||
blurred
|
blurred ? "text-transparent focus:text-transparent active:text-transparent" : ""
|
||||||
? "text-transparent focus:text-transparent active:text-transparent"
|
} ph-no-capture min-w-16 no-scrollbar::-webkit-scrollbar peer z-10 w-full bg-transparent px-2 py-2 font-mono text-sm text-transparent caret-white outline-none duration-200 no-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 && !overrideEnabled
|
blurred && !overrideEnabled
|
||||||
? "text-bunker-800 group-hover:text-gray-400 peer-focus:text-gray-100 peer-active:text-gray-400 duration-200"
|
? "text-bunker-800 duration-200 group-hover:text-gray-400 peer-focus:text-gray-100 peer-active:text-gray-400"
|
||||||
: ""
|
: ""
|
||||||
} ${overrideEnabled ? "text-primary-300" : "text-gray-400"}
|
} ${overrideEnabled ? "text-primary-300" : "text-gray-400"}
|
||||||
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`}
|
absolute z-0 flex flex-row whitespace-pre font-mono ${
|
||||||
|
blurred ? "invisible" : "visible"
|
||||||
|
} ph-no-capture min-w-16 no-scrollbar::-webkit-scrollbar mt-0.5 h-10 w-full overflow-x-scroll bg-transparent px-2 py-2 text-sm outline-none duration-100 no-scrollbar peer-focus:visible`}
|
||||||
>
|
>
|
||||||
{value?.split(REGEX).map((word) => {
|
{value?.split(REGEX).map((word) => {
|
||||||
if (word.match(REGEX) !== null) {
|
if (word.match(REGEX) !== null) {
|
||||||
@@ -203,20 +230,24 @@ const DashboardInputField = ({
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
{blurred && (
|
{blurred && (
|
||||||
<div className={`absolute flex flex-row justify-between items-center z-0 peer pr-2 ${
|
<div
|
||||||
isSideBarOpen ? "bg-mineshaft-700 duration-200" : "bg-mineshaft-800"
|
className={`peer absolute z-0 flex flex-row items-center justify-between 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`}>
|
isSideBarOpen ? "bg-mineshaft-700 duration-200" : "bg-mineshaft-800"
|
||||||
<div className="px-2 flex flex-row items-center overflow-x-scroll no-scrollbar no-scrollbar::-webkit-scrollbar">
|
} h-10 w-full text-clip text-bunker-400 duration-100 group-hover:bg-white/[0.00] peer-focus:hidden peer-active:hidden`}
|
||||||
|
>
|
||||||
|
<div className="no-scrollbar::-webkit-scrollbar flex flex-row items-center overflow-x-scroll px-2 no-scrollbar">
|
||||||
{value?.split("").map(() => (
|
{value?.split("").map(() => (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
key={guidGenerator()}
|
key={guidGenerator()}
|
||||||
className="text-xxs mr-0.5"
|
className="mr-0.5 text-xxs"
|
||||||
icon={faCircle}
|
icon={faCircle}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{value?.split("").length === 0 && <span className='text-bunker-400/80'>EMPTY</span>}
|
{value?.split("").length === 0 && <span className="text-bunker-400/80">EMPTY</span>}
|
||||||
|
</div>
|
||||||
|
<div className="invisible z-[100] cursor-default group-hover:visible">
|
||||||
|
<FontAwesomeIcon icon={faEye} />
|
||||||
</div>
|
</div>
|
||||||
<div className='invisible group-hover:visible cursor-default z-[100]'><FontAwesomeIcon icon={faEye} /></div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from "react"
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { faXmark } 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";
|
||||||
@@ -8,32 +8,35 @@ import Button from "../basic/buttons/Button";
|
|||||||
type Props = {
|
type Props = {
|
||||||
onSubmit: () => void;
|
onSubmit: () => void;
|
||||||
isPlain?: boolean;
|
isPlain?: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const DeleteActionButton = ({ onSubmit, isPlain }: Props) => {
|
export const DeleteActionButton = ({ onSubmit, isPlain }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${
|
<div
|
||||||
!isPlain
|
className={`${
|
||||||
? "bg-[#9B3535] opacity-70 hover:opacity-100 w-[4.5rem] h-[2.5rem] rounded-md duration-200 ml-2"
|
!isPlain
|
||||||
: "cursor-pointer w-[1.5rem] h-[2.35rem] mr-2 flex items-center justfy-center"}`}>
|
? "ml-2 h-[2.5rem] w-[4.5rem] rounded-md bg-[#9B3535] opacity-70 duration-200 hover:opacity-100"
|
||||||
{isPlain
|
: "justfy-center mr-2 flex h-[2.35rem] w-[1.5rem] cursor-pointer items-center"
|
||||||
? <div
|
}`}
|
||||||
onKeyDown={() => null}
|
>
|
||||||
role="button"
|
{isPlain ? (
|
||||||
tabIndex={0}
|
<div
|
||||||
onClick={onSubmit}
|
onKeyDown={() => null}
|
||||||
className="invisible group-hover:visible"
|
role="button"
|
||||||
>
|
tabIndex={0}
|
||||||
<FontAwesomeIcon className="text-bunker-300 hover:text-red pl-2 pr-6 text-lg mt-0.5" icon={faXmark} />
|
onClick={onSubmit}
|
||||||
</div>
|
className="invisible group-hover:visible"
|
||||||
: <Button
|
>
|
||||||
text={String(t("Delete"))}
|
<FontAwesomeIcon
|
||||||
color="red"
|
className="mt-0.5 pl-2 pr-6 text-lg text-bunker-300 hover:text-red"
|
||||||
size="md"
|
icon={faXmark}
|
||||||
onButtonPressed={onSubmit}
|
/>
|
||||||
/>}
|
</div>
|
||||||
|
) : (
|
||||||
|
<Button text={String(t("Delete"))} color="red" size="md" onButtonPressed={onSubmit} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const DownloadSecretMenu = ({ data, env }: { data: SecretDataProps[]; env: strin
|
|||||||
<Menu as="div" className="relative inline-block text-left">
|
<Menu as="div" className="relative inline-block text-left">
|
||||||
<Menu.Button
|
<Menu.Button
|
||||||
as="div"
|
as="div"
|
||||||
className="inline-flex w-full justify-center text-sm font-medium text-gray-200 rounded-md hover:bg-white/10 duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75"
|
className="inline-flex w-full justify-center rounded-md text-sm font-medium text-gray-200 duration-200 hover:bg-white/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75"
|
||||||
>
|
>
|
||||||
<Button color="mineshaft" size="icon-md" icon={faDownload} onButtonPressed={() => {}} />
|
<Button color="mineshaft" size="icon-md" icon={faDownload} onButtonPressed={() => {}} />
|
||||||
</Menu.Button>
|
</Menu.Button>
|
||||||
@@ -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-[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.Items className="absolute right-0 z-[90] mt-0.5 w-[12rem] origin-top-right space-y-2 rounded-md border border-mineshaft-500 bg-bunker p-2 shadow-lg ring-1 ring-black ring-opacity-5 drop-shadow-xl focus:outline-none">
|
||||||
<Menu.Item>
|
<Menu.Item>
|
||||||
<Button
|
<Button
|
||||||
color="mineshaft"
|
color="mineshaft"
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export default function NavHeader({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-row items-center pt-6">
|
<div className="flex flex-row items-center pt-6">
|
||||||
<div className="mr-2 flex h-5 w-5 items-center justify-center rounded-md bg-primary text-sm text-black min-w-[1.25rem]">
|
<div className="mr-2 flex h-5 w-5 min-w-[1.25rem] items-center justify-center rounded-md bg-primary text-sm text-black">
|
||||||
{currentOrg?.name?.charAt(0)}
|
{currentOrg?.name?.charAt(0)}
|
||||||
</div>
|
</div>
|
||||||
<Link passHref legacyBehavior href={`/org/${currentOrg?.id}/overview`}>
|
<Link passHref legacyBehavior href={`/org/${currentOrg?.id}/overview`}>
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { useOrganization, useWorkspace } from "@app/context";
|
|||||||
|
|
||||||
import { Select, SelectItem, Tooltip } from "../v2";
|
import { Select, SelectItem, Tooltip } from "../v2";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the component at the top of almost every page.
|
* This is the component at the top of almost every page.
|
||||||
* It shows how to navigate to a certain page.
|
* It shows how to navigate to a certain page.
|
||||||
@@ -39,10 +38,12 @@ export default function NavHeaderSecrets({
|
|||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const router = useRouter()
|
const router = useRouter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${!isSnapshot && "absolute"} ml-6 flex flex-row items-center pt-6 cursor-default`}>
|
<div
|
||||||
|
className={`${!isSnapshot && "absolute"} ml-6 flex cursor-default flex-row items-center pt-6`}
|
||||||
|
>
|
||||||
<div className="mr-3 flex h-6 w-6 items-center justify-center rounded-md bg-primary-900 text-mineshaft-100">
|
<div className="mr-3 flex h-6 w-6 items-center justify-center rounded-md bg-primary-900 text-mineshaft-100">
|
||||||
{currentOrg?.name?.charAt(0)}
|
{currentOrg?.name?.charAt(0)}
|
||||||
</div>
|
</div>
|
||||||
@@ -60,31 +61,39 @@ export default function NavHeaderSecrets({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-3 text-sm text-gray-400" />
|
<FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-3 text-sm text-gray-400" />
|
||||||
{pageName === "Secrets"
|
{pageName === "Secrets" ? (
|
||||||
? <a className="text-md font-medium text-primary/80 hover:text-primary" href={`${router.asPath.split("?")[0]}`}>{pageName}</a>
|
<a
|
||||||
: <div className="text-md text-gray-400">{pageName}</div>}
|
className="text-md font-medium text-primary/80 hover:text-primary"
|
||||||
{currentEnv &&
|
href={`${router.asPath.split("?")[0]}`}
|
||||||
<>
|
>
|
||||||
<FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-1.5 text-sm text-gray-400" />
|
{pageName}
|
||||||
<div className='pl-3 rounded-md hover:bg-bunker-100/10'>
|
</a>
|
||||||
<Tooltip content="Select environment">
|
) : (
|
||||||
<Select
|
<div className="text-md text-gray-400">{pageName}</div>
|
||||||
value={userAvailableEnvs?.filter(uae => uae.name === currentEnv)[0]?.slug}
|
)}
|
||||||
onValueChange={(value) => {
|
{currentEnv && (
|
||||||
if (value && onEnvChange) onEnvChange(value);
|
<>
|
||||||
}}
|
<FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-1.5 text-sm text-gray-400" />
|
||||||
className="text-md pl-0 font-medium text-primary/80 hover:text-primary bg-transparent"
|
<div className="rounded-md pl-3 hover:bg-bunker-100/10">
|
||||||
dropdownContainerClassName="text-bunker-200 bg-mineshaft-800 border border-mineshaft-600 drop-shadow-2xl"
|
<Tooltip content="Select environment">
|
||||||
>
|
<Select
|
||||||
{userAvailableEnvs?.map(({ name, slug }) => (
|
value={userAvailableEnvs?.filter((uae) => uae.name === currentEnv)[0]?.slug}
|
||||||
<SelectItem value={slug} key={slug}>
|
onValueChange={(value) => {
|
||||||
{name}
|
if (value && onEnvChange) onEnvChange(value);
|
||||||
</SelectItem>
|
}}
|
||||||
))}
|
className="text-md bg-transparent pl-0 font-medium text-primary/80 hover:text-primary"
|
||||||
</Select>
|
dropdownContainerClassName="text-bunker-200 bg-mineshaft-800 border border-mineshaft-600 drop-shadow-2xl"
|
||||||
</Tooltip>
|
>
|
||||||
</div>
|
{userAvailableEnvs?.map(({ name, slug }) => (
|
||||||
</>}
|
<SelectItem value={slug} key={slug}>
|
||||||
|
{name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ import React, { useState } from "react";
|
|||||||
import ReactCodeInput from "react-code-input";
|
import ReactCodeInput from "react-code-input";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import {
|
import { useSendVerificationEmail } from "@app/hooks/api";
|
||||||
useSendVerificationEmail
|
|
||||||
} from "@app/hooks/api";
|
|
||||||
|
|
||||||
import Error from "../basic/Error";
|
import Error from "../basic/Error";
|
||||||
import { Button } from "../v2";
|
import { Button } from "../v2";
|
||||||
@@ -90,8 +88,8 @@ export default function CodeInputStep({
|
|||||||
return (
|
return (
|
||||||
<div className="mx-auto h-full w-full pb-4 md:px-8">
|
<div className="mx-auto h-full w-full pb-4 md:px-8">
|
||||||
<p className="text-md flex justify-center text-bunker-200">{t("signup.step2-message")}</p>
|
<p className="text-md flex justify-center text-bunker-200">{t("signup.step2-message")}</p>
|
||||||
<p className="text-md flex justify-center font-semibold my-1 text-bunker-200">{email} </p>
|
<p className="text-md my-1 flex justify-center font-semibold text-bunker-200">{email} </p>
|
||||||
<div className="hidden md:block w-max min-w-[20rem] mx-auto">
|
<div className="mx-auto hidden w-max min-w-[20rem] md:block">
|
||||||
<ReactCodeInput
|
<ReactCodeInput
|
||||||
name=""
|
name=""
|
||||||
inputMode="tel"
|
inputMode="tel"
|
||||||
@@ -102,7 +100,7 @@ export default function CodeInputStep({
|
|||||||
className="mt-6 mb-2"
|
className="mt-6 mb-2"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="block md:hidden w-max mt-4 mx-auto">
|
<div className="mx-auto mt-4 block w-max md:hidden">
|
||||||
<ReactCodeInput
|
<ReactCodeInput
|
||||||
name=""
|
name=""
|
||||||
inputMode="tel"
|
inputMode="tel"
|
||||||
@@ -114,26 +112,29 @@ export default function CodeInputStep({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{codeError && <Error text={t("signup.step2-code-error")} />}
|
{codeError && <Error text={t("signup.step2-code-error")} />}
|
||||||
<div className="flex flex-col items-center justify-center lg:w-[19%] w-1/4 min-w-[20rem] mt-2 max-w-xs md:max-w-md mx-auto text-sm text-center md:text-left">
|
<div className="mx-auto mt-2 flex w-1/4 min-w-[20rem] max-w-xs flex-col items-center justify-center text-center text-sm md:max-w-md md:text-left lg:w-[19%]">
|
||||||
<div className="text-l py-1 text-lg w-full">
|
<div className="text-l w-full py-1 text-lg">
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
onClick={incrementStep}
|
onClick={incrementStep}
|
||||||
size="sm"
|
size="sm"
|
||||||
isFullWidth
|
isFullWidth
|
||||||
className='h-14'
|
className="h-14"
|
||||||
colorSchema="primary"
|
colorSchema="primary"
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
isLoading={isCodeInputCheckLoading}
|
isLoading={isCodeInputCheckLoading}
|
||||||
> {String(t("signup.verify"))} </Button>
|
>
|
||||||
|
{" "}
|
||||||
|
{String(t("signup.verify"))}{" "}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center justify-center w-full max-h-24 max-w-md mx-auto pt-2">
|
<div className="mx-auto flex max-h-24 w-full max-w-md flex-col items-center justify-center pt-2">
|
||||||
<div className="flex flex-row items-baseline gap-1 text-sm">
|
<div className="flex flex-row items-baseline gap-1 text-sm">
|
||||||
<span className="text-bunker-400">{t("signup.step2-resend-alert")}</span>
|
<span className="text-bunker-400">{t("signup.step2-resend-alert")}</span>
|
||||||
<div className="mt-2 text-bunker-400 text-md flex flex-row">
|
<div className="text-md mt-2 flex flex-row text-bunker-400">
|
||||||
<button disabled={isLoading} onClick={resendVerificationEmail} type="button">
|
<button disabled={isLoading} onClick={resendVerificationEmail} type="button">
|
||||||
<span className='hover:underline hover:underline-offset-4 hover:decoration-primary-700 hover:text-bunker-200 duration-200 cursor-pointer'>
|
<span className="cursor-pointer duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4">
|
||||||
{isResendingVerificationEmail
|
{isResendingVerificationEmail
|
||||||
? t("signup.step2-resend-progress")
|
? t("signup.step2-resend-progress")
|
||||||
: t("signup.step2-resend-submit")}
|
: t("signup.step2-resend-submit")}
|
||||||
@@ -141,7 +142,7 @@ export default function CodeInputStep({
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-bunker-400 pb-2">{t("signup.step2-spam-alert")}</p>
|
<p className="pb-2 text-sm text-bunker-400">{t("signup.step2-spam-alert")}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -57,19 +57,22 @@ export default function DonwloadBackupPDFStep({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center w-full h-full md:px-6 mx-auto mb-36 md:mb-16">
|
<div className="mx-auto mb-36 flex h-full w-full flex-col items-center md:mb-16 md:px-6">
|
||||||
<p className="text-xl text-center font-medium flex flex-col justify-center items-center text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200">
|
<p className="flex flex-col items-center justify-center bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-center text-xl font-medium text-transparent">
|
||||||
<FontAwesomeIcon icon={faWarning} className="ml-2 mr-3 pt-1 mb-6 text-6xl text-bunker-200" />
|
<FontAwesomeIcon
|
||||||
|
icon={faWarning}
|
||||||
|
className="ml-2 mr-3 mb-6 pt-1 text-6xl text-bunker-200"
|
||||||
|
/>
|
||||||
{t("signup.step4-message")}
|
{t("signup.step4-message")}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-col pb-2 bg-mineshaft-800 border border-mineshaft-600 items-center justify-center text-center lg:w-1/6 w-full md:min-w-[24rem] mt-8 max-w-md text-bunker-300 text-md rounded-md">
|
<div className="text-md mt-8 flex w-full max-w-md flex-col items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-800 pb-2 text-center text-bunker-300 md:min-w-[24rem] lg:w-1/6">
|
||||||
<div className="w-full mt-4 md:mt-8 flex flex-row text-center items-center m-2 text-bunker-300 rounded-md lg:w-1/6 lg:w-1/6 w-full md:min-w-[23rem] px-3 mx-auto">
|
<div className="m-2 mx-auto mt-4 flex w-full w-full flex-row items-center rounded-md px-3 text-center text-bunker-300 md:mt-8 md:min-w-[23rem] lg:w-1/6 lg:w-1/6">
|
||||||
<span className="mb-2">
|
<span className="mb-2">
|
||||||
{t("signup.step4-description1")} {t("signup.step4-description3")}
|
{t("signup.step4-description1")} {t("signup.step4-description3")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center px-3 justify-center mt-0 md:mt-4 mb-2 md:mb-4 lg:w-1/6 w-full md:min-w-[20rem] mt-2 md:max-w-md mx-auto text-sm text-center md:text-left">
|
<div className="mx-auto mt-0 mb-2 mt-2 flex w-full flex-col items-center justify-center px-3 text-center text-sm md:mt-4 md:mb-4 md:min-w-[20rem] md:max-w-md md:text-left lg:w-1/6">
|
||||||
<div className="text-l py-1 text-lg w-full">
|
<div className="text-l w-full py-1 text-lg">
|
||||||
<Button
|
<Button
|
||||||
onClick={handleBackupKeyGenerate}
|
onClick={handleBackupKeyGenerate}
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@@ -52,13 +52,13 @@ export default function EnterEmailStep({
|
|||||||
try {
|
try {
|
||||||
await mutateAsync({ email });
|
await mutateAsync({ email });
|
||||||
incrementStep();
|
incrementStep();
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
if (axios.isAxiosError(e)) {
|
if (axios.isAxiosError(e)) {
|
||||||
const { message = "Something went wrong" } = e.response?.data as { message: string};
|
const { message = "Something went wrong" } = e.response?.data as { message: string };
|
||||||
createNotification({
|
createNotification({
|
||||||
type: "error",
|
type: "error",
|
||||||
text: message
|
text: message
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,11 +66,11 @@ export default function EnterEmailStep({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="w-full md:px-6 mx-auto">
|
<div className="mx-auto w-full md:px-6">
|
||||||
<p className="text-xl font-medium flex justify-center text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200">
|
<p className="flex justify-center bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-xl font-medium text-transparent">
|
||||||
{t("signup.step1-start")}
|
{t("signup.step1-start")}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-col items-center justify-center lg:w-1/6 w-1/4 min-w-[20rem] m-auto rounded-lg mt-8">
|
<div className="m-auto mt-8 flex w-1/4 min-w-[20rem] flex-col items-center justify-center rounded-lg lg:w-1/6">
|
||||||
<Input
|
<Input
|
||||||
placeholder="Enter your email address..."
|
placeholder="Enter your email address..."
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
@@ -79,28 +79,35 @@ export default function EnterEmailStep({
|
|||||||
autoComplete="username"
|
autoComplete="username"
|
||||||
className="h-12"
|
className="h-12"
|
||||||
/>
|
/>
|
||||||
{emailError && <p className="text-red-600 text-xs text-left w-full ml-1.5 mt-1.5">Please enter a valid email.</p>}
|
{emailError && (
|
||||||
|
<p className="ml-1.5 mt-1.5 w-full text-left text-xs text-red-600">
|
||||||
|
Please enter a valid email.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center justify-center lg:w-1/6 w-1/4 min-w-[20rem] mt-2 max-w-xs md:max-w-md mx-auto text-sm text-center md:text-left">
|
<div className="mx-auto mt-2 flex w-1/4 min-w-[20rem] max-w-xs flex-col items-center justify-center text-center text-sm md:max-w-md md:text-left lg:w-1/6">
|
||||||
<div className="text-l py-1 text-lg w-full">
|
<div className="text-l w-full py-1 text-lg">
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
onClick={emailCheck}
|
onClick={emailCheck}
|
||||||
size="sm"
|
size="sm"
|
||||||
isFullWidth
|
isFullWidth
|
||||||
className='h-14'
|
className="h-14"
|
||||||
colorSchema="primary"
|
colorSchema="primary"
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
isDisabled={isLoading}
|
isDisabled={isLoading}
|
||||||
> {String(t("signup.step1-submit"))} </Button>
|
>
|
||||||
|
{" "}
|
||||||
|
{String(t("signup.step1-submit"))}{" "}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx-auto mb-48 mt-2 flex w-full max-w-md flex-col items-center justify-center pt-2 md:mb-16 md:pb-2">
|
<div className="mx-auto mb-48 mt-2 flex w-full max-w-md flex-col items-center justify-center pt-2 md:mb-16 md:pb-2">
|
||||||
<Link href="/login">
|
<Link href="/login">
|
||||||
<button type="button" className="w-max pb-3 duration-200 hover:opacity-90">
|
<button type="button" className="w-max pb-3 duration-200 hover:opacity-90">
|
||||||
<span className="text-sm text-mineshaft-400 hover:underline hover:underline-offset-4 hover:decoration-primary-700 hover:text-bunker-200 duration-200 cursor-pointer">
|
<span className="cursor-pointer text-sm text-mineshaft-400 duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4">
|
||||||
{t("signup.already-have-account")}
|
{t("signup.already-have-account")}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -40,55 +40,61 @@ export default function TeamInviteStep(): JSX.Element {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-max mx-auto min-w-lg h-full pb-4 px-8 mb-64 md:mb-32">
|
<div className="min-w-lg mx-auto mb-64 h-full w-max px-8 pb-4 md:mb-32">
|
||||||
<p className="text-2xl font-semibold flex justify-center text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200">
|
<p className="flex justify-center bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-2xl font-semibold text-transparent">
|
||||||
{t("signup.step5-invite-team")}
|
{t("signup.step5-invite-team")}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-center flex justify-center text-bunker-400 md:mx-8 mb-6 mt-4">
|
<p className="mb-6 mt-4 flex justify-center text-center text-bunker-400 md:mx-8">
|
||||||
{t("signup.step5-subtitle")}
|
{t("signup.step5-subtitle")}
|
||||||
</p>
|
</p>
|
||||||
<div className="bg-mineshaft-800 border border-mineshaft-500 w-max mx-auto pt-6 pb-4 px-8 rounded-xl drop-shadow-xl mb-6">
|
<div className="mx-auto mb-6 w-max rounded-xl border border-mineshaft-500 bg-mineshaft-800 px-8 pt-6 pb-4 drop-shadow-xl">
|
||||||
<div>
|
<div>
|
||||||
<div className="text-bunker-300 font-medium pl-1 pb-1 text-sm">
|
<div className="pl-1 pb-1 text-sm font-medium text-bunker-300">
|
||||||
<span>Emails</span>
|
<span>Emails</span>
|
||||||
</div>
|
</div>
|
||||||
<textarea
|
<textarea
|
||||||
className="bg-mineshaft-900/70 min-w-[30rem] h-20 w-full placeholder:text-bunker-400 py-1 px-2 rounded-md border border-mineshaft-500 text-sm text-bunker-300 outline-none focus:ring-2 ring-primary-800 ring-opacity-70"
|
className="h-20 w-full min-w-[30rem] rounded-md border border-mineshaft-500 bg-mineshaft-900/70 py-1 px-2 text-sm text-bunker-300 outline-none ring-primary-800 ring-opacity-70 placeholder:text-bunker-400 focus:ring-2"
|
||||||
value={emails}
|
value={emails}
|
||||||
onChange={(e) => setEmails(e.target.value)}
|
onChange={(e) => setEmails(e.target.value)}
|
||||||
placeholder="[email protected], [email protected]..."
|
placeholder="[email protected], [email protected]..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-end justify-end mt-0 md:mt-4 md:mb-2 w-full md:min-w-[30rem] mt-2 md:max-w-md mx-auto text-sm">
|
<div className="mx-auto mt-0 mt-2 flex w-full flex-row items-end justify-end text-sm md:mt-4 md:mb-2 md:min-w-[30rem] md:max-w-md">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (serverDetails?.emailConfigured) {
|
if (serverDetails?.emailConfigured) {
|
||||||
inviteUsers({ emails })
|
inviteUsers({ emails });
|
||||||
} else {
|
} else {
|
||||||
handlePopUpOpen("setUpEmail");
|
handlePopUpOpen("setUpEmail");
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
size="sm"
|
size="sm"
|
||||||
// isFullWidth
|
// isFullWidth
|
||||||
className='h-10'
|
className="h-10"
|
||||||
colorSchema="primary"
|
colorSchema="primary"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
> {t("signup.step5-send-invites") ?? ""} </Button>
|
>
|
||||||
|
{" "}
|
||||||
|
{t("signup.step5-send-invites") ?? ""}{" "}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<EmailServiceSetupModal
|
<EmailServiceSetupModal
|
||||||
isOpen={popUp.setUpEmail?.isOpen}
|
isOpen={popUp.setUpEmail?.isOpen}
|
||||||
onOpenChange={(isOpen) => handlePopUpToggle("setUpEmail", isOpen)}
|
onOpenChange={(isOpen) => handlePopUpToggle("setUpEmail", isOpen)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row max-w-max min-w-28 items-center justify-center md:p-2 min-w-[20rem] max-h-24 mx-auto text-lg px-4 mt-4 mb-2">
|
<div className="min-w-28 mx-auto mt-4 mb-2 flex max-h-24 min-w-[20rem] max-w-max flex-row items-center justify-center px-4 text-lg md:p-2">
|
||||||
<Button
|
<Button
|
||||||
onClick={redirectToHome}
|
onClick={redirectToHome}
|
||||||
size="sm"
|
size="sm"
|
||||||
isFullWidth
|
isFullWidth
|
||||||
className='h-12'
|
className="h-12"
|
||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
> {t("signup.step5-skip") ?? "Skip"} </Button>
|
>
|
||||||
|
{" "}
|
||||||
|
{t("signup.step5-skip") ?? "Skip"}{" "}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
import {
|
import { getAuthToken, setAuthToken, setMfaTempToken, setSignupTempToken } from "@app/reactQuery";
|
||||||
getAuthToken,
|
|
||||||
setAuthToken,
|
|
||||||
setMfaTempToken,
|
|
||||||
setSignupTempToken} from "@app/reactQuery";
|
|
||||||
|
|
||||||
|
|
||||||
export const PROVIDER_AUTH_TOKEN_KEY = "infisical__provider-auth-token";
|
export const PROVIDER_AUTH_TOKEN_KEY = "infisical__provider-auth-token";
|
||||||
|
|
||||||
// depreciated: go for apiRequest module in config/api
|
// depreciated: go for apiRequest module in config/api
|
||||||
export default class SecurityClient {
|
export default class SecurityClient {
|
||||||
|
|
||||||
static setProviderAuthToken(tokenStr: string) {
|
static setProviderAuthToken(tokenStr: string) {
|
||||||
localStorage.setItem(PROVIDER_AUTH_TOKEN_KEY, tokenStr || "")
|
localStorage.setItem(PROVIDER_AUTH_TOKEN_KEY, tokenStr || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
static getProviderAuthToken() {
|
static getProviderAuthToken() {
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ import crypto from "crypto";
|
|||||||
|
|
||||||
import jsrp from "jsrp";
|
import jsrp from "jsrp";
|
||||||
|
|
||||||
import {
|
import { changePassword, srp1 } from "@app/hooks/api/auth/queries";
|
||||||
changePassword,
|
|
||||||
srp1} from "@app/hooks/api/auth/queries";
|
|
||||||
|
|
||||||
import Aes256Gcm from "./cryptography/aes-256-gcm";
|
import Aes256Gcm from "./cryptography/aes-256-gcm";
|
||||||
import { deriveArgonKey } from "./cryptography/crypto";
|
import { deriveArgonKey } from "./cryptography/crypto";
|
||||||
@@ -15,95 +13,95 @@ const clientOldPassword = new jsrp.client();
|
|||||||
const clientNewPassword = new jsrp.client();
|
const clientNewPassword = new jsrp.client();
|
||||||
|
|
||||||
type Params = {
|
type Params = {
|
||||||
email: string;
|
email: string;
|
||||||
currentPassword: string;
|
currentPassword: string;
|
||||||
newPassword: string;
|
newPassword: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
const attemptChangePassword = ({ email, currentPassword, newPassword }: Params): Promise<void> => {
|
const attemptChangePassword = ({ email, currentPassword, newPassword }: Params): Promise<void> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
clientOldPassword.init({ username: email, password: currentPassword }, async () => {
|
clientOldPassword.init({ username: email, password: currentPassword }, async () => {
|
||||||
let serverPublicKey; let salt;
|
let serverPublicKey;
|
||||||
|
let salt;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const clientPublicKey = clientOldPassword.getPublicKey();
|
||||||
|
|
||||||
|
const res = await srp1({ clientPublicKey });
|
||||||
|
|
||||||
|
serverPublicKey = res.serverPublicKey;
|
||||||
|
salt = res.salt;
|
||||||
|
|
||||||
|
clientOldPassword.setSalt(salt);
|
||||||
|
clientOldPassword.setServerPublicKey(serverPublicKey);
|
||||||
|
|
||||||
|
const clientProof = clientOldPassword.getProof();
|
||||||
|
|
||||||
|
clientNewPassword.init({ username: email, password: newPassword }, async () => {
|
||||||
|
clientNewPassword.createVerifier(async (err, result) => {
|
||||||
try {
|
try {
|
||||||
const clientPublicKey = clientOldPassword.getPublicKey();
|
const derivedKey = await deriveArgonKey({
|
||||||
|
password: newPassword,
|
||||||
|
salt: result.salt,
|
||||||
|
mem: 65536,
|
||||||
|
time: 3,
|
||||||
|
parallelism: 1,
|
||||||
|
hashLen: 32
|
||||||
|
});
|
||||||
|
|
||||||
const res = await srp1({ clientPublicKey });
|
if (!derivedKey) throw new Error("Failed to derive key from password");
|
||||||
|
|
||||||
serverPublicKey = res.serverPublicKey;
|
const key = crypto.randomBytes(32);
|
||||||
salt = res.salt;
|
|
||||||
|
|
||||||
clientOldPassword.setSalt(salt);
|
const {
|
||||||
clientOldPassword.setServerPublicKey(serverPublicKey);
|
ciphertext: encryptedPrivateKey,
|
||||||
|
iv: encryptedPrivateKeyIV,
|
||||||
|
tag: encryptedPrivateKeyTag
|
||||||
|
} = Aes256Gcm.encrypt({
|
||||||
|
text: localStorage.getItem("PRIVATE_KEY") as string,
|
||||||
|
secret: key
|
||||||
|
});
|
||||||
|
|
||||||
const clientProof = clientOldPassword.getProof();
|
const {
|
||||||
|
ciphertext: protectedKey,
|
||||||
|
iv: protectedKeyIV,
|
||||||
|
tag: protectedKeyTag
|
||||||
|
} = Aes256Gcm.encrypt({
|
||||||
|
text: key.toString("hex"),
|
||||||
|
secret: Buffer.from(derivedKey.hash)
|
||||||
|
});
|
||||||
|
|
||||||
clientNewPassword.init({ username: email, password: newPassword }, async () => {
|
await changePassword({
|
||||||
clientNewPassword.createVerifier(async (err, result) => {
|
clientProof,
|
||||||
try {
|
protectedKey,
|
||||||
const derivedKey = await deriveArgonKey({
|
protectedKeyIV,
|
||||||
password: newPassword,
|
protectedKeyTag,
|
||||||
salt: result.salt,
|
encryptedPrivateKey,
|
||||||
mem: 65536,
|
encryptedPrivateKeyIV,
|
||||||
time: 3,
|
encryptedPrivateKeyTag,
|
||||||
parallelism: 1,
|
salt: result.salt,
|
||||||
hashLen: 32
|
verifier: result.verifier
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!derivedKey) throw new Error("Failed to derive key from password");
|
saveTokenToLocalStorage({
|
||||||
|
encryptedPrivateKey,
|
||||||
|
iv: encryptedPrivateKeyIV,
|
||||||
|
tag: encryptedPrivateKeyTag
|
||||||
|
});
|
||||||
|
|
||||||
const key = crypto.randomBytes(32);
|
resolve();
|
||||||
|
} catch (err2) {
|
||||||
const {
|
console.error(err2);
|
||||||
ciphertext: encryptedPrivateKey,
|
reject(err2);
|
||||||
iv: encryptedPrivateKeyIV,
|
|
||||||
tag: encryptedPrivateKeyTag
|
|
||||||
} = Aes256Gcm.encrypt({
|
|
||||||
text: localStorage.getItem("PRIVATE_KEY") as string,
|
|
||||||
secret: key
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
|
||||||
ciphertext: protectedKey,
|
|
||||||
iv: protectedKeyIV,
|
|
||||||
tag: protectedKeyTag
|
|
||||||
} = Aes256Gcm.encrypt({
|
|
||||||
text: key.toString("hex"),
|
|
||||||
secret: Buffer.from(derivedKey.hash)
|
|
||||||
});
|
|
||||||
|
|
||||||
await changePassword({
|
|
||||||
clientProof,
|
|
||||||
protectedKey,
|
|
||||||
protectedKeyIV,
|
|
||||||
protectedKeyTag,
|
|
||||||
encryptedPrivateKey,
|
|
||||||
encryptedPrivateKeyIV,
|
|
||||||
encryptedPrivateKeyTag,
|
|
||||||
salt: result.salt,
|
|
||||||
verifier: result.verifier
|
|
||||||
});
|
|
||||||
|
|
||||||
saveTokenToLocalStorage({
|
|
||||||
encryptedPrivateKey,
|
|
||||||
iv: encryptedPrivateKeyIV,
|
|
||||||
tag: encryptedPrivateKeyTag
|
|
||||||
});
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
} catch (err2) {
|
|
||||||
console.error(err2);
|
|
||||||
reject(err2);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
reject(err);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export default attemptChangePassword;
|
export default attemptChangePassword;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable prefer-destructuring */
|
/* eslint-disable prefer-destructuring */
|
||||||
import jsrp from "jsrp";
|
import jsrp from "jsrp";
|
||||||
|
|
||||||
import { login1 , verifyMfaToken } from "@app/hooks/api/auth/queries";
|
import { login1, verifyMfaToken } from "@app/hooks/api/auth/queries";
|
||||||
import KeyService from "@app/services/KeyService";
|
import KeyService from "@app/services/KeyService";
|
||||||
|
|
||||||
import { saveTokenToLocalStorage } from "./saveTokenToLocalStorage";
|
import { saveTokenToLocalStorage } from "./saveTokenToLocalStorage";
|
||||||
@@ -11,11 +11,11 @@ import SecurityClient from "./SecurityClient";
|
|||||||
const client = new jsrp.client();
|
const client = new jsrp.client();
|
||||||
|
|
||||||
interface IsMfaLoginSuccessful {
|
interface IsMfaLoginSuccessful {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
loginResponse:{
|
loginResponse: {
|
||||||
privateKey: string;
|
privateKey: string;
|
||||||
JTWToken: string;
|
JTWToken: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,81 +26,84 @@ interface IsMfaLoginSuccessful {
|
|||||||
* @param {String} obj.mfaToken - MFA code/token
|
* @param {String} obj.mfaToken - MFA code/token
|
||||||
*/
|
*/
|
||||||
const attemptLoginMfa = async ({
|
const attemptLoginMfa = async ({
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
providerAuthToken,
|
providerAuthToken,
|
||||||
mfaToken
|
mfaToken
|
||||||
}: {
|
}: {
|
||||||
email: string;
|
email: string;
|
||||||
password: string;
|
password: string;
|
||||||
providerAuthToken?: string,
|
providerAuthToken?: string;
|
||||||
mfaToken: string;
|
mfaToken: string;
|
||||||
}): Promise<IsMfaLoginSuccessful> => {
|
}): Promise<IsMfaLoginSuccessful> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
client.init({
|
client.init(
|
||||||
username: email,
|
{
|
||||||
password
|
username: email,
|
||||||
}, async () => {
|
password
|
||||||
try {
|
},
|
||||||
const clientPublicKey = client.getPublicKey();
|
async () => {
|
||||||
const { salt } = await login1({
|
try {
|
||||||
email,
|
const clientPublicKey = client.getPublicKey();
|
||||||
clientPublicKey,
|
const { salt } = await login1({
|
||||||
providerAuthToken,
|
email,
|
||||||
});
|
clientPublicKey,
|
||||||
|
providerAuthToken
|
||||||
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
encryptionVersion,
|
encryptionVersion,
|
||||||
protectedKey,
|
protectedKey,
|
||||||
protectedKeyIV,
|
protectedKeyIV,
|
||||||
protectedKeyTag,
|
protectedKeyTag,
|
||||||
token,
|
token,
|
||||||
publicKey,
|
publicKey,
|
||||||
encryptedPrivateKey,
|
encryptedPrivateKey,
|
||||||
iv,
|
iv,
|
||||||
tag
|
tag
|
||||||
} = await verifyMfaToken({
|
} = await verifyMfaToken({
|
||||||
email,
|
email,
|
||||||
mfaCode: mfaToken
|
mfaCode: mfaToken
|
||||||
});
|
});
|
||||||
|
|
||||||
// unset temporary (MFA) JWT token and set JWT token
|
// unset temporary (MFA) JWT token and set JWT token
|
||||||
SecurityClient.setMfaToken("");
|
SecurityClient.setMfaToken("");
|
||||||
SecurityClient.setToken(token);
|
SecurityClient.setToken(token);
|
||||||
SecurityClient.setProviderAuthToken("");
|
SecurityClient.setProviderAuthToken("");
|
||||||
|
|
||||||
const privateKey = await KeyService.decryptPrivateKey({
|
const privateKey = await KeyService.decryptPrivateKey({
|
||||||
encryptionVersion,
|
encryptionVersion,
|
||||||
encryptedPrivateKey,
|
encryptedPrivateKey,
|
||||||
iv,
|
iv,
|
||||||
tag,
|
tag,
|
||||||
password,
|
password,
|
||||||
salt,
|
salt,
|
||||||
protectedKey,
|
protectedKey,
|
||||||
protectedKeyIV,
|
protectedKeyIV,
|
||||||
protectedKeyTag
|
protectedKeyTag
|
||||||
});
|
});
|
||||||
|
|
||||||
saveTokenToLocalStorage({
|
saveTokenToLocalStorage({
|
||||||
publicKey,
|
publicKey,
|
||||||
encryptedPrivateKey,
|
encryptedPrivateKey,
|
||||||
iv,
|
iv,
|
||||||
tag,
|
tag,
|
||||||
privateKey
|
privateKey
|
||||||
});
|
});
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
success: true,
|
success: true,
|
||||||
loginResponse:{
|
loginResponse: {
|
||||||
privateKey,
|
privateKey,
|
||||||
JTWToken: token
|
JTWToken: token
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
} catch (err) {
|
||||||
}
|
reject(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export default attemptLoginMfa;
|
export default attemptLoginMfa;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable prefer-destructuring */
|
/* eslint-disable prefer-destructuring */
|
||||||
import jsrp from "jsrp";
|
import jsrp from "jsrp";
|
||||||
|
|
||||||
import { login1 , verifyMfaToken } from "@app/hooks/api/auth/queries";
|
import { login1, verifyMfaToken } from "@app/hooks/api/auth/queries";
|
||||||
import KeyService from "@app/services/KeyService";
|
import KeyService from "@app/services/KeyService";
|
||||||
|
|
||||||
import { saveTokenToLocalStorage } from "./saveTokenToLocalStorage";
|
import { saveTokenToLocalStorage } from "./saveTokenToLocalStorage";
|
||||||
@@ -18,75 +18,78 @@ const client = new jsrp.client();
|
|||||||
* @param {String} obj.mfaToken - MFA code/token
|
* @param {String} obj.mfaToken - MFA code/token
|
||||||
*/
|
*/
|
||||||
const attemptLoginMfa = async ({
|
const attemptLoginMfa = async ({
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
providerAuthToken,
|
providerAuthToken,
|
||||||
mfaToken
|
mfaToken
|
||||||
}: {
|
}: {
|
||||||
email: string;
|
email: string;
|
||||||
password: string;
|
password: string;
|
||||||
providerAuthToken?: string,
|
providerAuthToken?: string;
|
||||||
mfaToken: string;
|
mfaToken: string;
|
||||||
}): Promise<Boolean> => {
|
}): Promise<Boolean> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
client.init({
|
client.init(
|
||||||
username: email,
|
{
|
||||||
password
|
username: email,
|
||||||
}, async () => {
|
password
|
||||||
try {
|
},
|
||||||
const clientPublicKey = client.getPublicKey();
|
async () => {
|
||||||
const { salt } = await login1({
|
try {
|
||||||
email,
|
const clientPublicKey = client.getPublicKey();
|
||||||
clientPublicKey,
|
const { salt } = await login1({
|
||||||
providerAuthToken,
|
email,
|
||||||
});
|
clientPublicKey,
|
||||||
|
providerAuthToken
|
||||||
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
encryptionVersion,
|
encryptionVersion,
|
||||||
protectedKey,
|
protectedKey,
|
||||||
protectedKeyIV,
|
protectedKeyIV,
|
||||||
protectedKeyTag,
|
protectedKeyTag,
|
||||||
token,
|
token,
|
||||||
publicKey,
|
publicKey,
|
||||||
encryptedPrivateKey,
|
encryptedPrivateKey,
|
||||||
iv,
|
iv,
|
||||||
tag
|
tag
|
||||||
} = await verifyMfaToken({
|
} = await verifyMfaToken({
|
||||||
email,
|
email,
|
||||||
mfaCode: mfaToken
|
mfaCode: mfaToken
|
||||||
});
|
});
|
||||||
|
|
||||||
// unset temporary (MFA) JWT token and set JWT token
|
// unset temporary (MFA) JWT token and set JWT token
|
||||||
SecurityClient.setMfaToken("");
|
SecurityClient.setMfaToken("");
|
||||||
SecurityClient.setToken(token);
|
SecurityClient.setToken(token);
|
||||||
SecurityClient.setProviderAuthToken("");
|
SecurityClient.setProviderAuthToken("");
|
||||||
|
|
||||||
const privateKey = await KeyService.decryptPrivateKey({
|
const privateKey = await KeyService.decryptPrivateKey({
|
||||||
encryptionVersion,
|
encryptionVersion,
|
||||||
encryptedPrivateKey,
|
encryptedPrivateKey,
|
||||||
iv,
|
iv,
|
||||||
tag,
|
tag,
|
||||||
password,
|
password,
|
||||||
salt,
|
salt,
|
||||||
protectedKey,
|
protectedKey,
|
||||||
protectedKeyIV,
|
protectedKeyIV,
|
||||||
protectedKeyTag
|
protectedKeyTag
|
||||||
});
|
});
|
||||||
|
|
||||||
saveTokenToLocalStorage({
|
saveTokenToLocalStorage({
|
||||||
publicKey,
|
publicKey,
|
||||||
encryptedPrivateKey,
|
encryptedPrivateKey,
|
||||||
iv,
|
iv,
|
||||||
tag,
|
tag,
|
||||||
privateKey
|
privateKey
|
||||||
});
|
});
|
||||||
|
|
||||||
resolve(true);
|
resolve(true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
});
|
);
|
||||||
}
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export default attemptLoginMfa;
|
export default attemptLoginMfa;
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
import { checkIsPasswordBreached } from "./checkIsPasswordBreached";
|
import { checkIsPasswordBreached } from "./checkIsPasswordBreached";
|
||||||
import { escapeCharRegex, letterCharRegex, lowEntropyRegexes,numAndSpecialCharRegex, repeatedCharRegex } from "./passwordRegexes";
|
import {
|
||||||
|
escapeCharRegex,
|
||||||
|
letterCharRegex,
|
||||||
|
lowEntropyRegexes,
|
||||||
|
numAndSpecialCharRegex,
|
||||||
|
repeatedCharRegex
|
||||||
|
} from "./passwordRegexes";
|
||||||
|
|
||||||
interface PasswordCheckProps {
|
interface PasswordCheckProps {
|
||||||
password: string;
|
password: string;
|
||||||
@@ -29,40 +35,38 @@ const passwordCheck = async ({
|
|||||||
{
|
{
|
||||||
name: "tooShort",
|
name: "tooShort",
|
||||||
validator: (pwd: string) => pwd.length >= 14,
|
validator: (pwd: string) => pwd.length >= 14,
|
||||||
setError: setPasswordErrorTooShort,
|
setError: setPasswordErrorTooShort
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "tooLong",
|
name: "tooLong",
|
||||||
validator: (pwd: string) => pwd.length < 101,
|
validator: (pwd: string) => pwd.length < 101,
|
||||||
setError: setPasswordErrorTooLong,
|
setError: setPasswordErrorTooLong
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "noLetterChar",
|
name: "noLetterChar",
|
||||||
validator: (pwd: string) => letterCharRegex.test(pwd),
|
validator: (pwd: string) => letterCharRegex.test(pwd),
|
||||||
setError: setPasswordErrorNoLetterChar,
|
setError: setPasswordErrorNoLetterChar
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "noNumOrSpecialChar",
|
name: "noNumOrSpecialChar",
|
||||||
validator: (pwd: string) => numAndSpecialCharRegex.test(pwd),
|
validator: (pwd: string) => numAndSpecialCharRegex.test(pwd),
|
||||||
setError: setPasswordErrorNoNumOrSpecialChar,
|
setError: setPasswordErrorNoNumOrSpecialChar
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "repeatedChar",
|
name: "repeatedChar",
|
||||||
validator: (pwd: string) => !repeatedCharRegex.test(pwd),
|
validator: (pwd: string) => !repeatedCharRegex.test(pwd),
|
||||||
setError: setPasswordErrorRepeatedChar,
|
setError: setPasswordErrorRepeatedChar
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "escapeChar",
|
name: "escapeChar",
|
||||||
validator: (pwd: string) => !escapeCharRegex.test(pwd),
|
validator: (pwd: string) => !escapeCharRegex.test(pwd),
|
||||||
setError: setPasswordErrorEscapeChar,
|
setError: setPasswordErrorEscapeChar
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "lowEntropy",
|
name: "lowEntropy",
|
||||||
validator: (pwd: string) => (
|
validator: (pwd: string) => !lowEntropyRegexes.some((regex) => regex.test(pwd)),
|
||||||
!lowEntropyRegexes.some(regex => regex.test(pwd))
|
setError: setPasswordErrorLowEntropy
|
||||||
),
|
}
|
||||||
setError: setPasswordErrorLowEntropy,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const isBreached = await checkIsPasswordBreached(password);
|
const isBreached = await checkIsPasswordBreached(password);
|
||||||
@@ -81,7 +85,7 @@ const passwordCheck = async ({
|
|||||||
} else {
|
} else {
|
||||||
test.setError(false);
|
test.setError(false);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
return errorCheck;
|
return errorCheck;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,25 +17,25 @@ function bufferToHex(buffer: ArrayBuffer): string {
|
|||||||
return hexParts.join("");
|
return hexParts.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
// see API details here: https://haveibeenpwned.com/API/v3#SearchingPwnedPasswordsByRange
|
// see API details here: https://haveibeenpwned.com/API/v3#SearchingPwnedPasswordsByRange
|
||||||
// in short, the pending password is hashed (SHA-1), the first 5 chars are sliced and compared against a ranged hash table
|
// in short, the pending password is hashed (SHA-1), the first 5 chars are sliced and compared against a ranged hash table
|
||||||
// this hash table is formed from the 5 char hash prefix (ie. 00000-FFFFF) so 16^5 results
|
// this hash table is formed from the 5 char hash prefix (ie. 00000-FFFFF) so 16^5 results
|
||||||
// returns a hash table of 800-1000 results
|
// returns a hash table of 800-1000 results
|
||||||
// padding has been added to prevent MitM attacker determining which hash table was called by the response size
|
// padding has been added to prevent MitM attacker determining which hash table was called by the response size
|
||||||
// the last 35 chars of the password hash are compared client-side against the table
|
// the last 35 chars of the password hash are compared client-side against the table
|
||||||
// if there is a match, that password has been involved in a password breach (ie. pwnd) and should NOT be accepted
|
// if there is a match, that password has been involved in a password breach (ie. pwnd) and should NOT be accepted
|
||||||
// the database consists of ~700 mln breached passwords and is continuously updated, including with law enforcement ingestion
|
// the database consists of ~700 mln breached passwords and is continuously updated, including with law enforcement ingestion
|
||||||
// https://www.troyhunt.com/open-source-pwned-passwords-with-fbi-feed-and-225m-new-nca-passwords-is-now-live/
|
// https://www.troyhunt.com/open-source-pwned-passwords-with-fbi-feed-and-225m-new-nca-passwords-is-now-live/
|
||||||
|
|
||||||
// The HIBP API follows NIST guidance (pg.14) https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-63b.pdf
|
// The HIBP API follows NIST guidance (pg.14) https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-63b.pdf
|
||||||
// "When processing requests to establish and change memorized secrets, verifiers SHALL compare
|
// "When processing requests to establish and change memorized secrets, verifiers SHALL compare
|
||||||
// the prospective secrets against a list that contains values known to be commonly-used, expected,
|
// the prospective secrets against a list that contains values known to be commonly-used, expected,
|
||||||
// or compromised. For example, the list MAY include, but is not limited to:
|
// or compromised. For example, the list MAY include, but is not limited to:
|
||||||
// • Passwords obtained from previous breach corpuses.
|
// • Passwords obtained from previous breach corpuses.
|
||||||
// • Dictionary words.
|
// • Dictionary words.
|
||||||
// • Repetitive or sequential characters (e.g. ‘aaaaaa’, ‘1234abcd’).
|
// • Repetitive or sequential characters (e.g. ‘aaaaaa’, ‘1234abcd’).
|
||||||
// • Context-specific words, such as the name of the service, the username, and derivatives
|
// • Context-specific words, such as the name of the service, the username, and derivatives
|
||||||
// thereof."
|
// thereof."
|
||||||
|
|
||||||
export const checkIsPasswordBreached = async (password: string): Promise<boolean> => {
|
export const checkIsPasswordBreached = async (password: string): Promise<boolean> => {
|
||||||
const HAVE_I_BEEN_PWNED_API_URL = "https://api.pwnedpasswords.com";
|
const HAVE_I_BEEN_PWNED_API_URL = "https://api.pwnedpasswords.com";
|
||||||
@@ -66,8 +66,8 @@ export const checkIsPasswordBreached = async (password: string): Promise<boolean
|
|||||||
response = await axios.get(rangedHashTableUri, {
|
response = await axios.get(rangedHashTableUri, {
|
||||||
headers: {
|
headers: {
|
||||||
"Add-Padding": "true", // see https://www.troyhunt.com/enhancing-pwned-passwords-privacy-with-padding/
|
"Add-Padding": "true", // see https://www.troyhunt.com/enhancing-pwned-passwords-privacy-with-padding/
|
||||||
"Content-Type": "text/plain",
|
"Content-Type": "text/plain"
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
@@ -77,8 +77,7 @@ export const checkIsPasswordBreached = async (password: string): Promise<boolean
|
|||||||
const isBreachedPassword: boolean = responseData.includes(hashedPwd.slice(5, 40));
|
const isBreachedPassword: boolean = responseData.includes(hashedPwd.slice(5, 40));
|
||||||
return isBreachedPassword;
|
return isBreachedPassword;
|
||||||
}
|
}
|
||||||
retryAttempt += 1;
|
retryAttempt += 1;
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!axios.isAxiosError(err)) {
|
if (!axios.isAxiosError(err)) {
|
||||||
throw err;
|
throw err;
|
||||||
@@ -88,14 +87,15 @@ export const checkIsPasswordBreached = async (password: string): Promise<boolean
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.error(
|
console.error(
|
||||||
`Received a non-200 response (${response ? response.status : "unknown"}) from the Pwnd Passwords API`
|
`Received a non-200 response (${
|
||||||
|
response ? response.status : "unknown"
|
||||||
|
}) from the Pwnd Passwords API`
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error("An unexpected error has occurred:", err.message);
|
console.error("An unexpected error has occurred:", err.message);
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
|
|
||||||
// Clear the UTF-8 encoded password from memory
|
// Clear the UTF-8 encoded password from memory
|
||||||
|
|
||||||
if (encodedPwd) {
|
if (encodedPwd) {
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import { checkIsPasswordBreached } from "./checkIsPasswordBreached";
|
import { checkIsPasswordBreached } from "./checkIsPasswordBreached";
|
||||||
import { escapeCharRegex, letterCharRegex, lowEntropyRegexes,numAndSpecialCharRegex, repeatedCharRegex } from "./passwordRegexes";
|
import {
|
||||||
|
escapeCharRegex,
|
||||||
|
letterCharRegex,
|
||||||
|
lowEntropyRegexes,
|
||||||
|
numAndSpecialCharRegex,
|
||||||
|
repeatedCharRegex
|
||||||
|
} from "./passwordRegexes";
|
||||||
|
|
||||||
type Errors = {
|
type Errors = {
|
||||||
tooShort?: string;
|
tooShort?: string;
|
||||||
@@ -44,40 +50,38 @@ const checkPassword = async ({ password, setErrors }: CheckPasswordParams): Prom
|
|||||||
{
|
{
|
||||||
name: "tooShort",
|
name: "tooShort",
|
||||||
validator: (pwd: string) => pwd.length >= 14,
|
validator: (pwd: string) => pwd.length >= 14,
|
||||||
errorText: "at least 14 characters",
|
errorText: "at least 14 characters"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "tooLong",
|
name: "tooLong",
|
||||||
validator: (pwd: string) => pwd.length < 101,
|
validator: (pwd: string) => pwd.length < 101,
|
||||||
errorText: "at most 100 characters",
|
errorText: "at most 100 characters"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "noLetterChar",
|
name: "noLetterChar",
|
||||||
validator: (pwd: string) => letterCharRegex.test(pwd),
|
validator: (pwd: string) => letterCharRegex.test(pwd),
|
||||||
errorText: "at least 1 letter character",
|
errorText: "at least 1 letter character"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "noNumOrSpecialChar",
|
name: "noNumOrSpecialChar",
|
||||||
validator: (pwd: string) => numAndSpecialCharRegex.test(pwd),
|
validator: (pwd: string) => numAndSpecialCharRegex.test(pwd),
|
||||||
errorText: "at least 1 number or special character",
|
errorText: "at least 1 number or special character"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "repeatedChar",
|
name: "repeatedChar",
|
||||||
validator: (pwd: string) => !repeatedCharRegex.test(pwd),
|
validator: (pwd: string) => !repeatedCharRegex.test(pwd),
|
||||||
errorText: "at most 3 repeated, consecutive characters",
|
errorText: "at most 3 repeated, consecutive characters"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "escapeChar",
|
name: "escapeChar",
|
||||||
validator: (pwd: string) => !escapeCharRegex.test(pwd),
|
validator: (pwd: string) => !escapeCharRegex.test(pwd),
|
||||||
errorText: "No escape characters allowed.",
|
errorText: "No escape characters allowed."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "lowEntropy",
|
name: "lowEntropy",
|
||||||
validator: (pwd: string) => (
|
validator: (pwd: string) => !lowEntropyRegexes.some((regex) => regex.test(pwd)),
|
||||||
!lowEntropyRegexes.some(regex => regex.test(pwd))
|
errorText: "Password contains personal info."
|
||||||
),
|
}
|
||||||
errorText: "Password contains personal info.",
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const isBreached = await checkIsPasswordBreached(password);
|
const isBreached = await checkIsPasswordBreached(password);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// This regex covers letters (case insensitive) for the top 50 most spoken languages
|
// This regex covers letters (case insensitive) for the top 50 most spoken languages
|
||||||
/* eslint-disable no-misleading-character-class */
|
/* eslint-disable no-misleading-character-class */
|
||||||
export const letterCharRegex = /[A-Za-z\u00C0-\u00D6\u00D8-\u00DE\u00DF-\u00F6\u00F8-\u00FF\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u0600-\u06FF\u0400-\u04FF\u0500-\u052F\u2DE0-\u2DFF\uA640-\uA69F\u05B0-\u05FF\u0980-\u09FF\u1F00-\u1FFF\u0130\u015E\u011E\u00C7\u00FC\u00FB\u00EB\u00E7]/u;
|
export const letterCharRegex =
|
||||||
|
/[A-Za-z\u00C0-\u00D6\u00D8-\u00DE\u00DF-\u00F6\u00F8-\u00FF\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u0600-\u06FF\u0400-\u04FF\u0500-\u052F\u2DE0-\u2DFF\uA640-\uA69F\u05B0-\u05FF\u0980-\u09FF\u1F00-\u1FFF\u0130\u015E\u011E\u00C7\u00FC\u00FB\u00EB\u00E7]/u;
|
||||||
|
|
||||||
// This regex covers digits, special characters, symbols, and emojis.
|
// This regex covers digits, special characters, symbols, and emojis.
|
||||||
export const numAndSpecialCharRegex = /[\d!@#$%^&*(),.?":{}|<>]|[^\p{L}\p{N}\s]/u;
|
export const numAndSpecialCharRegex = /[\d!@#$%^&*(),.?":{}|<>]|[^\p{L}\p{N}\s]/u;
|
||||||
@@ -32,5 +33,5 @@ export const lowEntropyRegexes = [
|
|||||||
/\b(?:[A-Z0-9]{7,10}|[A-Z0-9]{10,11}|[A-Z0-9]{7,10})\b/,
|
/\b(?:[A-Z0-9]{7,10}|[A-Z0-9]{10,11}|[A-Z0-9]{7,10})\b/,
|
||||||
|
|
||||||
// US social security number
|
// US social security number
|
||||||
/\b\d{3}[-\s]?\d{2}[-\s]?\d{4}\b/,
|
/\b\d{3}[-\s]?\d{2}[-\s]?\d{4}\b/
|
||||||
];
|
];
|
||||||
@@ -3,9 +3,7 @@ import crypto from "crypto";
|
|||||||
|
|
||||||
import jsrp from "jsrp";
|
import jsrp from "jsrp";
|
||||||
|
|
||||||
import { issueBackupPrivateKey ,
|
import { issueBackupPrivateKey, srp1 } from "@app/hooks/api/auth/queries";
|
||||||
srp1
|
|
||||||
} from "@app/hooks/api/auth/queries";
|
|
||||||
|
|
||||||
import generateBackupPDF from "../generateBackupPDF";
|
import generateBackupPDF from "../generateBackupPDF";
|
||||||
import Aes256Gcm from "./aes-256-gcm";
|
import Aes256Gcm from "./aes-256-gcm";
|
||||||
@@ -97,7 +95,6 @@ const issueBackupKey = async ({
|
|||||||
generatedKey
|
generatedKey
|
||||||
});
|
});
|
||||||
setBackupKeyIssued(true);
|
setBackupKeyIssued(true);
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
setBackupKeyError(true);
|
setBackupKeyError(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,7 @@ import { useRouter } from "next/router";
|
|||||||
|
|
||||||
import { useUser } from "@app/context";
|
import { useUser } from "@app/context";
|
||||||
|
|
||||||
import {
|
import { boot as bootIntercom, load as loadIntercom, update as updateIntercom } from "./intercom";
|
||||||
boot as bootIntercom,
|
|
||||||
load as loadIntercom,
|
|
||||||
update as updateIntercom,
|
|
||||||
} from "./intercom";
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
export const IntercomProvider = ({ children }: { children: any }) => {
|
export const IntercomProvider = ({ children }: { children: any }) => {
|
||||||
@@ -16,7 +12,11 @@ export const IntercomProvider = ({ children }: { children: any }) => {
|
|||||||
|
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
loadIntercom();
|
loadIntercom();
|
||||||
bootIntercom({name: `${user?.firstName || ""} ${user?.lastName || ""}`, email: user?.email || "", created_at: Math.floor(((new Date(user?.createdAt))?.getTime() || 0) / 1000)});
|
bootIntercom({
|
||||||
|
name: `${user?.firstName || ""} ${user?.lastName || ""}`,
|
||||||
|
email: user?.email || "",
|
||||||
|
created_at: Math.floor((new Date(user?.createdAt)?.getTime() || 0) / 1000)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export const isValidHexColor = (hexColor: string) => {
|
export const isValidHexColor = (hexColor: string) => {
|
||||||
const hexColorPattern = /^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;
|
const hexColorPattern = /^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;
|
||||||
|
|
||||||
return hexColorPattern.test(hexColor);
|
return hexColorPattern.test(hexColor);
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -17,10 +17,9 @@ export const saveTokenToLocalStorage = ({
|
|||||||
encryptedPrivateKey,
|
encryptedPrivateKey,
|
||||||
iv,
|
iv,
|
||||||
tag,
|
tag,
|
||||||
privateKey,
|
privateKey
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (protectedKey) {
|
if (protectedKey) {
|
||||||
localStorage.removeItem("protectedKey");
|
localStorage.removeItem("protectedKey");
|
||||||
localStorage.setItem("protectedKey", protectedKey);
|
localStorage.setItem("protectedKey", protectedKey);
|
||||||
@@ -62,9 +61,7 @@ export const saveTokenToLocalStorage = ({
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof Error) {
|
if (err instanceof Error) {
|
||||||
throw new Error(
|
throw new Error(`Unable to send the tokens in local storage:${err.message}`);
|
||||||
`Unable to send the tokens in local storage:${ err.message}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user