/* eslint-disable no-nested-ternary */ /* eslint-disable no-unexpected-multiline */ /* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable vars-on-top */ /* eslint-disable no-var */ /* eslint-disable func-names */ import { useEffect, useMemo } from "react"; import { Controller, useForm } from "react-hook-form"; 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 { faAngleDown, faArrowLeft, faArrowUpRightFromSquare, faBook, faCheck, faEnvelope, faInfinity, faInfo, faMobile, faPlus, faQuestion } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { yupResolver } from "@hookform/resolvers/yup"; import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu"; import { twMerge } from "tailwind-merge"; import * as yup from "yup"; import { createNotification } from "@app/components/notifications"; import { OrgPermissionCan } from "@app/components/permissions"; import { tempLocalStorage } from "@app/components/utilities/checks/tempLocalStorage"; import { Button, Checkbox, DropdownMenu, DropdownMenuContent, DropdownMenuItem, FormControl, Input, Menu, MenuItem, Modal, ModalContent, Select, SelectItem, UpgradePlanModal } from "@app/components/v2"; import { UpgradeOverlay } from "@app/components/v2/UpgradeOverlay"; import { OrgPermissionActions, OrgPermissionSubjects, useOrganization, useSubscription, useUser, useWorkspace } from "@app/context"; import { usePopUp } from "@app/hooks"; import { fetchOrgUsers, useAddUserToWsNonE2EE, useCreateWorkspace, useGetAccessRequestsCount, useGetOrgTrialUrl, useGetSecretApprovalRequestCount, useGetUserAction, useLogoutUser, useRegisterUserAction, useSelectOrganization } from "@app/hooks/api"; import { navigateUserToOrg } from "@app/views/Login/Login.utils"; import { CreateOrgModal } from "@app/views/Org/components"; interface LayoutProps { children: React.ReactNode; } const supportOptions = [ [ , "Support Forum", "https://infisical.com/slack" ], [ , "Read Docs", "https://infisical.com/docs/documentation/getting-started/introduction" ], [ , "GitHub Issues", "https://github.com/Infisical/infisical/issues" ], [ , "Email Support", "mailto:support@infisical.com" ] ]; const formSchema = yup.object({ name: yup .string() .required() .label("Project Name") .trim() .max(64, "Too long, maximum length is 64 characters"), addMembers: yup.bool().required().label("Add Members") }); type TAddProjectFormData = yup.InferType; export const AppLayout = ({ children }: LayoutProps) => { const router = useRouter(); const { mutateAsync } = useGetOrgTrialUrl(); const { workspaces, currentWorkspace } = useWorkspace(); const { orgs, currentOrg } = useOrganization(); const { user } = useUser(); const { subscription } = useSubscription(); const workspaceId = currentWorkspace?.id || ""; const projectSlug = currentWorkspace?.slug || ""; const { data: updateClosed } = useGetUserAction("december_update_closed"); const { data: secretApprovalReqCount } = useGetSecretApprovalRequestCount({ workspaceId }); const { data: accessApprovalRequestCount } = useGetAccessRequestsCount({ projectSlug }); const pendingRequestsCount = useMemo(() => { return (secretApprovalReqCount?.open || 0) + (accessApprovalRequestCount?.pendingCount || 0); }, [secretApprovalReqCount, accessApprovalRequestCount]); const isAddingProjectsAllowed = subscription?.workspaceLimit ? subscription.workspacesUsed < subscription.workspaceLimit : true; const createWs = useCreateWorkspace(); const addUsersToProject = useAddUserToWsNonE2EE(); const infisicalPlatformVersion = process.env.NEXT_PUBLIC_INFISICAL_PLATFORM_VERSION; const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ "addNewWs", "upgradePlan", "createOrg" ] as const); const { control, formState: { isSubmitting }, reset, handleSubmit } = useForm({ resolver: yupResolver(formSchema) }); const { t } = useTranslation(); const registerUserAction = useRegisterUserAction(); const { mutateAsync: selectOrganization } = useSelectOrganization(); const closeUpdate = async () => { await registerUserAction.mutateAsync("december_update_closed"); }; const logout = useLogoutUser(); const logOutUser = async () => { try { console.log("Logging out..."); await logout.mutateAsync(); router.push("/login"); } catch (error) { console.error(error); } }; const changeOrg = async (orgId: string) => { await selectOrganization({ organizationId: orgId }); await navigateUserToOrg(router, orgId); }; // TODO(akhilmhdh): This entire logic will be rechecked and will try to avoid // Placing the localstorage as much as possible // Wait till tony integrates the azure and its launched useEffect(() => { // Put a user in an org if they're not in one yet const putUserInOrg = async () => { if (tempLocalStorage("orgData.id") === "" && orgs?.[0]?.id) { localStorage.setItem("orgData.id", orgs?.[0]?.id); } if ( currentOrg && ((workspaces?.length === 0 && router.asPath.includes("project")) || router.asPath.includes("/project/undefined") || (!orgs?.map((org) => org.id)?.includes(router.query.id as string) && !router.asPath.includes("project") && !router.asPath.includes("personal") && !router.asPath.includes("integration"))) ) { router.push(`/org/${currentOrg?.id}/overview`); } // else if (!router.asPath.includes("org") && !router.asPath.includes("project") && !router.asPath.includes("integrations") && !router.asPath.includes("personal-settings")) { // const pathSegments = router.asPath.split("/").filter((segment) => segment.length > 0); // let intendedWorkspaceId; // if (pathSegments.length >= 2 && pathSegments[0] === "dashboard") { // [, intendedWorkspaceId] = pathSegments; // } else if (pathSegments.length >= 3 && pathSegments[0] === "settings") { // [, , intendedWorkspaceId] = pathSegments; // } else { // const lastPathSegments = router.asPath.split("/").pop(); // if (lastPathSegments !== undefined) { // [intendedWorkspaceId] = lastPathSegments.split("?"); // } // } // if (!intendedWorkspaceId) return; // if (!["callback", "create", "authorize"].includes(intendedWorkspaceId)) { // localStorage.setItem("projectData.id", intendedWorkspaceId); // } // } }; putUserInOrg(); }, [router.query.id]); const onCreateProject = async ({ name, addMembers }: TAddProjectFormData) => { // type check if (!currentOrg) return; if (!user) return; try { const { data: { project: { id: newProjectId } } } = await createWs.mutateAsync({ projectName: name }); if (addMembers) { const orgUsers = await fetchOrgUsers(currentOrg.id); await addUsersToProject.mutateAsync({ usernames: orgUsers .map((member) => member.user.username) .filter((username) => username !== user.username), projectId: newProjectId }); } // eslint-disable-next-line no-promise-executor-return -- We do this because the function returns too fast, which sometimes causes an error when the user is redirected. await new Promise((resolve) => setTimeout(resolve, 2_000)); // eslint-disable-next-line no-promise-executor-return -- We do this because the function returns too fast, which sometimes causes an error when the user is redirected. await new Promise((resolve) => setTimeout(resolve, 2_000)); createNotification({ text: "Workspace created", type: "success" }); handlePopUpClose("addNewWs"); router.push(`/project/${newProjectId}/secrets/overview`); } catch (err) { console.error(err); createNotification({ text: "Failed to create workspace", type: "error" }); } }; return ( <>
{ handlePopUpToggle("addNewWs", isModalOpen); reset(); }} >
( )} />
( {(isAllowed) => (
Add all members of my organization to this project
)}
)} />
handlePopUpToggle("upgradePlan", isOpen)} text="You have exceeded the number of projects allowed on the free plan." /> handlePopUpToggle("createOrg", false)} />
{children}

{` ${t("common.no-mobile")} `}

); };