diff --git a/frontend/src/layouts/OrganizationLayout/components/NavBar/Navbar.tsx b/frontend/src/layouts/OrganizationLayout/components/NavBar/Navbar.tsx index c664f7aec..e14a8a01c 100644 --- a/frontend/src/layouts/OrganizationLayout/components/NavBar/Navbar.tsx +++ b/frontend/src/layouts/OrganizationLayout/components/NavBar/Navbar.tsx @@ -198,10 +198,12 @@ export const Navbar = () => { const handleOrgSelection = async ({ organizationId, subOrganizationId, + navigateTo, onSuccess }: { organizationId?: string; subOrganizationId?: string; + navigateTo?: string; onSuccess?: () => void | Promise; }) => { if (!organizationId && !subOrganizationId) return; @@ -234,7 +236,7 @@ export const Navbar = () => { queryClient.removeQueries({ queryKey: projectKeys.getAllUserProjects() }); await router.invalidate(); - await navigateUserToOrg(navigate, targetId); + await navigateUserToOrg({ navigate, organizationId: targetId, navigateTo }); queryClient.removeQueries({ queryKey: subOrgQuery.queryKey }); if (onSuccess) { @@ -243,21 +245,38 @@ export const Navbar = () => { }; const handleNavigateToRootOrgBilling = async () => { - const navigateToBilling = async () => { + const navigateToBilling = () => { navigate({ to: "/organizations/$orgId/billing", params: { orgId: rootOrg.id } }); + }; + + const onSuccess = () => { setShowCardDeclinedModal(false); }; - if (currentOrg.id !== rootOrg.id) { - await handleOrgSelection({ organizationId: rootOrg.id, onSuccess: navigateToBilling }); + if (isSubOrganization) { + await handleOrgSelection({ organizationId: rootOrg.id, onSuccess }); } else { await navigateToBilling(); } }; + const handleNavigateToAdminConsole = async () => { + const navigateToAdminConsole = () => { + navigate({ + to: "/admin" + }); + }; + + if (isSubOrganization) { + await handleOrgSelection({ organizationId: rootOrg.id, navigateTo: "/admin" }); + } else { + navigateToAdminConsole(); + } + }; + const { mutateAsync } = useGetOrgTrialUrl(); const logout = useLogoutUser(); @@ -368,14 +387,14 @@ export const Navbar = () => { + + +
+ + + +
+
+ - New Sub-Organization - - - +
+ Sub-Organizations +
+ {subOrganizations.map((subOrg) => ( + handleOrgSelection({ subOrganizationId: subOrg.id })} + className="cursor-pointer font-normal" + key={subOrg.id} + > +
+ {currentOrg?.id === subOrg.id && ( + + )} +

{subOrg.name}

+
+
+ ))} + {Boolean(subOrganizations.length && !isSubOrganization) && ( +
+ )} + {!isSubOrganization && ( + } + onClick={() => setShowSubOrgForm(true)} + > + New Sub-Organization + + )} + + +
)} {isProjectScope && ( @@ -618,6 +661,7 @@ export const Navbar = () => { Server Console diff --git a/frontend/src/layouts/OrganizationLayout/components/NavBar/NewSubOrganizationForm.tsx b/frontend/src/layouts/OrganizationLayout/components/NavBar/NewSubOrganizationForm.tsx index dbaa6d833..282b31b44 100644 --- a/frontend/src/layouts/OrganizationLayout/components/NavBar/NewSubOrganizationForm.tsx +++ b/frontend/src/layouts/OrganizationLayout/components/NavBar/NewSubOrganizationForm.tsx @@ -1,12 +1,16 @@ import { Controller, useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; +import { useQueryClient } from "@tanstack/react-query"; import { useNavigate, useRouter } from "@tanstack/react-router"; import { z } from "zod"; import { createNotification } from "@app/components/notifications"; +import SecurityClient from "@app/components/utilities/SecurityClient"; import { Button, FormControl, Input } from "@app/components/v2"; -import { useCreateSubOrganization } from "@app/hooks/api"; +import { projectKeys, subOrganizationsQuery, useCreateSubOrganization } from "@app/hooks/api"; +import { authKeys, selectOrganization } from "@app/hooks/api/auth/queries"; import { slugSchema } from "@app/lib/schemas"; +import { navigateUserToOrg } from "@app/pages/auth/LoginPage/Login.utils"; type ContentProps = { onClose: () => void; @@ -20,6 +24,8 @@ type FormData = z.infer; export const NewSubOrganizationForm = ({ onClose }: ContentProps) => { const createSubOrg = useCreateSubOrganization(); + const subOrgQuery = subOrganizationsQuery.list({ limit: 500, isAccessible: true }); + const queryClient = useQueryClient(); const { handleSubmit, @@ -46,11 +52,18 @@ export const NewSubOrganizationForm = ({ onClose }: ContentProps) => { }); onClose(); - navigate({ - to: "/organizations/$orgId/projects", - params: { orgId: organization.id } + const { token } = await selectOrganization({ + subOrganizationId: organization.id }); + + SecurityClient.setToken(token); + SecurityClient.setProviderAuthToken(""); + queryClient.removeQueries({ queryKey: authKeys.getAuthToken }); + queryClient.removeQueries({ queryKey: projectKeys.getAllUserProjects() }); + await router.invalidate({ sync: true }).catch(() => null); + queryClient.removeQueries({ queryKey: subOrgQuery.queryKey }); + await navigateUserToOrg({ navigate, organizationId: organization.id }); }; return ( diff --git a/frontend/src/pages/auth/LoginPage/Login.utils.tsx b/frontend/src/pages/auth/LoginPage/Login.utils.tsx index 06a897b5a..8f2c63f83 100644 --- a/frontend/src/pages/auth/LoginPage/Login.utils.tsx +++ b/frontend/src/pages/auth/LoginPage/Login.utils.tsx @@ -5,7 +5,17 @@ import { fetchOrganizations } from "@app/hooks/api/organization/queries"; import { queryClient } from "@app/hooks/api/reactQuery"; import { userKeys } from "@app/hooks/api/users"; -export const navigateUserToOrg = async (navigate: NavigateFn, organizationId?: string) => { +type NavigateUserToOrgParams = { + navigate: NavigateFn; + organizationId?: string; + navigateTo?: string; +}; + +export const navigateUserToOrg = async ({ + navigate, + organizationId, + navigateTo +}: NavigateUserToOrgParams) => { const userOrgs = await fetchOrganizations(); const nonAuthEnforcedOrgs = userOrgs.filter((org) => !org.authEnforced); @@ -13,7 +23,7 @@ export const navigateUserToOrg = async (navigate: NavigateFn, organizationId?: s if (organizationId) { localStorage.setItem("orgData.id", organizationId); navigate({ - to: "/organizations/$orgId/projects", + to: navigateTo || "/organizations/$orgId/projects", params: { orgId: organizationId } }); return; @@ -24,7 +34,7 @@ export const navigateUserToOrg = async (navigate: NavigateFn, organizationId?: s const userOrg = nonAuthEnforcedOrgs[0] && nonAuthEnforcedOrgs[0].id; localStorage.setItem("orgData.id", userOrg); navigate({ - to: "/organizations/$orgId/projects", + to: navigateTo || "/organizations/$orgId/projects", params: { orgId: userOrg } }); } else { diff --git a/frontend/src/pages/auth/LoginPage/components/PasswordStep/PasswordStep.tsx b/frontend/src/pages/auth/LoginPage/components/PasswordStep/PasswordStep.tsx index 3e13d6d08..4e8f2ba2c 100644 --- a/frontend/src/pages/auth/LoginPage/components/PasswordStep/PasswordStep.tsx +++ b/frontend/src/pages/auth/LoginPage/components/PasswordStep/PasswordStep.tsx @@ -115,7 +115,7 @@ export const PasswordStep = ({ return; } - await navigateUserToOrg(navigate, organizationId); + await navigateUserToOrg({ navigate, organizationId }); }; await finishWithOrgWorkflow(); @@ -131,7 +131,7 @@ export const PasswordStep = ({ } // case: no orgs found, so we navigate the user to create an org else { - await navigateUserToOrg(navigate); + await navigateUserToOrg({ navigate }); } } } catch (err: any) { @@ -233,7 +233,7 @@ export const PasswordStep = ({ } // case: no orgs found, so we navigate the user to create an org else { - await navigateUserToOrg(navigate); + await navigateUserToOrg({ navigate }); } } } else { @@ -254,7 +254,7 @@ export const PasswordStep = ({ // case: organization ID is present from the provider auth token -- navigate directly to the org if (organizationId) { - await navigateUserToOrg(navigate, organizationId); + await navigateUserToOrg({ navigate, organizationId }); } // case: no organization ID is present -- navigate to the select org page IF the user has any orgs // if the user has no orgs, navigate to the create org page @@ -264,7 +264,7 @@ export const PasswordStep = ({ if (userOrgs.length > 0) { navigateToSelectOrganization(undefined, isAdminLogin); } else { - await navigateUserToOrg(navigate); + await navigateUserToOrg({ navigate }); } } } @@ -316,7 +316,7 @@ export const PasswordStep = ({ return ( - navigateUserToOrg(navigate, organizationId).catch(() => + navigateUserToOrg({ navigate, organizationId }).catch(() => createNotification({ text: "Failed to navigate user", type: "error" }) ) } diff --git a/frontend/src/pages/auth/SelectOrgPage/SelectOrgSection.tsx b/frontend/src/pages/auth/SelectOrgPage/SelectOrgSection.tsx index 2a3e9c554..a15bab287 100644 --- a/frontend/src/pages/auth/SelectOrgPage/SelectOrgSection.tsx +++ b/frontend/src/pages/auth/SelectOrgPage/SelectOrgSection.tsx @@ -188,7 +188,7 @@ export const SelectOrganizationSection = () => { navigate({ to: "/cli-redirect" }); // cli page } else { - navigateUserToOrg(navigate, organization.id); + navigateUserToOrg({ navigate, organizationId: organization.id }); } }, [selectOrg] @@ -201,7 +201,7 @@ export const SelectOrganizationSection = () => { const decodedJwt = jwtDecode(authToken) as any; if (decodedJwt?.organizationId) { - navigateUserToOrg(navigate, decodedJwt.organizationId); + navigateUserToOrg({ navigate, organizationId: decodedJwt.organizationId }); } }