From 919f61dde65adc9f944954ca132ec1e2d536e57e Mon Sep 17 00:00:00 2001 From: cesnimda Date: Sun, 5 Jul 2026 20:25:43 +0200 Subject: [PATCH] feat(ui): public marketing landing page at "/" for logged-out visitors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a LandingPage (hero + features + how-it-works + CTAs) served at "/" so visitors learn about the product before signing in — matching the JobTrack mockups (indigo/cyan, dark hero + light sections). If the visitor already has a session, LandingPage redirects into the app (/jobs); otherwise it shows the marketing page with "Sign in" CTAs. The "/" route is public (outside the auth-gated Shell), so logged-out users no longer bounce straight to /login. Verified live: renders at "/" with headline, feature grid, how-it-works steps and CTAs; no console errors; type-clean. Co-Authored-By: Claude Opus 4.8 --- job-tracker-ui/src/App.tsx | 2 + job-tracker-ui/src/pages/LandingPage.tsx | 174 +++++++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 job-tracker-ui/src/pages/LandingPage.tsx diff --git a/job-tracker-ui/src/App.tsx b/job-tracker-ui/src/App.tsx index 228c397..15e49cc 100644 --- a/job-tracker-ui/src/App.tsx +++ b/job-tracker-ui/src/App.tsx @@ -28,6 +28,7 @@ import JobTable from "./components/JobTable"; import type { JobTableColumns } from "./components/JobTable"; import { I18nProvider, useI18n } from "./i18n/I18nProvider"; import LoginPage from "./pages/LoginPage"; +import LandingPage from "./pages/LandingPage"; import ForgotPasswordPage from "./pages/ForgotPasswordPage"; import ResetPasswordPage from "./pages/ResetPasswordPage"; import RouteErrorPage from "./pages/RouteErrorPage"; @@ -344,6 +345,7 @@ export default function App() { }); const router = useMemo(() => createBrowserRouter([ + { path: "/", element: , errorElement: }, { path: "/login", element: , errorElement: }, { path: "/forgot-password", element: , errorElement: }, { path: "/reset-password", element: , errorElement: }, diff --git a/job-tracker-ui/src/pages/LandingPage.tsx b/job-tracker-ui/src/pages/LandingPage.tsx new file mode 100644 index 0000000..64050f9 --- /dev/null +++ b/job-tracker-ui/src/pages/LandingPage.tsx @@ -0,0 +1,174 @@ +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 DashboardIcon from "@mui/icons-material/SpaceDashboardOutlined"; +import AlarmIcon from "@mui/icons-material/NotificationsActiveOutlined"; +import MatchIcon from "@mui/icons-material/FactCheckOutlined"; +import MailIcon from "@mui/icons-material/MarkEmailReadOutlined"; +import AttachIcon from "@mui/icons-material/DescriptionOutlined"; +import InsightsIcon from "@mui/icons-material/InsightsOutlined"; + +import { api } from "../api"; + +const BRAND_DARK = "#0b1020"; +const BRAND_PANEL = "#111a33"; + +const FEATURES: { icon: React.ReactNode; title: string; body: string }[] = [ + { icon: , title: "Centralized pipeline", body: "Track every application across Applied, Waiting, Interview, Offer, Rejected and Ghosted — drag to update." }, + { icon: , title: "Smart follow-ups", body: "Reminders surface what needs attention next, with a grounded draft ready to review and send." }, + { icon: , title: "Honest CV match", body: "A deterministic keyword-coverage score with matched vs missing skills — not an opaque black box." }, + { icon: , title: "Email correspondence", body: "Link Gmail threads to a job; new replies appear automatically without re-importing." }, + { icon: , title: "Attachments & docs", body: "Keep resumes, cover letters and portfolios versioned per application, right where you need them." }, + { icon: , title: "Dashboard & insights", body: "Response rates, funnel, time-in-stage and skill demand across your whole search." }, +]; + +const STEPS: { n: number; title: string; body: string }[] = [ + { n: 1, title: "Import", body: "Paste a job URL or use the bookmarklet — we parse the role into structured fields." }, + { n: 2, title: "Match", body: "See how your CV covers the role: matched keywords and the gaps to close." }, + { n: 3, title: "Tailor", body: "AI drafts a tailored CV and cover letter — you review every word before it goes out." }, + { n: 4, title: "Track", body: "Move it through the pipeline; documents, notes and history stay attached." }, + { n: 5, title: "Follow up", body: "Linked email threads and reminders keep momentum with grounded replies." }, + { n: 6, title: "Analyze", body: "See what's working — response rate, funnel and time-in-stage — and focus your effort." }, +]; + +export default function LandingPage() { + const navigate = useNavigate(); + const [checking, setChecking] = useState(true); + + // If the visitor already has a session, send them straight into the app. + useEffect(() => { + let active = true; + api + .get("/auth/me") + .then(() => { if (active) navigate("/jobs", { replace: true }); }) + .catch(() => { if (active) setChecking(false); }); + return () => { active = false; }; + }, [navigate]); + + if (checking) { + return ( + + Loading… + + ); + } + + const gradientText = { + background: "linear-gradient(90deg,#6366f1,#22d3ee)", + WebkitBackgroundClip: "text", + WebkitTextFillColor: "transparent", + } as const; + + return ( + + {/* Top bar */} + + + + + + JobTrack + + + + + + + {/* Hero */} + + + + + AI-ASSISTED JOB SEARCH WORKSPACE + + + Run your job search without losing the thread. + + + Import a role, tailor your CV, track every application, and keep recruiter correspondence tied to the + right job — all in one focused workspace. Assistive, never autonomous: you approve every draft. + + + + + + + React · TypeScript · ASP.NET Core · EF Core · FastAPI AI · Gmail + + + + + + {/* Features */} + + + WHAT IT DOES + One workspace for the whole search + + Everything from a single import to the final offer — no more spreadsheets and scattered inboxes. + + + + {FEATURES.map((f) => ( + + {f.icon} + {f.title} + {f.body} + + ))} + + + + {/* How it works */} + + + + HOW IT WORKS + From a link to an offer + + + {STEPS.map((s) => ( + + {s.n} + {s.title} + {s.body} + + ))} + + + + + {/* CTA */} + + + + Ready to organize your search? + Sign in to start tracking applications, tailoring CVs, and following up with intent. + + + + + + {/* Footer */} + + + + © {new Date().getFullYear()} JobTrack — a focused workspace for the modern job search. + + + + + + ); +}