Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 0 additions & 24 deletions src/new-landingpage/src/App.tsx

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two issues:

1

Once I go to Organisation, it is not directly going to /flows, it is going to /dashboard for a few seconds and then to /flows. We need to go to /flows directly from /organization

What we need is
/organization->/flows instead of /organization->/dashboard(for few seconds) ->/flows

2

⚠️ Note
This behavior exists in the old frontend and must be fully replicated in the new frontend. You can check for the Dashboard button and logout button in the old frontend and replicate its style in the new frontend


Post-Login Routing & Header Behavior

(Parity with Old Frontend)


Root (/) behavior after login

  • After a user has logged in, opening / in a new tab should remain on /.
  • Currently, / incorrectly redirects to /flows.
    This must be fixed.

Restricted routes after login

Once a user is logged in:

  • Visiting /login must redirect to /flows
  • Visiting /organization must redirect to /flows

Header actions on / after login

On the / page, after login:

  • The “Login” button (top-right) should change to “Dashboard”
  • Clicking Dashboard should navigate to /flows

Sign-out behavior on / after login

On the / page, after login:

  • The “Book a Demo” button (top-right) should change to “Sign Out”
  • Clicking Sign Out should properly log the user out

Original file line number Diff line number Diff line change
@@ -1,36 +1,12 @@
import { useAuth } from "@clerk/clerk-react";
import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
import { LANDING_BASENAME } from "./landingRoutes";
import NewLandingPageLogin from "./NewLandingPageLogin";
import OrganizationOnboarding from "./OrganizationOnboarding";
import "./App.css";
import DashboardPage from "./DashboardPage";
import { useCookies } from "react-cookie";
import LandingPage from "./LandingPage";
import {
hasWorkspaceSession,
LANGFLOW_ACCESS_TOKEN,
LANGFLOW_REFRESH_TOKEN,
} from "./session";

function RootRoute() {
const { isSignedIn } = useAuth();
const [cookies] = useCookies([LANGFLOW_ACCESS_TOKEN, LANGFLOW_REFRESH_TOKEN]);

console.log("[App] RootRoute render", { isSignedIn });

if (isSignedIn) {
const workspaceReady = hasWorkspaceSession(cookies);
const destination = workspaceReady ? "/dashboard" : "/organization";

console.log("[App] Redirecting signed-in user from root", {
workspaceReady,
destination,
});

return <Navigate to={destination} replace />;
}

return <LandingPage />;
}

Expand Down
20 changes: 12 additions & 8 deletions src/new-landingpage/src/NewLandingPageLogin.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { SignIn, SignedIn, SignedOut, useAuth } from "@clerk/clerk-react";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { useCookies } from "react-cookie";
import { LANDING_BASENAME } from "./landingRoutes";
import {
clearStoredOrgSelection,
hasWorkspaceSession,
LANGFLOW_ACCESS_TOKEN,
LANGFLOW_REFRESH_TOKEN,
} from "./session";
import { useCookies } from "react-cookie";

