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
+28 -16
View File
@@ -90,30 +90,42 @@ export default function App() {
// Handlers
const handleUpdateStatus = async (id: string, newStatus: ApplicationStatus, note?: string) => {
const previousApps = [...applications];
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
if (['Interviewing', 'Offer', 'Screening'].includes(newStatus)) {
const app = applications.find(a => a.id === id);
if (app) {
const notif = await saveNotificationLog({
timestamp: new Date().toISOString(),
type: 'STATUS_CHANGE',
recipient: userProfile.email,
subject: `Application Status Alert: ${app.company} — ${newStatus}`,
body: `Your job application for ${app.role} at ${app.company} has moved to ${newStatus}.`,
status: 'SENT',
platform: 'Gmail'
});
setNotifications(prev => [notif, ...prev]);
// Send email alert for important status updates
if (['Interviewing', 'Offer', 'Screening'].includes(newStatus)) {
const app = previousApps.find(a => a.id === id);
if (app) {
const notif = await saveNotificationLog({
timestamp: new Date().toISOString(),
type: 'STATUS_CHANGE',
recipient: userProfile.email,
subject: `Application Status Alert: ${app.company} — ${newStatus}`,
body: `Your job application for ${app.role} at ${app.company} has moved to ${newStatus}.`,
status: 'SENT',
platform: 'Gmail'
});
setNotifications(prev => [notif, ...prev]);
}
}
} catch (err) {
console.error('Failed to persist status update, rolling back:', err);
setApplications(previousApps);
}
};
const handleDeleteApplication = async (id: string) => {
const previousApps = [...applications];
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'>) => {
+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">
{app.role}
{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
</span>
)}
+17
View File
@@ -77,6 +77,23 @@ export const WalkthroughTailor: React.FC<WalkthroughTailorProps> = ({
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
const handleParsePosting = async () => {
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.');
}
const startTime = Date.now();
const interval = setInterval(async () => {
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) {
clearInterval(interval);
if (!cachedAccessToken) {