fix(dashboard): Restore preflight display with gold shield + geo

- Show gold shield icon with city/state for qualified workers
- Restore IP address, fingerprint, and antidetect status rows
- Keep geo session fields in worker interface

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Kelly
2025-12-13 16:31:06 -07:00
parent 3b8171d94e
commit 5ea92e25af

View File

@@ -334,67 +334,97 @@ function ResourceBadge({ worker }: { worker: Worker }) {
);
}
// Preflight Summary - shows qualification status with geo region
// Preflight Summary - shows IP, fingerprint, antidetect status, and qualification
function PreflightSummary({ worker }: { worker: Worker }) {
const httpStatus = worker.preflight_http_status || 'pending';
const isQualified = worker.is_qualified || httpStatus === 'passed';
const httpIp = worker.http_ip;
const fingerprint = worker.fingerprint_data;
const httpError = worker.preflight_http_error;
const httpMs = worker.preflight_http_ms;
const geoState = worker.current_state;
const geoCity = worker.current_city;
// Build tooltip
// Build detailed tooltip
const tooltipLines: string[] = [];
tooltipLines.push(`HTTP Preflight: ${httpStatus.toUpperCase()}`);
if (httpIp) tooltipLines.push(`IP: ${httpIp}`);
if (httpMs) tooltipLines.push(`Response: ${httpMs}ms`);
if (fingerprint?.browser) tooltipLines.push(`Browser: ${fingerprint.browser}`);
if (fingerprint?.timezone) tooltipLines.push(`Timezone: ${fingerprint.timezone}`);
if (fingerprint?.productsReturned !== undefined) tooltipLines.push(`Products returned: ${fingerprint.productsReturned}`);
if (fingerprint?.botDetection) {
const bd = fingerprint.botDetection;
tooltipLines.push(`Bot detection - webdriver: ${bd.webdriver ? 'detected' : 'hidden'}`);
}
if (geoState) tooltipLines.push(`Geo: ${geoCity ? `${geoCity}, ` : ''}${geoState}`);
if (worker.session_task_count !== undefined) {
tooltipLines.push(`Session: ${worker.session_task_count}/${worker.session_max_tasks || 7} tasks`);
}
if (httpError) tooltipLines.push(`Error: ${httpError}`);
// Qualified - show icon + geo
// Qualification styling - gold shield + geo for qualified
if (isQualified) {
return (
<div className="flex items-center gap-2" title={tooltipLines.join('\n')}>
<div className="p-1 rounded bg-gradient-to-r from-amber-100 to-yellow-100 border border-amber-300">
<ShieldCheck className="w-3.5 h-3.5 text-amber-600" />
<div className="flex flex-col gap-1" title={tooltipLines.join('\n')}>
{/* Gold shield + city/state */}
<div className="inline-flex items-center gap-2">
<ShieldCheck className="w-5 h-5 text-amber-500" />
<span className="font-semibold text-gray-800">
{geoCity && geoState ? `${geoCity}, ${geoState}` :
geoState ? geoState :
'No geo assigned'}
</span>
</div>
{geoState ? (
<div className="flex items-center gap-1 text-sm">
<MapPin className="w-3 h-3 text-gray-400" />
<span className="font-medium text-gray-700">
{geoCity ? `${geoCity}, ` : ''}{geoState}
</span>
{/* IP address */}
{httpIp && (
<div className="flex items-center gap-1 text-xs text-gray-600">
<Globe className="w-3 h-3 text-blue-500" />
<span className="font-mono">{httpIp}</span>
</div>
) : (
<span className="text-xs text-gray-500">No geo assigned</span>
)}
{/* Fingerprint summary */}
{fingerprint?.browser && (
<div className="flex items-center gap-1 text-xs text-gray-500">
<Fingerprint className="w-3 h-3 text-purple-500" />
<span className="truncate max-w-[100px]">{fingerprint.browser}</span>
</div>
)}
{/* Antidetect status */}
<div className="flex items-center gap-1 text-xs">
<Shield className="w-3 h-3 text-emerald-500" />
<span className="text-emerald-600">Antidetect OK</span>
{httpMs && <span className="text-gray-400">({httpMs}ms)</span>}
</div>
</div>
);
}
// Failed
// Not qualified - show failure state
if (httpStatus === 'failed') {
return (
<div className="flex items-center gap-2" title={tooltipLines.join('\n')}>
<div className="p-1 rounded bg-red-100 border border-red-300">
<ShieldX className="w-3.5 h-3.5 text-red-600" />
<div className="flex flex-col gap-1" title={tooltipLines.join('\n')}>
<div className="inline-flex items-center gap-1.5 px-2 py-1 rounded-lg bg-red-100 border border-red-300">
<ShieldX className="w-4 h-4 text-red-600" />
<span className="text-xs font-bold text-red-700">NOT QUALIFIED</span>
</div>
<div className="text-xs text-red-600 max-w-[140px] truncate" title={httpError}>
{httpError || 'Preflight failed'}
</div>
<span className="text-xs text-red-600 truncate max-w-[100px]">
{httpError || 'Failed'}
</span>
</div>
);
}
// Pending
// Pending state
return (
<div className="flex items-center gap-2" title={tooltipLines.join('\n')}>
<div className="p-1 rounded bg-yellow-100 border border-yellow-300">
<Shield className="w-3.5 h-3.5 text-yellow-600 animate-pulse" />
<div className="flex flex-col gap-1" title={tooltipLines.join('\n')}>
<div className="inline-flex items-center gap-1.5 px-2 py-1 rounded-lg bg-yellow-100 border border-yellow-300">
<Shield className="w-4 h-4 text-yellow-600 animate-pulse" />
<span className="text-xs font-bold text-yellow-700">QUALIFYING...</span>
</div>
<div className="text-xs text-gray-500">
Running preflight check
</div>
<span className="text-xs text-yellow-700">Qualifying...</span>
</div>
);
}