Skip to content

Commit cda8d88

Browse files
jubradclaude
andcommitted
console: render environment-not-ready flow in the standard layout
The environment-not-ready flow used a stripped custom layout with no navigation, so users without an enabled environment could not see or reach account-scoped pages like App Passwords, License, or Usage & Billing. A blocked or trial-expired organization, by contrast, already rendered the full BaseLayout with those links visible, making the two states inconsistent. Render EnvironmentNotReadyRoutes inside BaseLayout instead. The nav already handles the no-environment state: region-scoped items hide via HideIfEnvironmentDisabled while account-scoped Admin items carry forceShow, so new users now get the same chrome as everyone else. The logo links back to the enable-region flow while no environment is ready. The region-ready toast is preserved in a headless RegionReadyToast component. Its unmount cleanup reads the unstable toast reference through a ref instead of an eslint-disable, which the react-compiler lint rule no longer accepts. The custom layout file is deleted. Adds a test asserting the Admin nav group renders on the enable-region page with no enabled environment while region-scoped items stay hidden. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 7a0234d commit cda8d88

3 files changed

Lines changed: 92 additions & 176 deletions

File tree

console/src/platform/environment-not-ready/EnvironmentNotReadyRoutes.test.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,16 @@ describe("EnvironmentNotReadyRoutes", () => {
9191
);
9292
});
9393
});
94+
95+
it("shows account-level navigation without an enabled environment", async () => {
96+
await renderRoutes(["/enable-region"]);
97+
// Admin items are account-scoped and must stay reachable while no
98+
// environment is enabled.
99+
expect(await screen.findByText("Admin")).toBeInTheDocument();
100+
expect(screen.getByText("App Passwords")).toBeInTheDocument();
101+
expect(screen.getByText("Usage & Billing")).toBeInTheDocument();
102+
// Region-scoped items are hidden until an environment is ready.
103+
expect(screen.queryByText("Clusters")).not.toBeInTheDocument();
104+
expect(screen.queryByText("SQL Shell")).not.toBeInTheDocument();
105+
});
94106
});

console/src/platform/environment-not-ready/EnvironmentNotReadyRoutes.tsx

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,100 @@
77
// the Business Source License, use of this software will be governed
88
// by the Apache License, Version 2.0.
99

10+
import { Text, VStack } from "@chakra-ui/react";
11+
import { useAtomValue } from "jotai";
1012
import React from "react";
11-
import { Navigate, Route } from "react-router-dom";
13+
import { Link, Navigate, Route } from "react-router-dom";
1214

15+
import TextLink from "~/components/TextLink";
1316
import { User } from "~/external-library-wrappers/frontegg";
17+
import { useToast } from "~/hooks/useToast";
18+
import { BaseLayout } from "~/layouts/BaseLayout";
19+
import { regionPath } from "~/platform/routeHelpers";
1420
import { SentryRoutes } from "~/sentry";
21+
import {
22+
currentRegionIdAtom,
23+
useEnvironmentsWithHealth,
24+
useRegionSlug,
25+
} from "~/store/environments";
1526

1627
import EnableRegion from "./EnableRegion";
17-
import { EnvironmentNotReadyLayout } from "./Layout";
1828
import { OnboardingSteps } from "./OnboardingSteps";
1929

30+
const REGION_READY_TOAST_ID = "region-ready-toast";
31+
32+
export const RegionReadyToastBody = (props: {
33+
currentRegionId: string;
34+
regionPath: string;
35+
}) => {
36+
return (
37+
<VStack>
38+
<Text textStyle="text-ui-med">{props.currentRegionId} is ready!</Text>
39+
<TextLink textStyle="text-small" as={Link} to={props.regionPath}>
40+
Go to Materialize Console &rarr;
41+
</TextLink>
42+
</VStack>
43+
);
44+
};
45+
46+
/**
47+
* Pops a toast when the current region becomes healthy while the user is
48+
* still in the environment-not-ready flow. Renders nothing.
49+
*/
50+
const RegionReadyToast = () => {
51+
const toast = useToast();
52+
const regionSlug = useRegionSlug();
53+
const environments = useEnvironmentsWithHealth();
54+
const currentRegionId = useAtomValue(currentRegionIdAtom);
55+
const currentEnvironment = environments.get(currentRegionId);
56+
57+
// The toast reference isn't stable, so the unmount cleanup reads it through
58+
// a ref instead of depending on it directly.
59+
const toastRef = React.useRef(toast);
60+
React.useEffect(() => {
61+
toastRef.current = toast;
62+
}, [toast]);
63+
64+
React.useEffect(() => {
65+
if (
66+
currentEnvironment &&
67+
currentEnvironment.state === "enabled" &&
68+
currentEnvironment.status.health === "healthy" &&
69+
!toast.isActive(REGION_READY_TOAST_ID)
70+
) {
71+
toast({
72+
id: REGION_READY_TOAST_ID,
73+
duration: null, // keep it open
74+
position: "top-right",
75+
description: (
76+
<RegionReadyToastBody
77+
currentRegionId={currentRegionId}
78+
regionPath={regionPath(regionSlug)}
79+
/>
80+
),
81+
});
82+
}
83+
}, [currentEnvironment, currentRegionId, regionSlug, toast]);
84+
85+
React.useEffect(() => {
86+
return () => {
87+
// Close the toast when this component unmounts
88+
toastRef.current.close(REGION_READY_TOAST_ID);
89+
};
90+
}, []);
91+
92+
return null;
93+
};
94+
2095
export const EnvironmentNotReadyRoutes = ({ user }: { user: User }) => {
2196
return (
22-
<EnvironmentNotReadyLayout user={user}>
97+
<BaseLayout>
98+
<RegionReadyToast />
2399
<SentryRoutes>
24100
<Route path="enable-region" element={<EnableRegion user={user} />} />
25101
<Route path=":step" element={<OnboardingSteps user={user} />} />
26102
<Route path="*" element={<Navigate to="../enable-region" replace />} />
27103
</SentryRoutes>
28-
</EnvironmentNotReadyLayout>
104+
</BaseLayout>
29105
);
30106
};

console/src/platform/environment-not-ready/Layout.tsx

Lines changed: 0 additions & 172 deletions
This file was deleted.

0 commit comments

Comments
 (0)