sheet implemented

This commit is contained in:
DaKheera47
2026-01-18 00:58:25 +00:00
parent ff4cdb4101
commit 5bb4c67d10
9 changed files with 470 additions and 144 deletions
@@ -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 } 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,8 @@ const ScoreMeter: React.FC<{ score: number | null }> = ({ score }) => {
};
export const OrchestratorPage: React.FC = () => {
const location = useLocation();
const [navOpen, setNavOpen] = useState(false);
const [jobs, setJobs] = useState<Job[]>([]);
const [stats, setStats] = useState<Record<JobStatus, number>>({
discovered: 0,
@@ -302,6 +314,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<string | null>(null);
@@ -1044,49 +1063,67 @@ export const OrchestratorPage: React.FC = () => {
return (
<>
<header className="sticky top-0 z-40 border-b bg-background/80 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="container mx-auto flex max-w-7xl flex-col gap-3 px-4 py-4 sm:flex-row sm:items-center sm:justify-between">
<div className="flex min-w-0 flex-wrap items-center gap-3">
<div className="flex h-9 w-9 items-center justify-center rounded-lg border border-border/60 bg-muted/30">
<Sparkles className="h-4 w-4 text-muted-foreground" />
</div>
<div className="min-w-0 leading-tight">
<div className="text-sm font-semibold tracking-tight">Job Ops</div>
<div className="text-xs text-muted-foreground">Orchestrator</div>
<div className="container mx-auto flex max-w-7xl items-center justify-between gap-4 px-4 py-4">
<div className="flex items-center gap-3">
<Sheet open={navOpen} onOpenChange={setNavOpen}>
<SheetTrigger asChild>
<Button variant="ghost" size="icon">
<Menu className="h-5 w-5" />
<span className="sr-only">Open navigation menu</span>
</Button>
</SheetTrigger>
<SheetContent side="left" className="w-64">
<SheetHeader>
<SheetTitle>Navigation</SheetTitle>
</SheetHeader>
<nav className="mt-6 flex flex-col gap-2">
{navLinks.map(({ to, label, icon: Icon }) => (
<Link
key={to}
to={to}
onClick={() => setNavOpen(false)}
className={`flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground ${
location.pathname === to
? "bg-accent text-accent-foreground"
: "text-muted-foreground"
}`}
>
<Icon className="h-4 w-4" />
{label}
</Link>
))}
</nav>
</SheetContent>
</Sheet>
<div className="flex items-center gap-3">
<div className="flex h-9 w-9 items-center justify-center rounded-lg border border-border/60 bg-muted/30">
<Sparkles className="h-4 w-4 text-muted-foreground" />
</div>
<div className="min-w-0 leading-tight">
<div className="text-sm font-semibold tracking-tight">Job Ops</div>
<div className="text-xs text-muted-foreground">Orchestrator</div>
</div>
</div>
{isPipelineRunning && (
<span className="inline-flex items-center gap-2 rounded-full border border-amber-500/30 bg-amber-500/10 px-2 py-1 text-[11px] font-semibold uppercase tracking-wide text-amber-200">
<span className="hidden sm:inline-flex items-center gap-2 rounded-full border border-amber-500/30 bg-amber-500/10 px-2 py-1 text-[11px] font-semibold uppercase tracking-wide text-amber-200">
<span className="h-1.5 w-1.5 rounded-full bg-amber-400 animate-pulse" />
Pipeline running
</span>
)}
</div>
<div className="flex w-full flex-wrap items-center gap-2 sm:w-auto sm:justify-end">
<Button asChild variant="ghost" size="icon" aria-label="Visa Sponsors search">
<Link to="/visa-sponsors">
<Shield className="h-4 w-4" />
</Link>
</Button>
<Button asChild variant="ghost" size="icon" aria-label="UK Visa Jobs search">
<Link to="/ukvisajobs">
<Search className="h-4 w-4" />
</Link>
</Button>
<Button asChild variant="ghost" size="icon" aria-label="Settings">
<Link to="/settings">
<Settings className="h-4 w-4" />
</Link>
</Button>
<div className="flex w-full items-center gap-1 sm:w-auto">
<div className="flex items-center gap-2">
<div className="flex items-center gap-1">
<Button
size="sm"
onClick={handleRunPipeline}
disabled={isPipelineRunning}
className="w-full gap-2 sm:w-auto"
className="gap-2"
>
{isPipelineRunning ? <Loader2 className="h-4 w-4 animate-spin" /> : <Play className="h-4 w-4" />}
{isPipelineRunning ? "Running" : "Run pipeline"}
<span className="hidden sm:inline">{isPipelineRunning ? "Running" : "Run pipeline"}</span>
</Button>
<DropdownMenu>
@@ -3,7 +3,7 @@
*/
import React, { useEffect, useMemo, useState } from "react"
import { AlertTriangle, ArrowLeft, Settings, Trash2 } from "lucide-react"
import { AlertTriangle, Settings, Trash2 } from "lucide-react"
import { toast } from "sonner"
import { PageHeader } from "../components/layout"
@@ -418,7 +418,6 @@ export const SettingsPage: React.FC = () => {
icon={Settings}
title="Settings"
subtitle="Configure runtime behavior for this app."
nav={[{ icon: ArrowLeft, label: "Back to Orchestrator", to: "/" }]}
/>
<main className="container mx-auto max-w-3xl space-y-6 px-4 py-6 pb-12">
@@ -4,7 +4,6 @@
import React, { useEffect, useMemo, useState } from "react";
import {
ArrowLeft,
Briefcase,
Calendar,
ChevronLeft,
@@ -14,12 +13,15 @@ import {
DollarSign,
ExternalLink,
GraduationCap,
Home,
Loader2,
MapPin,
Menu,
Search,
Settings,
Shield,
} from "lucide-react";
import { Link } from "react-router-dom";
import { Link, useLocation } from "react-router-dom";
import { toast } from "sonner";
import { Badge } from "@/components/ui/badge";
@@ -28,6 +30,13 @@ import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
import { Drawer, DrawerClose, DrawerContent } from "@/components/ui/drawer";
import {
Sheet,
SheetContent,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet";
import { cn } from "@/lib/utils";
import * as api from "../api";
import type { CreateJobInput } from "../../shared/types";
@@ -72,7 +81,16 @@ const clampText = (value: string, max = 160) => (value.length > max ? `${value.s
const jobKey = (job: CreateJobInput) => job.sourceJobId || job.jobUrl;
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 },
];
export const UkVisaJobsPage: React.FC = () => {
const location = useLocation();
const [navOpen, setNavOpen] = useState(false);
const [searchTermInput, setSearchTermInput] = useState("");
const [activeSearchTerm, setActiveSearchTerm] = useState<string | null>(null);
const [results, setResults] = useState<CreateJobInput[]>([]);
@@ -333,8 +351,40 @@ export const UkVisaJobsPage: React.FC = () => {
return (
<>
<header className="sticky top-0 z-40 border-b bg-background/80 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="container mx-auto flex max-w-7xl flex-col gap-3 px-4 py-4 sm:flex-row sm:items-center sm:justify-between">
<div className="flex min-w-0 flex-wrap items-center gap-3">
<div className="container mx-auto flex max-w-7xl items-center justify-between gap-4 px-4 py-4">
<div className="flex items-center gap-3">
<Sheet open={navOpen} onOpenChange={setNavOpen}>
<SheetTrigger asChild>
<Button variant="ghost" size="icon">
<Menu className="h-5 w-5" />
<span className="sr-only">Open navigation menu</span>
</Button>
</SheetTrigger>
<SheetContent side="left" className="w-64">
<SheetHeader>
<SheetTitle>Navigation</SheetTitle>
</SheetHeader>
<nav className="mt-6 flex flex-col gap-2">
{navLinks.map(({ to, label, icon: Icon }) => (
<Link
key={to}
to={to}
onClick={() => setNavOpen(false)}
className={cn(
"flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground",
location.pathname === to
? "bg-accent text-accent-foreground"
: "text-muted-foreground"
)}
>
<Icon className="h-4 w-4" />
{label}
</Link>
))}
</nav>
</SheetContent>
</Sheet>
<div className="flex h-9 w-9 items-center justify-center rounded-lg border border-border/60 bg-muted/30">
<Briefcase className="h-4 w-4 text-muted-foreground" />
</div>
@@ -342,23 +392,10 @@ export const UkVisaJobsPage: React.FC = () => {
<div className="text-sm font-semibold tracking-tight">UK Visa Jobs</div>
<div className="text-xs text-muted-foreground">Live search console</div>
</div>
<Badge variant="outline" className="uppercase tracking-wide">
<Badge variant="outline" className="hidden sm:inline-flex uppercase tracking-wide">
API search
</Badge>
</div>
<div className="flex w-full items-center gap-2 sm:w-auto sm:justify-end">
<Button asChild variant="ghost" size="icon" aria-label="Back to orchestrator">
<Link to="/">
<ArrowLeft className="h-4 w-4" />
</Link>
</Button>
<Button asChild variant="ghost" size="icon" aria-label="Settings">
<Link to="/settings">
<Settings className="h-4 w-4" />
</Link>
</Button>
</div>
</div>
</header>
@@ -16,7 +16,6 @@ import {
MapPin,
Search,
Shield,
Sparkles,
X,
} from "lucide-react";
import { toast } from "sonner";
@@ -320,7 +319,6 @@ export const VisaSponsorsPage: React.FC = () => {
title="Visa Sponsors"
subtitle="UK Register Search"
statusIndicator={isUpdateInProgress ? <StatusIndicator label="Updating" /> : undefined}
nav={[{ icon: Sparkles, label: "Back to Orchestrator", to: "/" }]}
actions={
<>
{status && (