feat: add google drive resume workflow#27
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces a Google Drive-based resume workflow into the job application flow, letting users configure Google Drive connection/settings in Account Settings and then create/open a Google Docs resume copy (plus a quick link to Gemini) directly from the Application form.
Changes:
- Added Google Drive utilities and an API module to normalize/load settings, start/disconnect OAuth, and create resume copies.
- Enhanced ApplicationForm with “Resume tools” UI, settings-loading state, and a resume-copy creation flow (including creating the application first when needed).
- Enhanced AccountSettings with Google Drive connection management plus a UI for configuring the base folder and base resumes.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/googleDrive.js | Adds helpers to extract Google Doc/Drive folder IDs and build canonical URLs. |
| src/utils/externalLinks.js | Adds safe-ish helpers for opening external URLs and navigating a “pending” tab. |
| src/api/googleDrive.js | Adds Google Drive API wrappers plus response normalization for settings/resume-copy creation. |
| src/pages/applications/ApplicationForm.jsx | Loads Google Drive settings, adds resume tools UI, and implements resume-copy creation flow. |
| src/pages/account/AccountSettings.jsx | Adds Google Drive connection + settings management UI and dirty-state tracking integration. |
| const normalizeBaseResume = (resume, index) => { | ||
| const documentId = resume?.documentId ?? resume?.googleDocId ?? resume?.googleFileId ?? '' | ||
|
|
||
| return { | ||
| id: resume?.id ?? `resume-${index + 1}`, | ||
| name: resume?.name ?? resume?.label ?? resume?.documentName ?? `Resume ${index + 1}`, | ||
| documentId, | ||
| documentUrl: resume?.documentUrl ?? resume?.googleDocUrl ?? resume?.webViewLink ?? buildGoogleDocUrl(documentId), | ||
| isDefault: Boolean(resume?.isDefault), | ||
| } |
| const handleCreateResume = async () => { | ||
| const selectedResume = googleDriveSettings.baseResumes.find((resume) => resume.id === selectedBaseResumeId) | ||
| ?? googleDriveSettings.baseResumes[0] | ||
|
|
||
| if (!googleDriveSettings.connected) { | ||
| toast.current?.show({ | ||
| severity: 'warn', | ||
| summary: 'Resume tools unavailable', | ||
| detail: 'Connect your Google account in Account Settings before creating a resume.', | ||
| }) | ||
| return | ||
| } | ||
|
|
||
| if (!googleDriveSettings.baseFolderId || !selectedResume?.id) { | ||
| toast.current?.show({ | ||
| severity: 'warn', | ||
| summary: 'Resume tools unavailable', | ||
| detail: 'Configure a base Drive folder and at least one base resume in Account Settings.', | ||
| }) | ||
| return | ||
| } | ||
|
|
||
| if (!isEdit && !form.toSendLater && !form.applicationDate) { | ||
| toast.current?.show({ | ||
| severity: 'error', | ||
| summary: 'Validation', | ||
| detail: 'Application date is required unless "To send later" is enabled.', | ||
| }) | ||
| return | ||
| } | ||
|
|
||
| const pendingTab = openPendingTab() | ||
| setCreatingResume(true) | ||
|
|
||
| try { | ||
| let applicationId = id | ||
| let createdApplicationId = null | ||
|
|
||
| if (!isEdit) { | ||
| const payload = buildApplicationPayload(form) | ||
| const createResponse = await createApplication(payload) | ||
|
|
||
| if (createResponse.data?.queuedOffline || !createResponse.data?.id) { | ||
| throw new Error('The application must be saved online before creating a Google Docs resume.') | ||
| } | ||
|
|
||
| createdApplicationId = createResponse.data.id | ||
| applicationId = createResponse.data.id | ||
| window.localStorage.removeItem(draftKey) | ||
| await recordCreatedApplicationEvents(applicationId, payload) | ||
| } | ||
|
|
||
| const response = await createGoogleDriveResume({ | ||
| applicationId, | ||
| baseResumeId: selectedResume.id, | ||
| }) |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
@copilot apply changes based on the comments in this thread |
…ume E2E tests Agent-Logs-Url: https://github.com/vitorhugo-java/React-JobApplyTracker/sessions/001ac48e-c554-48ee-adc3-439096089cb2 Co-authored-by: vitorhugo-java <65777252+vitorhugo-java@users.noreply.github.com>
Agent-Logs-Url: https://github.com/vitorhugo-java/React-JobApplyTracker/sessions/001ac48e-c554-48ee-adc3-439096089cb2 Co-authored-by: vitorhugo-java <65777252+vitorhugo-java@users.noreply.github.com>
Applied in commit d840640:
E2E tests (
|
| const existingById = new Map( | ||
| currentSettings.baseResumes.map((resume) => [resume.id, resume]) | ||
| ) | ||
| const existingByDocumentId = new Map( | ||
| currentSettings.baseResumes.map((resume) => [resume.documentId, resume]) |
There was a problem hiding this comment.
Applied in commit 0c25d93:
existingByIdis now built with.filter((resume) => resume.id)before mapping, so null-id resumes are never inserted as anullkey that could cause false matches.existingByDocumentIdreceived the same filter fordocumentIdfor symmetry.- In the sync loop,
existingByIdis only consulted whenresume.idis truthy:(resume.id ? existingById.get(resume.id) : undefined) ?? existingByDocumentId.get(resume.documentId), so a null id never hits the map at all.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…in updateGoogleDriveSettings Agent-Logs-Url: https://github.com/vitorhugo-java/React-JobApplyTracker/sessions/5955c1c5-0b96-4202-8780-07a9e8b8da20 Co-authored-by: vitorhugo-java <65777252+vitorhugo-java@users.noreply.github.com>
This pull request adds Google Drive resume integration to the application form, allowing users to create a Google Docs resume copy from a configured base resume and open Gemini directly from the form. The main changes include implementing the Google Drive API integration, updating the application form UI and logic to support resume creation, and adding utility functions for handling external links.
Google Drive Integration:
googleDrive.js) for interacting with Google Drive, including functions to fetch and update settings, create resume copies, and manage the connection.Application Form Enhancements:
Utility Functions:
externalLinks.jsfor opening external URLs, handling pending tabs, and navigating opened tabs, used for the Gemini and resume creation features.Code Structure Improvements:
buildApplicationPayload) for reuse and clarity. [1] [2]