mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-17 01:12:59 +00:00
feat: renamed old frontend and fixed some minor bugs
This commit is contained in:
@@ -4,7 +4,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
const Error = ({ text }: { text: string }): JSX.Element => {
|
||||
return (
|
||||
<div className="relative m-auto flex w-fit flex-row items-center justify-center rounded-full">
|
||||
<FontAwesomeIcon icon={faExclamationTriangle} className="mx-2 mt-1.5 mb-2 text-red" />
|
||||
<FontAwesomeIcon icon={faExclamationTriangle} className="mx-2 mb-2 mt-1.5 text-red" />
|
||||
{text && <p className="relative top-0 mr-2 py-1 text-sm text-red">{text}</p>}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -48,7 +48,7 @@ const InputField = ({
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
required={isRequired}
|
||||
className="text-md min-w-16 w-full rounded-md border border-gray-600 bg-bunker-800 p-2 text-gray-400 outline-none"
|
||||
className="text-md w-full min-w-16 rounded-md border border-gray-600 bg-bunker-800 p-2 text-gray-400 outline-none"
|
||||
name={name}
|
||||
readOnly
|
||||
autoComplete={autoComplete}
|
||||
@@ -79,7 +79,7 @@ const InputField = ({
|
||||
: ""
|
||||
} ${
|
||||
error ? "focus:ring-red/50" : "focus:ring-primary/50"
|
||||
} 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`}
|
||||
} text-md peer relative w-full min-w-16 rounded-md bg-mineshaft-900 p-2 text-gray-400 outline-none duration-200 focus:ring-4`}
|
||||
name={name}
|
||||
spellCheck="false"
|
||||
autoComplete={autoComplete}
|
||||
@@ -121,7 +121,7 @@ const InputField = ({
|
||||
</div>
|
||||
)} */}
|
||||
</div>
|
||||
{error && <p className="mx-0 mt-0.5 mb-2 max-w-xs text-xs text-red">{errorText}</p>}
|
||||
{error && <p className="mx-0 mb-2 mt-0.5 max-w-xs text-xs text-red">{errorText}</p>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
import React, { Fragment } from "react";
|
||||
import { faAngleDown, faCheck, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Listbox, Transition } from "@headlessui/react";
|
||||
|
||||
interface ListBoxProps {
|
||||
isSelected: string;
|
||||
onChange: (arg: string) => void;
|
||||
data: string[] | null;
|
||||
text?: string;
|
||||
buttonAction?: () => void;
|
||||
isFull?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the component that we use for drop down lists.
|
||||
* @param {object} obj
|
||||
* @param {string} obj.isSelected - the item that is currently selected
|
||||
* @param {function} obj.onChange - what happends if you select the item inside a list
|
||||
* @param {string[]} obj.data - all the options available
|
||||
* @param {string} obj.text - the text that shows us in front of the select option
|
||||
* @param {function} obj.buttonAction - if there is a button at the bottom of the list, this is the action that happens when you click the button
|
||||
* @returns
|
||||
*/
|
||||
const ListBox = ({
|
||||
isSelected,
|
||||
onChange,
|
||||
data,
|
||||
text,
|
||||
buttonAction,
|
||||
isFull
|
||||
}: ListBoxProps): JSX.Element => {
|
||||
return (
|
||||
<Listbox value={isSelected} onChange={onChange}>
|
||||
<div className="relative">
|
||||
<Listbox.Button
|
||||
className={`relative text-gray-400 ${
|
||||
isFull ? "w-full" : "w-52"
|
||||
} 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">
|
||||
{text}
|
||||
<span className="ml-1 block cursor-pointer truncate font-semibold text-gray-300">
|
||||
{" "}
|
||||
{isSelected}
|
||||
</span>
|
||||
</div>
|
||||
{data && (
|
||||
<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" />
|
||||
</div>
|
||||
)}
|
||||
</Listbox.Button>
|
||||
{data && (
|
||||
<Transition
|
||||
as={Fragment}
|
||||
leave="transition ease-in duration-100"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<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) => (
|
||||
<Listbox.Option
|
||||
key={`${person}.${personIdx + 1}`}
|
||||
className={({ active, selected }) =>
|
||||
`relative my-0.5 cursor-default select-none rounded-md py-2 pl-10 pr-4 ${
|
||||
selected ? "bg-white/10 font-bold text-gray-400" : ""
|
||||
} ${
|
||||
active && !selected
|
||||
? "cursor-pointer bg-white/5 text-mineshaft-200"
|
||||
: "text-gray-400"
|
||||
} `
|
||||
}
|
||||
value={person}
|
||||
>
|
||||
{({ selected }) => (
|
||||
<>
|
||||
<span
|
||||
className={`block truncate text-primary${
|
||||
selected ? "font-medium" : "font-normal"
|
||||
}`}
|
||||
>
|
||||
{person}
|
||||
</span>
|
||||
{selected ? (
|
||||
<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" />
|
||||
</span>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</Listbox.Option>
|
||||
))}
|
||||
{buttonAction && (
|
||||
<button type="button" onClick={buttonAction} className="w-full cursor-pointer">
|
||||
<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="absolute inset-y-0 left-0 flex items-center rounded-lg pl-3 pr-4">
|
||||
<FontAwesomeIcon icon={faPlus} className="text-lg" />
|
||||
</span>
|
||||
Add Project
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
</Listbox.Options>
|
||||
</Transition>
|
||||
)}
|
||||
</div>
|
||||
</Listbox>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListBox;
|
||||
@@ -1,45 +0,0 @@
|
||||
import { Switch } from "@headlessui/react";
|
||||
|
||||
interface ToggleProps {
|
||||
enabled: boolean;
|
||||
setEnabled: (value: boolean) => void;
|
||||
addOverride: (value: string | undefined, id: string) => void;
|
||||
id: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a typical 'iPhone' toggle (e.g., user for overriding secrets with personal values)
|
||||
* @param obj
|
||||
* @param {boolean} obj.enabled - whether the toggle is turned on or off
|
||||
* @param {function} obj.setEnabled - change the state of the toggle
|
||||
* @param {function} obj.addOverride - a function that adds an override to a certain secret
|
||||
* @param {number} obj.id - id of a certain secret
|
||||
* @returns
|
||||
*/
|
||||
const Toggle = ({ enabled, setEnabled, addOverride, id }: ToggleProps): JSX.Element => {
|
||||
return (
|
||||
<Switch
|
||||
checked={enabled}
|
||||
onChange={() => {
|
||||
if (enabled === false) {
|
||||
addOverride("", id);
|
||||
} else {
|
||||
addOverride(undefined, id);
|
||||
}
|
||||
setEnabled(!enabled);
|
||||
}}
|
||||
className={`${
|
||||
enabled ? "bg-primary" : "bg-bunker-400"
|
||||
} relative inline-flex h-5 w-9 items-center rounded-full`}
|
||||
>
|
||||
<span className="sr-only">Enable notifications</span>
|
||||
<span
|
||||
className={`${
|
||||
enabled ? "translate-x-[1.26rem]" : "translate-x-0.5"
|
||||
} inline-block h-3.5 w-3.5 transform rounded-full bg-bunker-800 transition`}
|
||||
/>
|
||||
</Switch>
|
||||
);
|
||||
};
|
||||
|
||||
export default Toggle;
|
||||
@@ -1,148 +0,0 @@
|
||||
/* eslint-disable react/button-has-type */
|
||||
import React, { ButtonHTMLAttributes } from "react";
|
||||
import Image from "next/image";
|
||||
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
const classNames = require("classnames");
|
||||
|
||||
type ButtonProps = {
|
||||
text?: string;
|
||||
onButtonPressed: () => void;
|
||||
loading?: boolean;
|
||||
color?: string;
|
||||
size: string;
|
||||
icon?: IconProp;
|
||||
active?: boolean;
|
||||
iconDisabled?: IconProp;
|
||||
textDisabled?: string;
|
||||
type?: ButtonHTMLAttributes<any>["type"];
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the main butto component in the app.
|
||||
* @param {object} props
|
||||
* @param {string} props.text - text inside the button
|
||||
* @param {function} props.onButtonPressed - the action that happens when the button is clicked
|
||||
* @param {boolean} props.loading - if a button is currently in the laoding state
|
||||
* @param {string} props.color - button color
|
||||
* @param {string} props.size - button size
|
||||
* @param {FontAwesomeIconProps} props.icon - the icon inside the button
|
||||
* @param {boolean} props.active - if the button is active or disabled
|
||||
* @param {FontAwesomeIconProps} props.text - the icon inside the button when it is disabled
|
||||
* @param {string} props.textDisable - text inside the button when it is disabled
|
||||
* @returns
|
||||
*/
|
||||
const Button = ({
|
||||
active,
|
||||
text,
|
||||
textDisabled,
|
||||
color,
|
||||
size,
|
||||
onButtonPressed,
|
||||
loading,
|
||||
icon,
|
||||
iconDisabled,
|
||||
type = "button"
|
||||
}: ButtonProps): JSX.Element => {
|
||||
// Check if the button show always be 'active' - then true;
|
||||
// or if it should switch between 'active' and 'disabled' - then give the status
|
||||
const activityStatus = active || (text !== "" && textDisabled === undefined);
|
||||
|
||||
const styleButton = classNames(
|
||||
"group m-auto md:m-0 inline-block rounded-md duration-200",
|
||||
|
||||
// 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",
|
||||
(color === "primary" || !color) &&
|
||||
activityStatus &&
|
||||
"bg-primary border border-primary-400 opacity-80 hover:opacity-100",
|
||||
(color === "primary" || !color) && !activityStatus && "bg-primary",
|
||||
color === "red" && "bg-red-800 border border-red",
|
||||
|
||||
// Changing the opacity when active vs when not
|
||||
activityStatus ? "opacity-100 cursor-pointer" : "opacity-40",
|
||||
|
||||
// Setting the button sizes
|
||||
size === "md" && "h-10 w-full px-2 md:px-4",
|
||||
size === "lg" && "h-12 w-full px-2 md:px-8",
|
||||
!size && "md:py-1 px-3 md:px-8",
|
||||
size === "icon-md" && "h-10 w-10 flex items-center justify-center",
|
||||
size === "icon-sm" && "h-9 w-9 flex items-center justify-center"
|
||||
);
|
||||
|
||||
const styleMainDiv = classNames(
|
||||
"relative font-medium flex items-center",
|
||||
|
||||
// Setting the text color for the text and icon
|
||||
color === "mineshaft" && "text-bunker-200",
|
||||
color !== "mineshaft" && color !== "red" && color !== "none" && "text-black",
|
||||
color === "red" && "text-gray-200",
|
||||
color === "none" && "text-gray-200 text-xl",
|
||||
activityStatus && color !== "red" && color !== "mineshaft" && color !== "none"
|
||||
? "group-hover:text-black"
|
||||
: "",
|
||||
|
||||
size === "icon" && "flex items-center justify-center"
|
||||
);
|
||||
|
||||
const textStyle = classNames(
|
||||
"relative duration-200 text-center w-full",
|
||||
|
||||
// Show the loading sign if the loading indicator is on
|
||||
loading ? "opacity-0" : "opacity-100",
|
||||
size === "md" && "text-sm",
|
||||
size === "lg" && "text-lg"
|
||||
);
|
||||
|
||||
const button = (
|
||||
<button
|
||||
disabled={!activityStatus}
|
||||
type={type}
|
||||
onClick={onButtonPressed}
|
||||
className={styleButton}
|
||||
>
|
||||
<div className={styleMainDiv}>
|
||||
<div
|
||||
className={`${
|
||||
loading === true ? "opacity-100" : "opacity-0"
|
||||
} absolute flex w-full items-center bg-primary px-3 duration-200`}
|
||||
>
|
||||
<Image
|
||||
src="/images/loading/loadingblack.gif"
|
||||
height={25}
|
||||
width={42}
|
||||
alt="loading animation"
|
||||
className="rounded-xl"
|
||||
/>
|
||||
</div>
|
||||
{icon && (
|
||||
<FontAwesomeIcon
|
||||
icon={icon}
|
||||
className={`my-auto flex font-extrabold ${size === "icon-sm" ? "text-sm" : "text-sm"} ${
|
||||
(text || textDisabled) && "mr-2"
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
{iconDisabled && (
|
||||
<FontAwesomeIcon
|
||||
icon={iconDisabled as IconProp}
|
||||
className={`my-auto flex font-extrabold ${size === "icon-sm" ? "text-sm" : "text-md"} ${
|
||||
(text || textDisabled) && "mr-2"
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
{(text || textDisabled) && (
|
||||
<p className={textStyle}>{activityStatus ? text : textDisabled}</p>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
|
||||
return button;
|
||||
};
|
||||
|
||||
export default Button;
|
||||
@@ -1,100 +0,0 @@
|
||||
import { Fragment } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
|
||||
import Button from "../buttons/Button";
|
||||
|
||||
interface IntegrationOption {
|
||||
clientId: string;
|
||||
clientSlug?: string; // vercel-integration specific
|
||||
docsLink: string;
|
||||
image: string;
|
||||
isAvailable: boolean;
|
||||
name: string;
|
||||
slug: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
closeModal: () => void;
|
||||
selectedIntegrationOption: IntegrationOption | null;
|
||||
integrationOptionPress: (integrationOption: IntegrationOption) => void;
|
||||
};
|
||||
|
||||
const ActivateBotDialog = ({
|
||||
isOpen,
|
||||
closeModal,
|
||||
selectedIntegrationOption,
|
||||
integrationOptionPress
|
||||
}: Props) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const submit = async () => {
|
||||
try {
|
||||
// type check
|
||||
if (!selectedIntegrationOption) return;
|
||||
|
||||
// start integration or probe for PAT
|
||||
integrationOptionPress(selectedIntegrationOption);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
closeModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={closeModal}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black bg-opacity-70" />
|
||||
</Transition.Child>
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<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 as="h3" className="text-lg font-medium leading-6 text-gray-400">
|
||||
{t("integrations.grant-access-to-secrets")}
|
||||
</Dialog.Title>
|
||||
<div className="mt-2 mb-2">
|
||||
<p className="text-sm text-gray-500">
|
||||
{t("integrations.why-infisical-needs-access")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-6 max-w-max">
|
||||
<Button
|
||||
onButtonPressed={submit}
|
||||
color="mineshaft"
|
||||
text={t("integrations.grant-access-button") as string}
|
||||
size="md"
|
||||
/>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ActivateBotDialog;
|
||||
@@ -1,149 +0,0 @@
|
||||
import { Fragment } from "react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { useRouter } from "next/router";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
|
||||
import Button from "../buttons/Button";
|
||||
import ListBox from "../Listbox";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
closeModal: () => void;
|
||||
submitModal: () => void;
|
||||
data: any;
|
||||
email: string;
|
||||
setEmail: (email: string) => void;
|
||||
};
|
||||
|
||||
const AddProjectMemberDialog = ({
|
||||
isOpen,
|
||||
closeModal,
|
||||
submitModal,
|
||||
data,
|
||||
email,
|
||||
setEmail
|
||||
}: Props) => {
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="z-50">
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative" onClose={closeModal}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black bg-opacity-70" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className="w-full max-w-md transform rounded-md border border-mineshaft-600 bg-mineshaft-800 p-6 text-left align-middle shadow-xl transition-all">
|
||||
{data?.length > 0 ? (
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="z-50 text-lg font-medium leading-6 text-gray-400"
|
||||
>
|
||||
{t("section.members.add-dialog.add-member-to-project")}
|
||||
</Dialog.Title>
|
||||
) : (
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="z-50 mb-4 text-lg font-medium text-mineshaft-300"
|
||||
>
|
||||
{t("section.members.add-dialog.already-all-invited")}
|
||||
</Dialog.Title>
|
||||
)}
|
||||
<div className="mt-2 mb-4">
|
||||
{data?.length > 0 ? (
|
||||
<div className="flex flex-col">
|
||||
<p className="text-sm text-gray-500">
|
||||
{t("section.members.add-dialog.user-will-email")}
|
||||
</p>
|
||||
<div className="">
|
||||
<Trans
|
||||
i18nKey="section.members.add-dialog.looking-add"
|
||||
components={[
|
||||
// eslint-disable-next-line react/jsx-key
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex justify-center rounded-md py-1 text-sm text-gray-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||
onClick={() => router.push(`/org/${router.query.id}/members`)}
|
||||
aria-label="add member"
|
||||
/>,
|
||||
// eslint-disable-next-line react/jsx-key
|
||||
<button
|
||||
type="button"
|
||||
className="ml-1 inline-flex justify-center rounded-md py-1 text-sm text-gray-500 hover:text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||
onClick={() =>
|
||||
router.push(`/org/${router.query.id}/members?action=invite`)
|
||||
}
|
||||
aria-label="add member"
|
||||
/>
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-gray-500">
|
||||
{t("section.members.add-dialog.add-user-org-first")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="max-h-28">
|
||||
{data?.length > 0 && (
|
||||
<ListBox
|
||||
isSelected={email || data[0]}
|
||||
onChange={setEmail}
|
||||
data={data}
|
||||
isFull
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="max-w-max">
|
||||
{data?.length > 0 ? (
|
||||
<div className="mt-6 flex w-max flex-col justify-start">
|
||||
<Button
|
||||
onButtonPressed={submitModal}
|
||||
color="mineshaft"
|
||||
text={t("section.members.add-member") as string}
|
||||
size="md"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
onButtonPressed={() =>
|
||||
router.push(`/org/${localStorage.getItem("orgData.id")}/members`)
|
||||
}
|
||||
color="mineshaft"
|
||||
text={t("section.members.add-dialog.add-user-to-org") as string}
|
||||
size="md"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddProjectMemberDialog;
|
||||
@@ -1,140 +0,0 @@
|
||||
import { FormEventHandler, Fragment, useEffect, useState } from "react";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
|
||||
import Button from "../buttons/Button";
|
||||
import InputField from "../InputField";
|
||||
|
||||
type FormFields = { name: string; slug: string };
|
||||
|
||||
type Props = {
|
||||
isOpen?: boolean;
|
||||
isEditMode?: boolean;
|
||||
// on edit mode load up initial values
|
||||
initialValues?: FormFields;
|
||||
onClose: () => void;
|
||||
onCreateSubmit: (data: FormFields) => void;
|
||||
onEditSubmit: (data: FormFields) => void;
|
||||
};
|
||||
|
||||
// TODO: Migrate to better form management and validation. Preferable react-hook-form + yup
|
||||
/**
|
||||
* The dialog modal for when the user wants to create a new workspace
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
export const AddUpdateEnvironmentDialog = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
onCreateSubmit,
|
||||
onEditSubmit,
|
||||
initialValues,
|
||||
isEditMode
|
||||
}: Props) => {
|
||||
const [formInput, setFormInput] = useState<FormFields>({
|
||||
name: "",
|
||||
slug: ""
|
||||
});
|
||||
|
||||
// This use effect can be removed when the unmount is happening from outside the component
|
||||
// When unmount happens outside state gets unmounted also
|
||||
useEffect(() => {
|
||||
setFormInput(initialValues || { name: "", slug: "" });
|
||||
}, [isOpen]);
|
||||
|
||||
// REFACTOR: Move to react-hook-form with yup for better form management
|
||||
const onInputChange = (fieldName: string, fieldValue: string) => {
|
||||
setFormInput((state) => ({ ...state, [fieldName]: fieldValue }));
|
||||
};
|
||||
|
||||
const onFormSubmit: FormEventHandler = (e) => {
|
||||
e.preventDefault();
|
||||
const data = {
|
||||
name: formInput.name,
|
||||
slug: formInput.slug.toLowerCase()
|
||||
};
|
||||
if (isEditMode) {
|
||||
onEditSubmit(data);
|
||||
return;
|
||||
}
|
||||
onCreateSubmit(data);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-20" onClose={onClose}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-out duration-150"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black bg-opacity-70" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-50 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<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 as="h3" className="text-lg font-medium leading-6 text-gray-400">
|
||||
{isEditMode ? "Update environment" : "Create a new environment"}
|
||||
</Dialog.Title>
|
||||
<form onSubmit={onFormSubmit}>
|
||||
<div className="mt-4 max-h-28">
|
||||
<InputField
|
||||
label="Environment Name"
|
||||
onChangeHandler={(val) => onInputChange("name", val)}
|
||||
type="varName"
|
||||
value={formInput.name}
|
||||
placeholder=""
|
||||
isRequired
|
||||
// error={error.length > 0}
|
||||
// errorText={error}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4 max-h-28">
|
||||
<InputField
|
||||
label="Environment Slug"
|
||||
onChangeHandler={(val) => onInputChange("slug", val)}
|
||||
type="varName"
|
||||
value={formInput.slug}
|
||||
placeholder=""
|
||||
isRequired
|
||||
// error={error.length > 0}
|
||||
// errorText={error}
|
||||
/>
|
||||
</div>
|
||||
<p className="mt-2 text-xs text-gray-500">
|
||||
Slugs are shorthands used in cli to access environment
|
||||
</p>
|
||||
<div className="mt-4 max-w-min">
|
||||
<Button
|
||||
onButtonPressed={() => null}
|
||||
type="submit"
|
||||
color="mineshaft"
|
||||
text={isEditMode ? "Update" : "Create"}
|
||||
active={formInput.name !== "" && formInput.slug !== ""}
|
||||
size="md"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,104 +0,0 @@
|
||||
import { Fragment, useState } from "react";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
|
||||
import Button from "../buttons/Button";
|
||||
import InputField from "../InputField";
|
||||
import { Checkbox } from "../table/Checkbox";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
closeModal: () => void;
|
||||
submitModal: (email: string, addAllUser: boolean) => void;
|
||||
workspaceName: string;
|
||||
setWorkspaceName: (workspaceName: string) => void;
|
||||
error: boolean;
|
||||
loading: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* The dialog modal for when the user wants to create a new workspace
|
||||
*/
|
||||
const AddWorkspaceDialog = ({
|
||||
isOpen,
|
||||
closeModal,
|
||||
submitModal,
|
||||
workspaceName,
|
||||
setWorkspaceName,
|
||||
error,
|
||||
loading
|
||||
}: Props) => {
|
||||
const [addAllUsers, setAddAllUsers] = useState(true);
|
||||
const submit = () => {
|
||||
submitModal(workspaceName, addAllUsers);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-[100]" onClose={closeModal}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black bg-opacity-70" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<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 as="h3" className="text-lg font-medium leading-6 text-gray-400">
|
||||
Create a new project
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-gray-500">
|
||||
This project will contain your secrets and configs.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-4 max-h-28">
|
||||
<InputField
|
||||
label="Project Name"
|
||||
onChangeHandler={setWorkspaceName}
|
||||
type="varName"
|
||||
value={workspaceName}
|
||||
placeholder=""
|
||||
isRequired
|
||||
error={error}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4 ml-1">
|
||||
<Checkbox addAllUsers={addAllUsers} setAddAllUsers={setAddAllUsers} />
|
||||
</div>
|
||||
<div className="mt-4 max-w-min">
|
||||
<Button
|
||||
onButtonPressed={submit}
|
||||
loading={loading}
|
||||
color="mineshaft"
|
||||
text="Create"
|
||||
size="md"
|
||||
/>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddWorkspaceDialog;
|
||||
@@ -1,91 +0,0 @@
|
||||
import { Fragment, useEffect, useState } from "react";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
|
||||
import InputField from "../InputField";
|
||||
|
||||
// REFACTOR: Move all these modals into one reusable one
|
||||
type Props = {
|
||||
isOpen?: boolean;
|
||||
onClose: () => void;
|
||||
title: string;
|
||||
onSubmit: () => void;
|
||||
deleteKey?: string;
|
||||
};
|
||||
|
||||
const DeleteActionModal = ({ isOpen, onClose, title, onSubmit, deleteKey }: Props) => {
|
||||
const [deleteInputField, setDeleteInputField] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
setDeleteInputField("");
|
||||
}, [isOpen]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={onClose}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-150"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black bg-opacity-70" />
|
||||
</Transition.Child>
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<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 as="h3" className="text-lg font-medium leading-6 text-gray-400">
|
||||
{title}
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-gray-500">This action is irrevertible.</p>
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
<InputField
|
||||
isRequired
|
||||
label={`Type ${deleteKey} to delete the resource`}
|
||||
onChangeHandler={(val) => setDeleteInputField(val)}
|
||||
value={deleteInputField}
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-6">
|
||||
<button
|
||||
type="button"
|
||||
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}
|
||||
disabled={Boolean(deleteKey) && deleteInputField !== deleteKey}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
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}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteActionModal;
|
||||
@@ -1,81 +0,0 @@
|
||||
import { Fragment } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
|
||||
// #TODO: USE THIS. Currently it's not. Kinda complicated to set up because of state.
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSubmit: () => void;
|
||||
};
|
||||
|
||||
export const DeleteEnvVar = ({ isOpen, onClose, onSubmit }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div>
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-[80]" onClose={() => {}}>
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div
|
||||
className="fixed inset-0 bg-black bg-opacity-70"
|
||||
onClick={onClose}
|
||||
onKeyDown={onClose}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label="Close"
|
||||
/>
|
||||
</Transition.Child>
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<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">
|
||||
{t("dashboard:sidebar.delete-key-dialog.title")}
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-bunker-300">
|
||||
{t("dashboard:sidebar.delete-key-dialog.confirm-delete-message")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-6 flex justify-start">
|
||||
<button
|
||||
type="button"
|
||||
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}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
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}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,77 +0,0 @@
|
||||
import { Fragment } from "react";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
|
||||
// #TODO: USE THIS. Currently it's not. Kinda complicated to set up because of state.
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
closeModal: () => void;
|
||||
submitModal: (userIdToBeDeleted: string) => void;
|
||||
userIdToBeDeleted: string;
|
||||
};
|
||||
|
||||
const DeleteUserDialog = ({ isOpen, closeModal, submitModal, userIdToBeDeleted }: Props) => {
|
||||
const submit = () => {
|
||||
submitModal(userIdToBeDeleted);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={closeModal}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black bg-opacity-25" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<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 as="h3" className="text-lg font-medium leading-6 text-gray-400">
|
||||
Are you sure you want to remove this user from the workspace?
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-gray-500">This action is irrevertible.</p>
|
||||
</div>
|
||||
<div className="mt-6">
|
||||
<button
|
||||
type="button"
|
||||
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}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
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}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteUserDialog;
|
||||
@@ -1,80 +0,0 @@
|
||||
import { Fragment } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
|
||||
import { useOrganization } from "@app/context";
|
||||
|
||||
// REFACTOR: Move all these modals into one reusable one
|
||||
type Props = {
|
||||
isOpen?: boolean;
|
||||
onClose: () => void;
|
||||
text: string;
|
||||
};
|
||||
|
||||
const UpgradePlanModal = ({ isOpen, onClose, text }: Props) => {
|
||||
const router = useRouter();
|
||||
const { currentOrg } = useOrganization();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={onClose}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-150"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 drop-shadow-xl" />
|
||||
</Transition.Child>
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md border border-mineshaft-500 bg-bunker p-6 pt-5 text-left align-middle shadow-xl transition-all">
|
||||
<Dialog.Title as="h3" className="text-xl font-medium leading-6 text-primary">
|
||||
Unleash Infisical's Full Power
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<p className="mb-1 text-sm text-bunker-300">{text}</p>
|
||||
<p className="text-sm text-bunker-300">
|
||||
Upgrade and get access to this, as well as to other powerful enhancements.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<button
|
||||
type="button"
|
||||
className="hover:text-semibold inline-flex justify-center rounded-md border border-transparent bg-primary px-4 py-2 text-sm font-medium text-black 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={() => router.push(`/org/${currentOrg?.id}/billing`)}
|
||||
>
|
||||
Upgrade Now
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
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-red hover:text-red focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||
onClick={onClose}
|
||||
>
|
||||
Maybe Later
|
||||
</button>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UpgradePlanModal;
|
||||
@@ -1,69 +0,0 @@
|
||||
import { faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
interface PopupProps {
|
||||
buttonText: string;
|
||||
buttonLink: string;
|
||||
titleText: string;
|
||||
emoji: string;
|
||||
textLine1: string;
|
||||
textLine2: string;
|
||||
setCheckDocsPopUpVisible: (value: boolean) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the notification that pops up at the bottom right when a user performs a certain action
|
||||
* @param {object} org
|
||||
* @param {string} org.buttonText - text of the button inside the notification
|
||||
* @param {string} org.buttonLink - where the button leads to
|
||||
* @param {string} org.titleText - the text at the top of a notification
|
||||
* @param {string} org.emoji - the emoji in the notification title
|
||||
* @param {string} org.textLine1 - first line of the text in the notification
|
||||
* @param {string} org.textLine2 - second line of the text in the notification
|
||||
* @param {string} org.setCheckDocsPopUpVisible - the functions that closes the popup
|
||||
* @returns
|
||||
*/
|
||||
const BottonRightPopup = ({
|
||||
buttonText,
|
||||
buttonLink,
|
||||
titleText,
|
||||
emoji,
|
||||
textLine1,
|
||||
textLine2,
|
||||
setCheckDocsPopUpVisible
|
||||
}: PopupProps): JSX.Element => {
|
||||
return (
|
||||
<div
|
||||
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"
|
||||
>
|
||||
<div className="flex w-full flex-row items-center justify-between border-b border-gray-600/70 px-6 pb-3">
|
||||
<div className="mr-2 mt-0.5 flex flex-row text-xl font-bold">
|
||||
<div>{titleText}</div>
|
||||
<div className="ml-2.5">{emoji}</div>
|
||||
</div>
|
||||
<button className="mt-1" onClick={() => setCheckDocsPopUpVisible(false)} type="button">
|
||||
<FontAwesomeIcon
|
||||
icon={faXmark}
|
||||
className="cursor-pointer text-2xl text-gray-400 duration-200 hover:text-red"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-4 mb-0.5 block px-6 text-gray-300 sm:inline">{textLine1}</div>
|
||||
<div className="mb-4 block px-6 sm:inline">{textLine2}</div>
|
||||
<div className="flex w-full flex-row px-6">
|
||||
{/* eslint-disable-next-line react/jsx-no-target-blank */}
|
||||
<a
|
||||
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}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
{buttonText}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BottonRightPopup;
|
||||
@@ -1,46 +0,0 @@
|
||||
import { useState } from "react";
|
||||
import { faInfo } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { Tooltip } from "@app/components/v2/Tooltip";
|
||||
|
||||
const GlobPatternExamples = () => {
|
||||
const [showTip, setShowTip] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
isOpen={showTip}
|
||||
onOpenChange={setShowTip}
|
||||
content={
|
||||
<div>
|
||||
<h4 className="mb-2">Here are some examples of glob patterns:</h4>
|
||||
<div className="ol-listStyleType">
|
||||
<li>
|
||||
<code className="text-primary">/</code> - Matches all files and directories in the
|
||||
current directory
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-primary">**/*</code> - Matches all files and directories in the
|
||||
current directory and its subdirectories
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-primary">{"/{dir1,dir2}"}</code> - Matches all files and
|
||||
directories in dir1 and dir2
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
position="right"
|
||||
className="text-xs"
|
||||
>
|
||||
<div
|
||||
className="flex h-3.5 w-3.5 items-center justify-center rounded-full border border-[1px] border-mineshaft-300"
|
||||
onMouseEnter={() => setShowTip(true)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faInfo} className="h-2 w-2" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export default GlobPatternExamples;
|
||||
@@ -1,32 +0,0 @@
|
||||
/* eslint-disable jsx-a11y/label-has-associated-control */
|
||||
type Props = {
|
||||
addAllUsers: boolean;
|
||||
setAddAllUsers: (arg: boolean) => void;
|
||||
};
|
||||
|
||||
export const Checkbox = ({ addAllUsers, setAddAllUsers }: Props) => (
|
||||
<div className="flex flex-row items-center">
|
||||
{addAllUsers === true ? (
|
||||
<input
|
||||
type="checkbox"
|
||||
className="h-4 w-4 accent-primary"
|
||||
checked
|
||||
readOnly
|
||||
onClick={() => setAddAllUsers(!addAllUsers)}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
onKeyDown={() => setAddAllUsers(!addAllUsers)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label="add all users"
|
||||
className="h-4 w-4 rounded-sm border border-gray-600 bg-bunker"
|
||||
onClick={() => setAddAllUsers(!addAllUsers)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<label className="ml-2 text-sm text-gray-500">
|
||||
Add all members of my organization to this project.
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
Reference in New Issue
Block a user