tinovisas/frontend/src/app/checkup/page.tsx

149 lines
6.6 KiB
TypeScript

"use client";
import { useState, useEffect } from "react";
import { useRouter } from "next/navigation";
import { useAuth } from "../../hooks/useAuth";
import { useClients } from "../../hooks/useClients";
import { useCountries } from "../../hooks/useCountries";
import Sidebar from "../../components/Sidebar";
import Header from "../../components/Header";
import { checkupAPI, clientAPI } from "../../lib/api";
import { toast } from "react-toastify";
import { Activity, Play, Loader2, Monitor, AlertCircle } from "lucide-react";
export default function CheckupPage() {
const router = useRouter();
const { user, loading } = useAuth();
const { clients } = useClients({ status: "active", limit: 100 });
const { countries } = useCountries(true);
const [running, setRunning] = useState(false);
const [selectedClient, setSelectedClient] = useState("");
const [selectedCountry, setSelectedCountry] = useState("");
const [logs, setLogs] = useState<string[]>([]);
useEffect(() => { if (!loading && !user) router.push("/login"); }, [user, loading, router]);
const handleRunCheckup = async () => {
if (!selectedClient || !selectedCountry) {
toast.error("Select a client and country");
return;
}
setRunning(true);
setLogs([]);
try {
setLogs((prev) => [...prev, "Starting checkup session..."]);
const { data } = await checkupAPI.run({ client_id: selectedClient, country_id: selectedCountry });
setLogs((prev) => [...prev, "Checkup completed!", `Available dates: ${data.data?.available_dates?.length || 0} found`]);
toast.success("Checkup completed successfully");
} catch (error: any) {
const msg = error.response?.data?.message || "Checkup failed";
setLogs((prev) => [...prev, `Error: ${msg}`]);
toast.error(msg);
} finally {
setRunning(false);
}
};
if (loading || !user) return null;
return (
<div className="flex min-h-screen bg-dark-900">
<Sidebar />
<div className="flex-1 flex flex-col">
<Header />
<main className="flex-1 p-6 overflow-y-auto">
<div className="mb-6">
<h1 className="text-2xl font-bold text-white flex items-center gap-2">
<Activity className="w-6 h-6 text-accent-cyan" />
Checkup Mode
</h1>
<p className="text-gray-500">Check visa appointment availability</p>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Control Panel */}
<div className="card">
<h2 className="text-lg font-semibold text-white mb-4">Run Checkup</h2>
<div className="space-y-4">
<div>
<label className="label">Select Client</label>
<select value={selectedClient} onChange={(e) => setSelectedClient(e.target.value)} className="select w-full">
<option value="">Choose a client...</option>
{clients.map((c: any) => (
<option key={c.id} value={c.id}>{c.first_name} {c.last_name} ({c.nationality || "N/A"})</option>
))}
</select>
</div>
<div>
<label className="label">Select Country</label>
<div className="grid grid-cols-2 gap-2">
{countries.map((c: any) => (
<button
key={c.id}
onClick={() => setSelectedCountry(c.id)}
className={`flex items-center gap-2 p-3 rounded-lg border transition-all ${
selectedCountry === c.id
? "border-accent-blue bg-accent-blue/10 text-white"
: "border-dark-600 hover:border-dark-500 text-gray-400"
}`}
>
<span className="text-xl">{c.flag_emoji}</span>
<span className="text-sm font-medium">{c.name}</span>
</button>
))}
</div>
</div>
<button
onClick={handleRunCheckup}
disabled={running || !selectedClient || !selectedCountry}
className="btn-primary w-full flex items-center justify-center gap-2 disabled:opacity-50"
>
{running ? <Loader2 className="w-4 h-4 animate-spin" /> : <Play className="w-4 h-4" />}
{running ? "Running Checkup..." : "Start Checkup"}
</button>
</div>
</div>
{/* Logs Panel */}
<div className="card">
<h2 className="text-lg font-semibold text-white mb-4 flex items-center gap-2">
<Monitor className="w-5 h-5 text-accent-blue" />
Session Logs
</h2>
<div className="bg-dark-900 rounded-lg p-4 h-80 overflow-y-auto font-mono text-sm space-y-1">
{logs.length === 0 ? (
<p className="text-gray-600 italic">No logs yet. Start a checkup to see logs here.</p>
) : (
logs.map((log, i) => (
<div key={i} className={`${log.includes("Error") ? "text-red-400" : log.includes("completed") ? "text-green-400" : "text-gray-400"}`}>
<span className="text-gray-600">[{new Date().toLocaleTimeString()}]</span> {log}
</div>
))
)}
</div>
</div>
</div>
{/* Info Cards */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-6">
<div className="card">
<AlertCircle className="w-8 h-8 text-accent-orange mb-2" />
<h3 className="font-medium text-white mb-1">Automated Checkup</h3>
<p className="text-sm text-gray-500">Uses Playwright to navigate VFS websites and check for available appointment slots.</p>
</div>
<div className="card">
<Activity className="w-8 h-8 text-accent-cyan mb-2" />
<h3 className="font-medium text-white mb-1">Real-time Monitoring</h3>
<p className="text-sm text-gray-500">Screenshots are captured during the checkup process for verification.</p>
</div>
<div className="card">
<Monitor className="w-8 h-8 text-accent-purple mb-2" />
<h3 className="font-medium text-white mb-1">Session Logging</h3>
<p className="text-sm text-gray-500">All actions are logged and stored for audit and debugging purposes.</p>
</div>
</div>
</main>
</div>
</div>
);
}