Skip to content

feat: add google drive resume workflow#27

Open
vitorhugo-java wants to merge 6 commits intomainfrom
feat/google-drive-resume-integration
Open

feat: add google drive resume workflow#27
vitorhugo-java wants to merge 6 commits intomainfrom
feat/google-drive-resume-integration

Conversation

@vitorhugo-java
Copy link
Copy Markdown
Owner

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:

  • Added a new API module (googleDrive.js) for interacting with Google Drive, including functions to fetch and update settings, create resume copies, and manage the connection.
  • Integrated Google Drive settings and resume creation into the application form, including state management, loading logic, and error handling. [1] [2] [3]

Application Form Enhancements:

  • Added UI components to the application form for Google Drive resume tools, including buttons to open Gemini and create a resume, a dropdown for selecting a base resume, and contextual messaging based on integration status.
  • Implemented the logic to handle resume creation, including validation, application creation (if needed), and opening the resulting Google Doc in a new tab.

Utility Functions:

  • Added new utility functions in externalLinks.js for opening external URLs, handling pending tabs, and navigating opened tabs, used for the Gemini and resume creation features.

Code Structure Improvements:

  • Refactored payload building for application creation/update into a helper function (buildApplicationPayload) for reuse and clarity. [1] [2]
  • Consolidated gamification event recording into a single helper function to reduce code duplication and improve error handling. [1] [2]

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@vitorhugo-java
Copy link
Copy Markdown
Owner Author

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/api/googleDrive.js Outdated
Comment thread src/api/googleDrive.js
Comment on lines +4 to +13
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),
}
Comment on lines +331 to +386
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>
@vitorhugo-java
Copy link
Copy Markdown
Owner Author

@copilot apply changes based on the comments in this thread

Copy link
Copy Markdown
Contributor

Copilot AI commented May 5, 2026

@copilot apply changes based on the comments in this thread

Applied in commit d840640:

normalizeBaseResume synthetic ID fix (src/api/googleDrive.js): Changed id: resume?.id ?? \resume-${index + 1}`toid: resume?.id ?? null. Guarded the DELETE call in updateGoogleDriveSettingswithresume.id &&so resumes without a real backend ID are never passed toDELETE /base-resumes/:id, and guarded the PUT call with existingResume.id` for the same reason.

E2E tests (tests/google-drive-resume.spec.ts): Added 5 Playwright tests that mock the /google-drive/status endpoint and verify:

  • Button is disabled when not connected
  • Button is disabled while settings are loading (deferred-promise latch, no hardcoded delay)
  • Button is enabled once fully connected with settings
  • Resume creation POST includes the correct baseResumeId
  • Error toast shown when the application creation returns queuedOffline: true

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 7 changed files in this pull request and generated 2 comments.

Comment thread src/api/googleDrive.js Outdated
Comment on lines +52 to +56
const existingById = new Map(
currentSettings.baseResumes.map((resume) => [resume.id, resume])
)
const existingByDocumentId = new Map(
currentSettings.baseResumes.map((resume) => [resume.documentId, resume])
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied in commit 0c25d93:

  • existingById is now built with .filter((resume) => resume.id) before mapping, so null-id resumes are never inserted as a null key that could cause false matches.
  • existingByDocumentId received the same filter for documentId for symmetry.
  • In the sync loop, existingById is only consulted when resume.id is truthy: (resume.id ? existingById.get(resume.id) : undefined) ?? existingByDocumentId.get(resume.documentId), so a null id never hits the map at all.

Comment thread src/api/googleDrive.js
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants