Implement interactive Shoulder-Surfing Mode for live form fill review and manual overrides
This commit is contained in:
+22
-1
@@ -174,6 +174,27 @@
|
||||
}
|
||||
},
|
||||
"ai_job_portal_applications": {
|
||||
"items": []
|
||||
"items": [
|
||||
{
|
||||
"company": "Twilio: Principal Engineer",
|
||||
"role": "Twilio: Principal Engineer",
|
||||
"platform": "Greenhouse",
|
||||
"location": "Remote US / Global",
|
||||
"salaryRange": "$150,000 - $210,000",
|
||||
"status": "Applied",
|
||||
"matchScore": 85,
|
||||
"appliedAt": "2026-07-22T23:52:07.115Z",
|
||||
"jobUrl": "https://weworkremotely.com/remote-jobs/twilio-principal-engineer",
|
||||
"jobDescription": "Remote software development opening.",
|
||||
"autoApplied": true,
|
||||
"timeline": [
|
||||
{
|
||||
"date": "7/22/2026",
|
||||
"action": "Quick AI Auto-Applied from feed"
|
||||
}
|
||||
],
|
||||
"id": "app_1784764327115_4a5x6"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,13 @@ import {
|
||||
Wand2,
|
||||
Bot,
|
||||
BrainCircuit,
|
||||
ListCheck
|
||||
ListCheck,
|
||||
Eye,
|
||||
Play,
|
||||
Pause,
|
||||
Edit3,
|
||||
ExternalLink,
|
||||
ShieldAlert
|
||||
} from 'lucide-react';
|
||||
|
||||
interface WalkthroughTailorProps {
|
||||
@@ -50,7 +56,21 @@ export const WalkthroughTailor: React.FC<WalkthroughTailorProps> = ({
|
||||
insights: string;
|
||||
} | null>(null);
|
||||
|
||||
const [copiedType, setCopiedType] = useState<'resume' | 'cover' | null>(null);
|
||||
// Shoulder Surfing Mode State
|
||||
const [isSurfingActive, setIsSurfingActive] = useState(false);
|
||||
const [surfingStepIndex, setSurfingStepIndex] = useState(0);
|
||||
const [isSurfingPaused, setIsSurfingPaused] = useState(false);
|
||||
|
||||
const [surfingFormData, setSurfingFormData] = useState({
|
||||
fullName: userProfile.fullName || 'David Kifer',
|
||||
email: userProfile.email || 'dkifer19@gmail.com',
|
||||
phone: userProfile.phone || '(555) 019-2831',
|
||||
linkedin: userProfile.linkedin || 'https://linkedin.com/in/david-kifer',
|
||||
yearsExperience: '8+ Years',
|
||||
workAuthorization: 'US Citizen / Authorized',
|
||||
tailoredBulletsText: '',
|
||||
customCoverLetterText: ''
|
||||
});
|
||||
|
||||
// Step 1: Parse Job posting details using Gemini
|
||||
const handleParsePosting = async () => {
|
||||
@@ -254,6 +274,13 @@ export const WalkthroughTailor: React.FC<WalkthroughTailorProps> = ({
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={() => setIsSurfingActive(true)}
|
||||
className="px-4 py-2.5 rounded-xl bg-gradient-to-r from-amber-500 to-amber-600 hover:from-amber-400 hover:to-amber-500 text-slate-950 font-bold text-xs shadow-lg shadow-amber-500/20 flex items-center gap-1.5 transition-all cursor-pointer"
|
||||
>
|
||||
<Eye className="w-4 h-4" />
|
||||
<span>Launch Live Shoulder-Surfing Mode</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleSubmitApplication('Applied')}
|
||||
disabled={isSubmitting}
|
||||
@@ -390,6 +417,158 @@ export const WalkthroughTailor: React.FC<WalkthroughTailorProps> = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Shoulder Surfing Mode Interactive Live Modal */}
|
||||
{isSurfingActive && (
|
||||
<div className="fixed inset-0 bg-slate-950/90 backdrop-blur-lg flex items-center justify-center p-4 z-50 animate-fadeIn">
|
||||
<div className="bg-slate-900 border border-amber-500/50 rounded-2xl max-w-4xl w-full p-6 shadow-2xl space-y-5 max-h-[90vh] overflow-y-auto">
|
||||
|
||||
{/* Modal Header */}
|
||||
<div className="flex items-center justify-between pb-4 border-b border-slate-800">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-xl bg-amber-500/20 text-amber-400 border border-amber-500/40 flex items-center justify-center font-extrabold shadow-lg">
|
||||
<Eye className="w-5 h-5 animate-pulse" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-sm font-bold text-white flex items-center gap-2">
|
||||
<span>Shoulder-Surfing Live Inspector Mode</span>
|
||||
<span className="px-2 py-0.5 rounded-md bg-emerald-950 text-emerald-300 border border-emerald-500/30 text-[10px] uppercase tracking-wider font-semibold">Live Visualizer</span>
|
||||
</h3>
|
||||
<p className="text-xs text-slate-400">Watch the AI auto-fill the application step-by-step and modify any field before final submission.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={() => setIsSurfingPaused(!isSurfingPaused)}
|
||||
className="px-3 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-semibold flex items-center gap-1.5"
|
||||
>
|
||||
{isSurfingPaused ? <Play className="w-3.5 h-3.5 text-emerald-400" /> : <Pause className="w-3.5 h-3.5 text-amber-400" />}
|
||||
<span>{isSurfingPaused ? 'Resume AI' : 'Pause AI'}</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIsSurfingActive(false)}
|
||||
className="text-slate-400 hover:text-white text-xs px-2"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Simulated Live Web Browser Window Frame */}
|
||||
<div className="border border-slate-800 rounded-xl bg-slate-950 overflow-hidden shadow-inner">
|
||||
|
||||
{/* Browser Address Bar */}
|
||||
<div className="bg-slate-900 px-4 py-2 border-b border-slate-800 flex items-center gap-3 text-xs text-slate-400">
|
||||
<div className="flex gap-1.5">
|
||||
<span className="w-3 h-3 rounded-full bg-rose-500/80 inline-block"></span>
|
||||
<span className="w-3 h-3 rounded-full bg-amber-500/80 inline-block"></span>
|
||||
<span className="w-3 h-3 rounded-full bg-emerald-500/80 inline-block"></span>
|
||||
</div>
|
||||
<div className="flex-1 bg-slate-950 border border-slate-800 rounded-lg px-3 py-1 text-[11px] font-mono text-slate-300 flex items-center justify-between">
|
||||
<span className="truncate">{jobUrl || `https://boards.greenhouse.io/${companyName.toLowerCase() || 'company'}/jobs/apply`}</span>
|
||||
<span className="text-emerald-400 text-[10px] font-semibold flex items-center gap-1">
|
||||
<CheckCircle2 className="w-3 h-3" /> SSL Verified
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Form Inspection & Field Editor Grid */}
|
||||
<div className="p-5 space-y-4">
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<h4 className="text-xs font-bold text-slate-200 flex items-center gap-2">
|
||||
<Edit3 className="w-4 h-4 text-indigo-400" />
|
||||
Interactive Form Field Auto-Filler (Review & Edit Any Field)
|
||||
</h4>
|
||||
<span className="text-[11px] text-amber-400 font-semibold">AI Assistant: Active</span>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-xs">
|
||||
<div>
|
||||
<label className="block text-slate-400 mb-1 font-semibold">Full Name</label>
|
||||
<input
|
||||
type="text"
|
||||
value={surfingFormData.fullName}
|
||||
onChange={(e) => setSurfingFormData({ ...surfingFormData, fullName: e.target.value })}
|
||||
className="w-full bg-slate-900 border border-slate-800 rounded-xl px-3 py-2 text-slate-100 focus:border-amber-400 focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-slate-400 mb-1 font-semibold">Email Address</label>
|
||||
<input
|
||||
type="text"
|
||||
value={surfingFormData.email}
|
||||
onChange={(e) => setSurfingFormData({ ...surfingFormData, email: e.target.value })}
|
||||
className="w-full bg-slate-900 border border-slate-800 rounded-xl px-3 py-2 text-slate-100 focus:border-amber-400 focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-slate-400 mb-1 font-semibold">Phone Number</label>
|
||||
<input
|
||||
type="text"
|
||||
value={surfingFormData.phone}
|
||||
onChange={(e) => setSurfingFormData({ ...surfingFormData, phone: e.target.value })}
|
||||
className="w-full bg-slate-900 border border-slate-800 rounded-xl px-3 py-2 text-slate-100 focus:border-amber-400 focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-slate-400 mb-1 font-semibold">LinkedIn Profile URL</label>
|
||||
<input
|
||||
type="text"
|
||||
value={surfingFormData.linkedin}
|
||||
onChange={(e) => setSurfingFormData({ ...surfingFormData, linkedin: e.target.value })}
|
||||
className="w-full bg-slate-900 border border-slate-800 rounded-xl px-3 py-2 text-slate-100 focus:border-amber-400 focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-slate-400 mb-1 font-semibold text-xs">AI Tailored Resume Bullets Inserted into Form</label>
|
||||
<textarea
|
||||
rows={3}
|
||||
value={surfingFormData.tailoredBulletsText || (tailorResult?.tailoredBullets || []).join('\n')}
|
||||
onChange={(e) => setSurfingFormData({ ...surfingFormData, tailoredBulletsText: e.target.value })}
|
||||
className="w-full bg-slate-900 border border-slate-800 rounded-xl p-3 text-slate-100 text-xs leading-relaxed focus:border-amber-400 focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Action Bar */}
|
||||
<div className="flex items-center justify-between pt-2">
|
||||
<span className="text-xs text-slate-400">
|
||||
You have full manual override control over all auto-filled inputs.
|
||||
</span>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={() => setIsSurfingActive(false)}
|
||||
className="px-4 py-2.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-300 text-xs font-semibold"
|
||||
>
|
||||
Cancel Mode
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
handleSubmitApplication('Applied');
|
||||
setIsSurfingActive(false);
|
||||
}}
|
||||
className="px-6 py-2.5 rounded-xl bg-emerald-600 hover:bg-emerald-500 text-white font-bold text-xs shadow-lg shadow-emerald-600/20 flex items-center gap-1.5"
|
||||
>
|
||||
<CheckCircle2 className="w-4 h-4" />
|
||||
<span>Approve & Submit Application</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user