diff --git a/frontend/src/components/v2/Input/Input.tsx b/frontend/src/components/v2/Input/Input.tsx
index 9263a4aad..5dd11642d 100644
--- a/frontend/src/components/v2/Input/Input.tsx
+++ b/frontend/src/components/v2/Input/Input.tsx
@@ -13,7 +13,7 @@ type Props = {
};
const inputVariants = cva(
- "input w-full py-[0.375rem] text-gray-400 placeholder:text-sm placeholder-gray-500 placeholder-opacity-50 outline-none focus:ring-2 hover:ring-[0.05rem] hover:ring-bunker-400/60 duration-100",
+ "input w-full py-[0.375rem] text-gray-400 placeholder:text-sm placeholder-gray-500 placeholder-opacity-50 outline-none focus:ring-2 hover:ring-bunker-400/60 duration-100",
{
variants: {
size: {
diff --git a/frontend/src/hooks/useSyntaxHighlight.tsx b/frontend/src/hooks/useSyntaxHighlight.tsx
index 325917c83..16ce3d46c 100644
--- a/frontend/src/hooks/useSyntaxHighlight.tsx
+++ b/frontend/src/hooks/useSyntaxHighlight.tsx
@@ -7,6 +7,10 @@ const REGEX = /([$]{.*?})/g;
export const useSyntaxHighlight = () => {
const syntaxHighlight = useCallback((text: string, isHidden?: boolean) => {
if (isHidden) {
+ if (!text) {
+ return "EMPTY";
+ }
+
return text
.split("")
.slice(0, 200)
diff --git a/frontend/src/layouts/AppLayout/components/NavBar/NavBar.tsx b/frontend/src/layouts/AppLayout/components/NavBar/NavBar.tsx
new file mode 100644
index 000000000..b263a25ff
--- /dev/null
+++ b/frontend/src/layouts/AppLayout/components/NavBar/NavBar.tsx
@@ -0,0 +1,359 @@
+/* eslint-disable jsx-a11y/anchor-is-valid */
+/* eslint-disable react/jsx-key */
+import { Fragment, useMemo } from "react";
+import { useTranslation } from "react-i18next";
+import Image from "next/image";
+import Link from "next/link";
+import { useRouter } from "next/router";
+import { faGithub, faSlack } from "@fortawesome/free-brands-svg-icons";
+import { faCircleQuestion } from "@fortawesome/free-regular-svg-icons";
+import {
+ faAngleDown,
+ faBook,
+ faCoins,
+ faEnvelope,
+ faGear,
+ faPlus,
+ faRightFromBracket
+} from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Menu, Transition } from "@headlessui/react";
+import {TFunction} from "i18next";
+
+import guidGenerator from "@app/components/utilities/randomId";
+import { useOrganization, useSubscription,useUser } from "@app/context";
+import { useLogoutUser } from "@app/hooks/api";
+
+const supportOptions = (t: TFunction) => [
+ [
+ ,
+ t("nav.support.slack"),
+ "https://infisical.com/slack"
+ ],
+ [
+ ,
+ t("nav.support.docs"),
+ "https://infisical.com/docs/documentation/getting-started/introduction"
+ ],
+ [
+ ,
+ t("nav.support.issue"),
+ "https://github.com/Infisical/infisical-cli/issues"
+ ],
+ [
+ ,
+ t("nav.support.email"),
+ "mailto:support@infisical.com"
+ ]
+];
+
+export interface ICurrentOrg {
+ name: string;
+}
+
+export interface IUser {
+ firstName: string;
+ lastName: string;
+ email: string;
+}
+
+/**
+ * This is the navigation bar in the main app.
+ * It has two main components: support options and user menu (inlcudes billing, logout, org/user settings)
+ * @returns NavBar
+ */
+export const Navbar = () => {
+ const router = useRouter();
+ const { subscription } = useSubscription();
+
+ const { currentOrg, orgs } = useOrganization();
+ const { user } = useUser();
+
+ const logout = useLogoutUser();
+
+ const { t } = useTranslation();
+
+ // remove this memo
+ const supportOptionsList = useMemo(() => supportOptions(t), [t]);
+
+ const closeApp = async () => {
+ try {
+ console.log("Logging out...")
+ await logout.mutateAsync();
+ localStorage.removeItem("protectedKey");
+ localStorage.removeItem("protectedKeyIV");
+ localStorage.removeItem("protectedKeyTag");
+ localStorage.removeItem("publicKey");
+ localStorage.removeItem("encryptedPrivateKey");
+ localStorage.removeItem("iv");
+ localStorage.removeItem("tag");
+ localStorage.removeItem("PRIVATE_KEY");
+ localStorage.removeItem("orgData.id");
+ localStorage.removeItem("projectData.id");
+ router.push("/login");
+ } catch (error) {
+ console.error(error);
+ }
+ };
+
+ function formatPlanSlug(slug: string) {
+ return slug
+ .replace(/(\b[a-z])/g, match => match.toUpperCase())
+ .replace(/-/g, " ");
+ }
+
+ const calculateRemainingDays = (date: number) => {
+ const now = new Date();
+ const endDate = new Date(date * 1000);
+
+ const differenceInTime = endDate.getTime() - now.getTime();
+ const differenceInDays = Math.ceil(differenceInTime / (1000 * 3600 * 24));
+
+ return differenceInDays;
+ }
+
+ const formatDate = (date: number) => {
+ const endDate = new Date(date * 1000);
+ const day: number = endDate.getDate();
+ const month: number = endDate.getMonth() + 1;
+ const year: number = endDate.getFullYear();
+
+ const formattedDate: string = `${day}/${month}/${year}`;
+ const remainingDays: number = calculateRemainingDays(date);
+
+ return {
+ formattedDate,
+ remainingDays
+ };
+ }
+
+ return (
+
+
+
+
+
+
+ Docs
+
+
+
+
+
+
+
+
+
+ {supportOptionsList.map(([icon, text, url]) => (
+
+
+
+ ))}
+
+
+
+
+
+
+ {user?.firstName} {user?.lastName}
+
+
+
+
+
+
+
+ {t("nav.user.signed-in-as")}
+
+
null}
+ role="button"
+ tabIndex={0}
+ onClick={() => router.push(`/settings/personal/${router.query.id}`)}
+ className="mx-1 my-1 flex cursor-pointer flex-row items-center rounded-md px-1 hover:bg-white/5"
+ >
+
+ {user?.firstName?.charAt(0)}
+
+
+
+
+ {" "}
+ {user?.firstName} {user?.lastName}
+
+
{user?.email}
+
+
+
+
+
+
+
+ {t("nav.user.current-organization")}
+
+
null}
+ role="button"
+ tabIndex={0}
+ onClick={() => router.push(`/settings/org/${router.query.id}`)}
+ className="mt-2 flex cursor-pointer flex-row items-center rounded-md px-2 py-1 hover:bg-white/5"
+ >
+
+ {currentOrg?.name?.charAt(0)}
+
+
+
+ {subscription && subscription.slug !== null && (
+
+ null}
+ role="button"
+ tabIndex={0}
+ onClick={() => router.push(`/settings/billing/${router.query.id}`)}
+ className="relative mt-1 flex cursor-pointer select-none justify-start rounded-md py-2 px-2 text-gray-400 duration-200 hover:bg-white/5 hover:text-gray-200"
+ >
+
+
{t("nav.user.usage-billing")}
+
+
+ )}
+
+ null}
+ role="button"
+ tabIndex={0}
+ onClick={() => router.push(`/settings/org/${router.query.id}?invite`)}
+ className="relative mt-1 flex cursor-pointer select-none justify-start rounded-md py-2 pl-10 pr-4 text-gray-400 duration-200 hover:bg-primary/100 hover:font-semibold hover:text-black"
+ >
+
+
+
+
{t("nav.user.invite")}
+
+
+
+ {orgs && orgs?.length > 1 && (
+
+
+ {t("nav.user.other-organizations")}
+
+
+ {orgs
+ ?.filter((org: { _id: string }) => org._id !== currentOrg?._id)
+ .map((org: { _id: string; name: string }) => (
+
null}
+ role="button"
+ tabIndex={0}
+ key={guidGenerator()}
+ onClick={() => {
+ localStorage.setItem("orgData.id", org._id);
+ router.reload();
+ }}
+ className="flex w-full cursor-pointer flex-row items-center justify-start rounded-md p-1.5 hover:bg-white/5"
+ >
+
+ {org.name.charAt(0)}
+
+
+
+ ))}
+
+
+ )}
+
+
+ {({ active }) => (
+
+
+
+ {t("common.logout")}
+
+
+ )}
+
+
+
+
+
+
+
+ {subscription && subscription.status === "trialing" && subscription.trial_end && (
+
+
+ {`Currently trialing the ${formatPlanSlug(subscription.slug)} plan until ${formatDate(subscription.trial_end).formattedDate} - ${formatDate(subscription.trial_end).remainingDays} day(s) left. `}
+ Add a card to avoid being downgraded to the Starter plan afterward →
+
+
+ )}
+
+ );
+};
\ No newline at end of file
diff --git a/frontend/src/views/DashboardPage/DashboardPage.tsx b/frontend/src/views/DashboardPage/DashboardPage.tsx
index f77e0211a..1a3b0f73e 100644
--- a/frontend/src/views/DashboardPage/DashboardPage.tsx
+++ b/frontend/src/views/DashboardPage/DashboardPage.tsx
@@ -340,7 +340,10 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
}
};
- const onAppendSecret = () => append(DEFAULT_SECRET_VALUE);
+ const onAppendSecret = () => {
+ setSearchFilter("");
+ append(DEFAULT_SECRET_VALUE)
+ };
const onSaveSecret = async ({ secrets: userSec = [], isSnapshotMode }: FormData) => {
if (isSnapshotMode) {
@@ -700,6 +703,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
});
}
prepend(DEFAULT_SECRET_VALUE, { shouldFocus: false });
+ setSearchFilter("");
}}
isDisabled={isReadOnly || isRollbackMode}
variant="outline_bg"
diff --git a/frontend/src/views/DashboardPage/components/EnvComparisonRow/EnvComparisonRow.tsx b/frontend/src/views/DashboardPage/components/EnvComparisonRow/EnvComparisonRow.tsx
index 201012fa1..6c86b3fce 100644
--- a/frontend/src/views/DashboardPage/components/EnvComparisonRow/EnvComparisonRow.tsx
+++ b/frontend/src/views/DashboardPage/components/EnvComparisonRow/EnvComparisonRow.tsx
@@ -4,8 +4,7 @@ import { faEye, faEyeSlash, faKey, faMinus } from "@fortawesome/free-solid-svg-i
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useSyntaxHighlight } from "@app/hooks";
-
-import { useToggle } from "~/hooks/useToggle";
+import { useToggle } from "@app/hooks/useToggle";
type Props = {
secrets: any[] | undefined;
@@ -71,6 +70,8 @@ const DashboardInput = ({
ref={ref}
className={`absolute top-1.5 left-3.5 z-10 overflow-auto font-mono text-sm transition-all no-scrollbar ${
isOverridden && "text-primary-300"
+ } ${
+ (value || "") === "" && "text-mineshaft-400"
}`}
style={{ height: `${maxMultilineHeight}px`, width: "calc(100% - 12px)" }}
>
diff --git a/frontend/src/views/DashboardPage/components/SecretInputRow/MaskedInput.tsx b/frontend/src/views/DashboardPage/components/SecretInputRow/MaskedInput.tsx
index a41589a1f..d4663556d 100644
--- a/frontend/src/views/DashboardPage/components/SecretInputRow/MaskedInput.tsx
+++ b/frontend/src/views/DashboardPage/components/SecretInputRow/MaskedInput.tsx
@@ -94,6 +94,8 @@ export const MaskedInput = ({ isReadOnly, isSecretValueHidden, index, isOverridd
ref={ref}
className={`absolute top-1.5 left-3.5 z-10 w-full overflow-auto font-mono text-sm transition-all no-scrollbar ${
isOverridden && "text-primary-300"
+ } ${
+ (value || "") === "" && "text-mineshaft-400"
}`}
style={{ height: `${maxMultilineHeight}px`, width: "calc(100% - 12px)" }}
>
diff --git a/frontend/src/views/Settings/BillingSettingsPage/components/BillingCloudTab/PreviewSection.tsx b/frontend/src/views/Settings/BillingSettingsPage/components/BillingCloudTab/PreviewSection.tsx
index e1a39ca06..23a4be24d 100644
--- a/frontend/src/views/Settings/BillingSettingsPage/components/BillingCloudTab/PreviewSection.tsx
+++ b/frontend/src/views/Settings/BillingSettingsPage/components/BillingCloudTab/PreviewSection.tsx
@@ -45,7 +45,7 @@ export const PreviewSection = () => {
return (
{!isSubscriptionLoading && subscription?.slug !== "enterprise" && subscription?.slug !== "pro" && subscription?.slug !== "pro-annual" && (
-
+
Become Infisical
Unlimited members, projects, RBAC, smart alerts, and so much more