;
+
+function LoginStub() {
+ const location = useLocation() as { state?: { from?: string } };
+ return login page, from={location.state?.from ?? "none"}
;
+}
+
+// Regression check for the auth-guard fix: a protected route bounces an
+// unauthenticated visitor to "/" with `state.from` set to the page they
+// wanted. The home page must forward that state to /login so sign-in
+// returns them to the originally-requested page instead of dropping them
+// on /jobs.
+describe("LandingPage forwards deep-link intent to /login", () => {
+ it("preserves location.state.from through the Sign in CTA", async () => {
+ mockedApi.get.mockRejectedValueOnce(new Error("401")); // /auth/me: not signed in
+
+ render(
+
+
+ } />
+ } />
+
+ ,
+ );
+
+ await waitFor(() => expect(mockedApi.get).toHaveBeenCalledWith("/auth/me"));
+
+ const signInButtons = await screen.findAllByText("Sign in", { selector: "button" });
+ await userEvent.click(signInButtons[0]);
+
+ expect(await screen.findByText("login page, from=/jobs/42")).toBeInTheDocument();
+ });
+});
diff --git a/job-tracker-ui/src/pages/LandingPage.tsx b/job-tracker-ui/src/pages/LandingPage.tsx
index 70f674d..7029a26 100644
--- a/job-tracker-ui/src/pages/LandingPage.tsx
+++ b/job-tracker-ui/src/pages/LandingPage.tsx
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
import { Box, Button, Container, Stack, Typography } from "@mui/material";
import { alpha } from "@mui/material/styles";
-import { useNavigate } from "react-router-dom";
+import { useLocation, useNavigate } from "react-router-dom";
import DashboardIcon from "@mui/icons-material/SpaceDashboardOutlined";
import AlarmIcon from "@mui/icons-material/NotificationsActiveOutlined";
@@ -42,6 +42,7 @@ const PRICING: { name: string; price: string; cadence: string; highlight: boolea
export default function LandingPage() {
const navigate = useNavigate();
+ const location = useLocation() as { state?: { from?: string } };
const [checking, setChecking] = useState(true);
// If the visitor already has a session, send them straight into the app.
@@ -54,6 +55,11 @@ export default function LandingPage() {
return () => { active = false; };
}, [navigate]);
+ // A protected route redirects unauthenticated visitors here with the page they
+ // wanted in location state; forward it to /login so sign-in returns them there
+ // instead of dropping them on /jobs.
+ const goToLogin = () => navigate("/login", { state: location.state });
+
if (checking) {
return (
@@ -78,7 +84,7 @@ export default function LandingPage() {
✓
JobTrack
-
- navigate("/login")} sx={{ background: "linear-gradient(90deg,#6366f1,#22d3ee)", color: "#0b1020", fontWeight: 800, px: 4, py: 1.25, whiteSpace: "nowrap" }}>
+
Sign in →
@@ -248,7 +254,7 @@ export default function LandingPage() {
© {new Date().getFullYear()} JobTrack — a focused workspace for the modern job search.
- navigate("/login")} sx={{ fontWeight: 700 }}>Sign in
+ Sign in