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:
@@ -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 });
|
||||||
|
|||||||
+13
-1
@@ -90,12 +90,14 @@ 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));
|
||||||
|
try {
|
||||||
await updateApplicationStatus(id, newStatus, note);
|
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(),
|
||||||
@@ -109,11 +111,21 @@ export default function App() {
|
|||||||
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));
|
||||||
|
try {
|
||||||
await deleteApplicationRecord(id);
|
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'>) => {
|
||||||
|
|||||||
@@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user