diff --git a/frontend/components/basic/layout.js b/frontend/components/basic/layout.js index 28557d853..366368d9a 100644 --- a/frontend/components/basic/layout.js +++ b/frontend/components/basic/layout.js @@ -24,6 +24,8 @@ import { faPlus } from "@fortawesome/free-solid-svg-icons"; import { decryptAssymmetric, encryptAssymmetric } from "../utilities/crypto"; import Button from "./buttons/Button"; +import useTranslation from "next-translate/useTranslation"; + export default function Layout({ children }) { const router = useRouter(); const [workspaceList, setWorkspaceList] = useState([]); @@ -34,6 +36,8 @@ export default function Layout({ children }) { const [loading, setLoading] = useState(false); const [error, setError] = useState(false); + const { t } = useTranslation(); + function closeModal() { setIsOpen(false); } @@ -48,9 +52,7 @@ export default function Layout({ children }) { setLoading(true); setTimeout(() => setLoading(false), 1500); const workspaces = await getWorkspaces(); - const currentWorkspaces = workspaces.map( - (workspace) => workspace.name - ); + const currentWorkspaces = workspaces.map((workspace) => workspace.name); if (!currentWorkspaces.includes(workspaceName)) { const newWorkspace = await createWorkspace( workspaceName, @@ -104,7 +106,7 @@ export default function Layout({ children }) { setIsOpen(false); setNewWorkspaceName(""); } else { - setError("A project with this name already exists."); + setError(t("common:error_project-already-exists")); setLoading(false); } } @@ -119,29 +121,32 @@ export default function Layout({ children }) { "/dashboard/" + workspaceMapping[workspaceSelected] + "?Development", - title: "Secrets", + title: t("common:nav.menu.secrets"), emoji: , }, { href: "/users/" + workspaceMapping[workspaceSelected], - title: "Members", + title: t("common:nav.menu.members"), emoji: , }, { href: "/integrations/" + workspaceMapping[workspaceSelected], - title: "Integrations", + title: t("common:nav.menu.integrations"), emoji: , }, { href: "/settings/project/" + workspaceMapping[workspaceSelected], - title: "Project Settings", + title: t("common:nav.menu.project-settings"), emoji: , }, ]; useEffect(async () => { // Put a user in a workspace if they're not in one yet - if (localStorage.getItem("orgData.id") == null || localStorage.getItem("orgData.id") == "") { + if ( + localStorage.getItem("orgData.id") == null || + localStorage.getItem("orgData.id") == "" + ) { const userOrgs = await getOrganizations(); localStorage.setItem("orgData.id", userOrgs[0]._id); } @@ -150,7 +155,11 @@ export default function Layout({ children }) { orgId: localStorage.getItem("orgData.id"), }); let userWorkspaces = orgUserProjects; - if (userWorkspaces.length == 0 && (router.asPath != "/noprojects" && !router.asPath.includes("settings"))) { + if ( + userWorkspaces.length == 0 && + router.asPath != "/noprojects" && + !router.asPath.includes("settings") + ) { router.push("/noprojects"); } else if (router.asPath != "/noprojects") { const intendedWorkspaceId = router.asPath @@ -158,7 +167,9 @@ export default function Layout({ children }) { [router.asPath.split("/").length - 1].split("?")[0]; // If a user is not a member of a workspace they are trying to access, just push them to one of theirs - if (intendedWorkspaceId != "heroku" && !userWorkspaces + if ( + intendedWorkspaceId != "heroku" && + !userWorkspaces .map((workspace) => workspace._id) .includes(intendedWorkspaceId) ) { @@ -207,7 +218,10 @@ export default function Layout({ children }) { workspaceMapping[workspaceSelected] + "?Development" ); - localStorage.setItem("projectData.id", workspaceMapping[workspaceSelected]) + localStorage.setItem( + "projectData.id", + workspaceMapping[workspaceSelected] + ); } } catch (error) { console.log(error); @@ -224,10 +238,10 @@ export default function Layout({ children }) {
- PROJECT + {t("common:nav.menu.project")}
- {workspaceList.length>0 - ? 0 ? ( + - :
    - {workspaceList.length > 0 && menuItems.map(({ href, title, emoji }) => ( -
  • - {router.asPath.split("/")[1] === - href.split("/")[1] && - (["project", "billing", "org", "personal"].includes( - router.asPath.split("/")[2] - ) - ? router.asPath.split("/")[2] === - href.split("/")[2] - : true) ? ( -
    -
    -

    - {emoji} -

    - {title} -
    - ) : router.asPath == "/noprojects" ? ( -
    -

    - {emoji} -

    - {title} -
    - ) : ( - + {workspaceList.length > 0 && + menuItems.map(({ href, title, emoji }) => ( +
  • + {router.asPath.split("/")[1] === + href.split("/")[1] && + ([ + "project", + "billing", + "org", + "personal", + ].includes( + router.asPath.split("/")[2] + ) + ? router.asPath.split( + "/" + )[2] === href.split("/")[2] + : true) ? (
    +
    +

    + {emoji} +

    + {title} +
    + ) : router.asPath == + "/noprojects" ? ( +

    {emoji}

    {title}
    - - )} -
  • - ))} + ) : ( + +
    +

    + {emoji} +

    + {title} +
    + + )} + + ))}
@@ -308,9 +331,7 @@ export default function Layout({ children }) { className="text-gray-300 text-7xl mb-8" />

- {" "} - To use Infisical, please log in through a device with larger - dimensions.{" "} + {` ${t("common:need-login")} `}

diff --git a/frontend/components/navigation/NavBarDashboard.js b/frontend/components/navigation/NavBarDashboard.js index e73a61268..d4429b00e 100644 --- a/frontend/components/navigation/NavBarDashboard.js +++ b/frontend/components/navigation/NavBarDashboard.js @@ -1,4 +1,4 @@ -import React, { useState, Fragment, useEffect } from "react"; +import React, { useState, Fragment, useEffect, useMemo } from "react"; import { useRouter } from "next/router"; import Image from "next/image"; @@ -12,8 +12,8 @@ import { faCoins, faRightFromBracket, faEnvelope, - faPlus, - faAngleDown + faPlus, + faAngleDown, } from "@fortawesome/free-solid-svg-icons"; import { faSlack, faGithub } from "@fortawesome/free-brands-svg-icons"; import { Menu, Transition } from "@headlessui/react"; @@ -21,26 +21,30 @@ import getUser from "../../pages/api/user/getUser"; import getOrganizations from "../../pages/api/organization/getOrgs"; import getOrganization from "../../pages/api/organization/GetOrg"; import guidGenerator from "../utilities/randomId"; +import useTranslation from "next-translate/useTranslation"; -const supportOptions = [ +/** + * @param {(key: string) => string} t + */ +const supportOptions = (t) => [ [ , - "[NEW] Join Slack Forum", + t("common:nav.support.slack"), "https://join.slack.com/t/infisical/shared_invite/zt-1dgg63ln8-G7PCNJdCymAT9YF3j1ewVA", ], [ , - "Read Docs", + t("common:nav.support.docs"), "https://infisical.com/docs/getting-started/introduction", ], [ , - "Open a GitHub Issue", + t("common:nav.support.issue"), "https://github.com/Infisical/infisical-cli/issues", ], [ , - "Send us an Email", + t("common:nav.support.email"), "mailto:support@infisical.com", ], ]; @@ -51,6 +55,10 @@ export default function Navbar({ onButtonPressed }) { const [orgs, setOrgs] = useState([]); const [currentOrg, setCurrentOrg] = useState([]); + const { t } = useTranslation("common"); + + const supportOptionsList = useMemo(() => supportOptions(t), [t]); + useEffect(async () => { const userData = await getUser(); setUser(userData); @@ -87,7 +95,7 @@ export default function Navbar({ onButtonPressed }) {
-
+
- {supportOptions.map((option) => ( + {supportOptionsList.map(([icon, text, url]) => (
- {option[0]} -
- {option[1]} -
+ {icon} +
{text}
))} @@ -128,7 +134,10 @@ export default function Navbar({ onButtonPressed }) {
{user?.firstName} {user?.lastName} - +
- SIGNED IN AS + {t("common:nav.user.signed-in-as")}
-
router.push( - "/settings/personal/" + router.query.id + "/settings/personal/" + + router.query.id ) } - className="flex flex-row items-center px-1 mx-1 my-1 hover:bg-white/5 cursor-pointer rounded-md"> + className="flex flex-row items-center px-1 mx-1 my-1 hover:bg-white/5 cursor-pointer rounded-md" + >
{user?.firstName?.charAt(0)}
@@ -159,7 +170,8 @@ export default function Navbar({ onButtonPressed }) {

{" "} - {user?.firstName} {user?.lastName} + {user?.firstName}{" "} + {user?.lastName}

{" "} @@ -175,7 +187,7 @@ export default function Navbar({ onButtonPressed }) {

- CURRENT ORGANIZATION + {t("common:nav.user.current-organization")}
@@ -216,7 +228,7 @@ export default function Navbar({ onButtonPressed }) { icon={faCoins} />
- Usage & Billing + {t("common:nav.user.usage-billing")}
@@ -235,51 +247,58 @@ export default function Navbar({ onButtonPressed }) { className="relative flex justify-start cursor-pointer select-none py-2 pl-10 pr-4 rounded-md text-gray-400 hover:bg-primary/100 duration-200 hover:text-black hover:font-semibold mt-1" > - +
- Invite Members + {t("common:nav.user.invite")}
- {orgs?.length > 1 &&
-
- OTHER ORGANIZATIONS -
-
- {orgs - .filter( - (org) => - org._id != - localStorage.getItem( - "orgData.id" - ) - ) - .map((org) => ( -
{ - localStorage.setItem( - "orgData.id", - org._id - ); - router.reload(); - }} - className="flex flex-row justify-start items-center hover:bg-white/5 w-full p-1.5 cursor-pointer rounded-md" - > -
- {org.name.charAt(0)} + {orgs?.length > 1 && ( +
+
+ {t( + "common:nav.user.other-organizations" + )} +
+
+ {orgs + .filter( + (org) => + org._id != + localStorage.getItem( + "orgData.id" + ) + ) + .map((org) => ( +
{ + localStorage.setItem( + "orgData.id", + org._id + ); + router.reload(); + }} + className="flex flex-row justify-start items-center hover:bg-white/5 w-full p-1.5 cursor-pointer rounded-md" + > +
+ {org.name.charAt(0)} +
+
+

+ {org.name} +

+
-
-

- {org.name} -

-
-
- ))} + ))} +
-
} + )}
{({ active }) => ( @@ -296,7 +315,7 @@ export default function Navbar({ onButtonPressed }) { className="text-lg ml-1.5 mr-3" icon={faRightFromBracket} /> - Log Out + {t("common:logout")}
)} diff --git a/frontend/locales/en-US/common.json b/frontend/locales/en-US/common.json index d6592fe7e..a50f0c204 100644 --- a/frontend/locales/en-US/common.json +++ b/frontend/locales/en-US/common.json @@ -11,11 +11,36 @@ "og-description": "Infisical a simple end-to-end encrypted platform that enables teams to sync and manage API-keys and environemntal variables. Works with Node.js, Next.js, Gatsby, Nest.js..." } }, + "nav": { + "support": { + "slack": "[NEW] Join Slack Forum", + "docs": "Read Docs", + "issue": "Open a Github Issue", + "email": "Send us an Email" + }, + "user": { + "signed-in-as": "SIGNED IN AS", + "current-organization": "CURRENT ORGANIZATION", + "usage-billing": "Usage & Billing", + "invite": "Invite Members", + "other-organizations": "OTHER ORGANIZATION" + }, + "menu": { + "project": "PROJECT", + "secrets": "Secrets", + "members": "Members", + "integrations": "Integrations", + "project-settings": "Project Settings" + } + }, + "error_project-already-exists": "A project with this name already exists.", + "need-login": " To use Infisical, please log in through a device with larger dimensions. ", "email": "Email", "password": "Password", "verify": "Verify", "first-name": "First Name", "last-name": "Last Name", + "logout": "Log Out", "validate-required": "Please input your {{name}}", "password-validate": "Password should contain at least:", "password-validate-length": "14 characters",