export default function NewLandingPageLogin() {
const { isSignedIn, isLoaded } = useAuth();
Expand All @@ -26,14 +26,18 @@ export default function NewLandingPageLogin() {
useEffect(() => {
if (isLoaded && isSignedIn) {
const workspaceReady = hasWorkspaceSession(cookies);
const destination = workspaceReady ? "/dashboard" : "/organization";
const destination = workspaceReady ? "/flows" : "/organization";

console.log(
"[NewLandingPageLogin] User signed in, redirecting based on session",
{ workspaceReady, destination },
);
console.log("[NewLandingPageLogin] User signed in, redirecting", {
workspaceReady,
destination,
});

navigate(destination, { replace: true });
if (workspaceReady) {
window.location.assign(destination);
} else {
navigate("/organization", { replace: true });
}
}
}, [cookies, isLoaded, isSignedIn, navigate]);

Expand All @@ -55,7 +59,7 @@ export default function NewLandingPageLogin() {
/>
</SignedOut>
<SignedIn>
<div style={{ textAlign: "center" }}>Redirecting you to your organization list…</div>
<div style={{ textAlign: "center" }}>Redirecting you to your workspace…</div>
</SignedIn>
</div>
</div>
Expand Down
137 changes: 104 additions & 33 deletions src/new-landingpage/src/OrganizationOnboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ import {
} from "react";
import {
Navigate,
useLocation,
useNavigate,
useSearchParams,
} from "react-router-dom";
import { useCookies } from "react-cookie";
Expand All @@ -26,6 +24,7 @@ import logoicon from "./new-assets/visualailogo.png";
import ProgressBar from "./ProgressBar";
import {
ACTIVE_ORG_STORAGE_KEY,
hasWorkspaceSession,
LANGFLOW_ACCESS_TOKEN,
LANGFLOW_REFRESH_TOKEN,
ORG_SELECTED_KEY,
Expand Down Expand Up @@ -181,12 +180,22 @@ async function createOrganisation(token: string) {
token,
});
} catch (error: any) {
// Some backends return 200 or 400 when org already exists
if (
error instanceof HttpError &&
(error.status === 200 || error.status === 400)
) {
return;
// Some backends return 200/400/409 when org already exists
if (error instanceof HttpError) {
const detail =
typeof error.data?.detail === "string" ? error.data.detail : "";
const isRecoverable =
error.status === 200 ||
error.status === 400 ||
error.status === 409 ||
detail.includes("organization already exists");

if (isRecoverable) {
console.debug(
"[OrganizationOnboarding] createOrganisation(): org already exists; continuing bootstrap",
);
return;
}
}
throw error;
}
Expand All @@ -211,6 +220,35 @@ function setStoredActiveOrgId(orgId: string | null) {
}
}

function markOrgSelection(activeOrgId: string) {
if (typeof window === "undefined") return;

localStorage.setItem(ORG_SELECTED_KEY, "true");
sessionStorage.setItem("isOrgSelected", "true");
setStoredActiveOrgId(activeOrgId);
}

function clearOrgSelection() {
if (typeof window === "undefined") return;

localStorage.removeItem(ORG_SELECTED_KEY);
sessionStorage.removeItem("isOrgSelected");
setStoredActiveOrgId(null);
}

function hasStoredWorkspaceSession(cookies: Record<string, any>) {
if (typeof window === "undefined") return { hasSession: false, activeOrgId: null };

const orgSelected = localStorage.getItem(ORG_SELECTED_KEY) === "true";
const activeOrgId = localStorage.getItem(ACTIVE_ORG_STORAGE_KEY);
const hasAccessToken = Boolean(cookies[LANGFLOW_ACCESS_TOKEN]);

return {
hasSession: orgSelected && Boolean(activeOrgId) && hasAccessToken,
activeOrgId,
};
}

/**
* ==========
* Main component
Expand All @@ -228,8 +266,6 @@ export default function OrganizationOnboarding() {
organizationId: organization?.id,
});

const location = useLocation();
const navigate = useNavigate();
const [searchParams, setSearchParams] = useSearchParams();

const [cookies, setCookie, removeCookie] = useCookies([
Expand All @@ -241,11 +277,28 @@ export default function OrganizationOnboarding() {
const [error, setError] = useState<string | null>(null);
const [status, setStatus] = useState<string | null>(null);
const [isBootstrapping, setIsBootstrapping] = useState(false);
const [shouldGoToDashboard, setShouldGoToDashboard] = useState(false);
const [shouldRedirectToFlows, setShouldRedirectToFlows] = useState(false);

const bootstrappedRef = useRef(false);
const processedOrgRef = useRef<string | null>(null);
const provisioningOrgRef = useRef<string | null>(null);
const redirectTo = useMemo(() => {
const next = searchParams.get("next");
if (!next) return "/flows";
return next.startsWith("/") ? next : "/flows";
}, [searchParams]);

const redirectToWorkspace = useCallback(() => {
setShouldRedirectToFlows(true);
window.location.assign(redirectTo);
}, [redirectTo]);

const orgRedirectQuery = useMemo(() => {
const params = new URLSearchParams();
params.set("selected", "true");
params.set("next", redirectTo);
return params.toString();
}, [redirectTo]);

/**
* Derived user info for display
Expand Down Expand Up @@ -293,8 +346,7 @@ export default function OrganizationOnboarding() {
setCookie(LANGFLOW_REFRESH_TOKEN, refreshToken, cookieOptions);
}

localStorage.setItem(ORG_SELECTED_KEY, "true");
setStoredActiveOrgId(activeOrgId);
markOrgSelection(activeOrgId);

console.log("[OrganizationOnboarding] Session persisted", {
hasAccessToken: Boolean(accessToken),
Expand All @@ -309,8 +361,7 @@ export default function OrganizationOnboarding() {
removeCookie(LANGFLOW_ACCESS_TOKEN, { path: "/" });
removeCookie(LANGFLOW_REFRESH_TOKEN, { path: "/" });
removeCookie(LANGFLOW_AUTO_LOGIN_OPTION, { path: "/" });
localStorage.removeItem(ORG_SELECTED_KEY);
setStoredActiveOrgId(null);
clearOrgSelection();

try {
await signOut();
Expand Down Expand Up @@ -367,13 +418,12 @@ export default function OrganizationOnboarding() {

persistSession(orgToken, (tokens as any)?.refresh_token ?? null, activeOrgId);

setStatus("Redirecting to dashboard...");
setStatus("Redirecting to your workspace...");
console.log(
"[OrganizationOnboarding] Redirecting to dashboard with org",
"[OrganizationOnboarding] Redirecting to workspace with org",
activeOrgId,
);
setShouldGoToDashboard(true);
navigate("/dashboard", { replace: true });
redirectToWorkspace();
} catch (err: any) {
console.error("[OrganizationOnboarding] Failed to bootstrap", err);
const msg =
Expand All @@ -391,8 +441,8 @@ export default function OrganizationOnboarding() {
clearSession,
getToken,
isSignedIn,
navigate,
organization?.id,
redirectToWorkspace,
persistSession,
user,
]);
Expand All @@ -401,6 +451,12 @@ export default function OrganizationOnboarding() {
if (!isLoaded || !isSignedIn || !organization?.id) return;
if (!hasExistingOrgSelection || bootstrappedRef.current) return;

const { hasSession } = hasStoredWorkspaceSession(cookies);
if (hasSession) {
// Session already usable; let the other effect handle redirect/rehydration
return;
}

console.log(
"[OrganizationOnboarding] Existing org selection detected; bootstrapping",
);
Expand All @@ -417,18 +473,20 @@ export default function OrganizationOnboarding() {
useEffect(() => {
if (!isLoaded || !isSignedIn) return;

const orgSelected = localStorage.getItem(ORG_SELECTED_KEY) === "true";
const activeOrgId = localStorage.getItem(ACTIVE_ORG_STORAGE_KEY);
const hasAccessToken = Boolean(cookies[LANGFLOW_ACCESS_TOKEN]);
const { hasSession, activeOrgId } = hasStoredWorkspaceSession(cookies);

if (hasSession && activeOrgId) {
// Ensure sessionStorage flag is set for this tab so the main app honors the org selection
sessionStorage.setItem("isOrgSelected", "true");
setStoredActiveOrgId(activeOrgId);
bootstrappedRef.current = true;

if (orgSelected && activeOrgId && hasAccessToken) {
console.log("[OrganizationOnboarding] Session already present; routing to /dashboard", {
console.log("[OrganizationOnboarding] Session already present; routing to workspace", {
activeOrgId,
});
setShouldGoToDashboard(true);
navigate("/dashboard", { replace: true });
redirectToWorkspace();
}
}, [cookies, isLoaded, isSignedIn, navigate]);
}, [cookies, isLoaded, isSignedIn, redirectToWorkspace]);

/**
* When Clerk redirects back with ?selected=true,
Expand Down Expand Up @@ -488,16 +546,29 @@ export default function OrganizationOnboarding() {
return null;
}

const workspaceReady = hasWorkspaceSession(cookies);

if (!isSignedIn) {
console.log(
"[OrganizationOnboarding] User not signed in, redirecting to /login",
);
return <Navigate to="/login" replace />;
}

if (shouldGoToDashboard) {
console.log("[OrganizationOnboarding] Local redirect flag set; sending to /dashboard");
return <Navigate to="/dashboard" replace />;
if (workspaceReady) {
console.log(
"[OrganizationOnboarding] Workspace session present; redirecting to /flows",
);
window.location.assign("/flows");
return null;
}

if (shouldRedirectToFlows) {
console.log("[OrganizationOnboarding] Local redirect flag set; sending to workspace", {
redirectTo,
});
window.location.assign(redirectTo);
return null;
}

return (
Expand Down Expand Up @@ -690,8 +761,8 @@ export default function OrganizationOnboarding() {
<SignedIn>
<OrganizationList
hidePersonal
afterCreateOrganizationUrl={`${LANDING_BASENAME}/organization?selected=true`}
afterSelectOrganizationUrl={`${LANDING_BASENAME}/organization?selected=true`}
afterCreateOrganizationUrl={`${LANDING_BASENAME}/organization?${orgRedirectQuery}`}
afterSelectOrganizationUrl={`${LANDING_BASENAME}/organization?${orgRedirectQuery}`}
/>
</SignedIn>

Expand Down
Loading