-
-
-
-
-
- {badge && (
-
- {badge}
-
- )}
- {statusIndicator}
-
+}) => {
+ const location = useLocation();
+ const navigate = useNavigate();
+ const [navOpen, setNavOpen] = useState(false);
-
- {nav?.map((item) => (
-
- ))}
- {actions}
+ const handleNavClick = (to: string) => {
+ if (location.pathname === to) {
+ setNavOpen(false);
+ return;
+ }
+ setNavOpen(false);
+ setTimeout(() => navigate(to), 150);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+ JobOps
+
+
+
+
+
+
+
+
+
+ {badge && (
+
+ {badge}
+
+ )}
+ {statusIndicator}
+
+
+
+ {actions}
+
-
-
-);
+
+ );
+};
// ============================================================================
// Status Indicator (Pipeline running, Updating, etc.)
diff --git a/orchestrator/src/client/pages/OrchestratorPage.tsx b/orchestrator/src/client/pages/OrchestratorPage.tsx
index 590ce1e..fac2efc 100644
--- a/orchestrator/src/client/pages/OrchestratorPage.tsx
+++ b/orchestrator/src/client/pages/OrchestratorPage.tsx
@@ -5,6 +5,7 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
ArrowUpDown,
+ Briefcase,
Calendar,
CheckCircle2,
ChevronDown,
@@ -14,8 +15,10 @@ import {
ExternalLink,
FileText,
Filter,
+ Home,
Loader2,
MapPin,
+ Menu,
MoreHorizontal,
Play,
RefreshCcw,
@@ -26,7 +29,7 @@ import {
Sparkles,
XCircle,
} from "lucide-react";
-import { Link } from "react-router-dom";
+import { Link, useLocation, useNavigate } from "react-router-dom";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { toast } from "sonner";
@@ -48,6 +51,13 @@ import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Textarea } from "@/components/ui/textarea";
import { Drawer, DrawerClose, DrawerContent } from "@/components/ui/drawer";
+import {
+ Sheet,
+ SheetContent,
+ SheetHeader,
+ SheetTitle,
+ SheetTrigger,
+} from "@/components/ui/sheet";
import { cn } from "@/lib/utils";
import { copyTextToClipboard, formatJobForWebhook } from "@client/lib/jobCopy";
import { PipelineProgress, DiscoveredPanel } from "../components";
@@ -293,6 +303,9 @@ const ScoreMeter: React.FC<{ score: number | null }> = ({ score }) => {
};
export const OrchestratorPage: React.FC = () => {
+ const location = useLocation();
+ const navigate = useNavigate();
+ const [navOpen, setNavOpen] = useState(false);
const [jobs, setJobs] = useState
([]);
const [stats, setStats] = useState>({
discovered: 0,
@@ -302,6 +315,13 @@ export const OrchestratorPage: React.FC = () => {
skipped: 0,
expired: 0,
});
+
+ const navLinks = [
+ { to: "/", label: "Dashboard", icon: Home },
+ { to: "/visa-sponsors", label: "Visa Sponsors", icon: Shield },
+ { to: "/ukvisajobs", label: "UK Visa Jobs", icon: Briefcase },
+ { to: "/settings", label: "Settings", icon: Settings },
+ ];
const [isLoading, setIsLoading] = useState(true);
const [isPipelineRunning, setIsPipelineRunning] = useState(false);
const [processingJobId, setProcessingJobId] = useState(null);
@@ -1044,49 +1064,74 @@ export const OrchestratorPage: React.FC = () => {
return (
<>
-
-
-
-
-
-
-
Job Ops
-
Orchestrator
+
+
+
+
+
+
+
+
+ JobOps
+
+
+
+
+
+
+
+
+
+
+
Job Ops
+
Orchestrator
+
+
{isPipelineRunning && (
-
+
Pipeline running
)}
-
-
-
-
-
-
+
+
@@ -1160,13 +1205,13 @@ export const OrchestratorPage: React.FC = () => {
{/* Compact metrics summary - demoted visual weight */}
-
{totalJobs} jobs total
-
•
{stats.ready} ready
•
{stats.discovered + stats.processing} discovered
•
{stats.applied} applied
+
•
+
{totalJobs} jobs total
{(stats.skipped > 0 || stats.expired > 0) && (
<>
•
@@ -1191,8 +1236,8 @@ export const OrchestratorPage: React.FC = () => {
))}
-
-
+
+
{
className="h-8 pl-8 text-sm"
/>
-