Fix subagent audit bugs: optimistic state rollback, reactive surfing state sync, Tailwind classes, JSON parsing, null checks, and popup intervals

This commit is contained in:
2026-07-23 13:43:58 -04:00
parent 69d4857d04
commit ed947f334c
6 changed files with 64 additions and 23 deletions
+1 -1
View File
@@ -1 +1 @@
274566 275088
+9 -5
View File
@@ -221,7 +221,8 @@ Extract the following fields accurately and output JSON:
}; };
const { text, providerUsed } = await generateTextWithLLMFallback(prompt, schema, llmConfig); const { text, providerUsed } = await generateTextWithLLMFallback(prompt, schema, llmConfig);
const parsedData = JSON.parse(text || "{}"); const cleanJson = (text || "{}").replace(/```json/gi, '').replace(/```/g, '').trim();
const parsedData = JSON.parse(cleanJson || "{}");
res.json({ success: true, data: parsedData, providerUsed }); res.json({ success: true, data: parsedData, providerUsed });
} catch (error: any) { } catch (error: any) {
console.error("Error parsing job URL:", error); console.error("Error parsing job URL:", error);
@@ -455,8 +456,8 @@ Candidate Criteria:
- Excluded Companies: ${excludedCompanies.join(", ")} - Excluded Companies: ${excludedCompanies.join(", ")}
Candidate Profile: Candidate Profile:
- Skills: ${userProfile.skills?.join(", ")} - Skills: ${userProfile?.skills?.join(", ") || "Cybersecurity, Python, Incident Response"}
- Summary: ${userProfile.summary} - Summary: ${userProfile?.summary || "Cybersecurity & IT Specialist"}
List of Jobs to Evaluate: List of Jobs to Evaluate:
${JSON.stringify(jobFeeds)} ${JSON.stringify(jobFeeds)}
@@ -943,8 +944,11 @@ app.post("/api/local/data", (req, res) => {
try { store = JSON.parse(fs.readFileSync(DATA_FILE, "utf-8") || "{}"); } catch (e) {} try { store = JSON.parse(fs.readFileSync(DATA_FILE, "utf-8") || "{}"); } catch (e) {}
} }
if (key) { if (key) {
if (items !== undefined) store[key] = { items }; store[key] = {
if (profile !== undefined) store[key] = { profile }; ...(store[key] || {}),
...(items !== undefined && { items }),
...(profile !== undefined && { profile })
};
} }
fs.writeFileSync(DATA_FILE, JSON.stringify(store, null, 2)); fs.writeFileSync(DATA_FILE, JSON.stringify(store, null, 2));
res.json({ success: true }); res.json({ success: true });
+28 -16
View File
@@ -90,30 +90,42 @@ export default function App() {
// Handlers // Handlers
const handleUpdateStatus = async (id: string, newStatus: ApplicationStatus, note?: string) => { const handleUpdateStatus = async (id: string, newStatus: ApplicationStatus, note?: string) => {
const previousApps = [...applications];
setApplications(prev => prev.map(a => a.id === id ? { ...a, status: newStatus } : a)); setApplications(prev => prev.map(a => a.id === id ? { ...a, status: newStatus } : a));
await updateApplicationStatus(id, newStatus, note); try {
await updateApplicationStatus(id, newStatus, note);
// Send email alert for important status updates // Send email alert for important status updates
if (['Interviewing', 'Offer', 'Screening'].includes(newStatus)) { if (['Interviewing', 'Offer', 'Screening'].includes(newStatus)) {
const app = applications.find(a => a.id === id); const app = previousApps.find(a => a.id === id);
if (app) { if (app) {
const notif = await saveNotificationLog({ const notif = await saveNotificationLog({
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
type: 'STATUS_CHANGE', type: 'STATUS_CHANGE',
recipient: userProfile.email, recipient: userProfile.email,
subject: `Application Status Alert: ${app.company} — ${newStatus}`, subject: `Application Status Alert: ${app.company} — ${newStatus}`,
body: `Your job application for ${app.role} at ${app.company} has moved to ${newStatus}.`, body: `Your job application for ${app.role} at ${app.company} has moved to ${newStatus}.`,
status: 'SENT', status: 'SENT',
platform: 'Gmail' platform: 'Gmail'
}); });
setNotifications(prev => [notif, ...prev]); setNotifications(prev => [notif, ...prev]);
}
} }
} catch (err) {
console.error('Failed to persist status update, rolling back:', err);
setApplications(previousApps);
} }
}; };
const handleDeleteApplication = async (id: string) => { const handleDeleteApplication = async (id: string) => {
const previousApps = [...applications];
setApplications(prev => prev.filter(a => a.id !== id)); setApplications(prev => prev.filter(a => a.id !== id));
await deleteApplicationRecord(id); try {
await deleteApplicationRecord(id);
} catch (err) {
console.error('Failed to delete application, rolling back:', err);
setApplications(previousApps);
}
}; };
const handleSaveApplicationRecord = async (appData: Omit<JobApplication, 'id'>) => { const handleSaveApplicationRecord = async (appData: Omit<JobApplication, 'id'>) => {
+1 -1
View File
@@ -269,7 +269,7 @@ export const ApplicationsTracker: React.FC<ApplicationsTrackerProps> = ({
<div className="font-semibold text-white group-hover:text-indigo-300 transition-colors flex items-center gap-1.5"> <div className="font-semibold text-white group-hover:text-indigo-300 transition-colors flex items-center gap-1.5">
{app.role} {app.role}
{app.autoApplied && ( {app.autoApplied && (
<span className="inline-flex items-center gap-0.5 px-1.5 py-0.2 rounded text-[10px] font-bold bg-indigo-500/20 text-indigo-300 border border-indigo-500/30" title="Autonomous AI Applied"> <span className="inline-flex items-center gap-0.5 px-1.5 py-0.5 rounded text-[10px] font-bold bg-indigo-500/20 text-indigo-300 border border-indigo-500/30" title="Autonomous AI Applied">
<Bot className="w-2.5 h-2.5" /> AI <Bot className="w-2.5 h-2.5" /> AI
</span> </span>
)} )}
+17
View File
@@ -77,6 +77,23 @@ export const WalkthroughTailor: React.FC<WalkthroughTailorProps> = ({
customCoverLetterText: '' customCoverLetterText: ''
}); });
// Keep surfingFormData reactively synchronized with userProfile and tailorResult updates
React.useEffect(() => {
setSurfingFormData(prev => ({
...prev,
fullName: userProfile.fullName || prev.fullName,
email: userProfile.email || prev.email,
phone: userProfile.phone || prev.phone,
linkedin: userProfile.linkedinUrl || prev.linkedin,
workAuthorization: userProfile.eeoDeclarations?.workAuthorization || prev.workAuthorization,
veteranStatus: userProfile.eeoDeclarations?.veteranStatus === 'PROTECTED_VETERAN' ? 'Protected Veteran' : 'Not a Veteran',
disabilityStatus: userProfile.eeoDeclarations?.disabilityStatus === 'YES_DISABILITY' ? 'Yes, Disability' : 'No Disability',
clearanceLevel: userProfile.eeoDeclarations?.clearanceLevel || prev.clearanceLevel,
tailoredBulletsText: tailorResult?.tailoredBullets?.join('\n• ') || prev.tailoredBulletsText,
customCoverLetterText: tailorResult?.coverLetter || prev.customCoverLetterText
}));
}, [userProfile, tailorResult]);
// Step 1: Parse Job posting details using Gemini // Step 1: Parse Job posting details using Gemini
const handleParsePosting = async () => { const handleParsePosting = async () => {
if (!jobUrl && !jobDescriptionText) return; if (!jobUrl && !jobDescriptionText) return;
+8
View File
@@ -65,8 +65,16 @@ export const googleWorkspaceSignIn = async (silentMode: boolean = false): Promis
throw new Error('Popup window was blocked by your browser. Please allow popups for http://localhost:3000.'); throw new Error('Popup window was blocked by your browser. Please allow popups for http://localhost:3000.');
} }
const startTime = Date.now();
const interval = setInterval(async () => { const interval = setInterval(async () => {
try { try {
if (Date.now() - startTime > 60000) {
clearInterval(interval);
try { popup.close(); } catch (e) {}
reject(new Error('Sign-in process timed out after 60 seconds.'));
return;
}
if (popup.closed) { if (popup.closed) {
clearInterval(interval); clearInterval(interval);
if (!cachedAccessToken) { if (!cachedAccessToken) {