Skip to main content

Frontend Critical Fixes — 2026-03-04

Audit date: March 4, 2026 Scope: Critical severity issues from the frontend audit covering GraphQL schema alignment, auth bugs, and broken features. Total fixes: 9 critical issues resolved


C1. SUBSCRIPTION_FIELDS fragment — wrong GraphQL type

File: graphql/operations/schema/payment/fragments.ts Severity: Critical — all payment/subscription queries returned empty data

Problem: The SubscriptionFields fragment targeted Subscription (the reserved GraphQL root type for WebSocket subscriptions) instead of BillingSubscription (the actual backend type). The fragment never matched, so all payment queries using it silently received empty subscription data. Additionally, the fragment queried userId which is deprecated on BillingSubscription.

Fix:

  • Changed fragment target from on Subscription to on BillingSubscription
  • Replaced deprecated userId field with companyId

C2. UPDATE_USER_THEME — missing required argument + wrong field name

Files:

  • graphql/operations/user/mutations.ts
  • contexts/ThemeContext.tsx
  • lib/types/user.ts

Severity: Critical — theme saving completely broken

Problem: Three separate issues:

  1. Missing required $id: ID! argument — backend signature is updateUser(id: ID!, input: UpdateUserInput!)
  2. Mutation returned themeId which doesn't exist on User type (correct field: theme)
  3. UpdateUserInput accepts theme: String, not themeId

The ThemeContext read user?.themeId (non-existent field) and sent { themeId: newThemeId } (wrong input key).

Fix:

  • Added $id: ID! argument to mutation and pass user.id
  • Changed return field from themeId to theme
  • Updated ThemeContext to read user?.theme and send { theme: newThemeId }
  • Updated User TypeScript interface: themeIdtheme

C3. UPDATE_TEAM_MEMBER — invalid GraphQL syntax

File: graphql/operations/user/mutations.ts

Severity: Critical — GraphQL validation error at runtime

Problem: The mutation mixed TypeScript type annotations into a GraphQL document:

inviteToCompany(input: $input) {
email: string # NOT valid GraphQL
companyId: string # NOT valid GraphQL
}

Additionally, the input type inviteUserToCompanyInput (lowercase) didn't match the backend type InviteToCompanyInput.

Fix:

  • Rewrote with valid GraphQL returning success and message (matches backend SuccessResponse!)
  • Fixed input type to InviteToCompanyInput
  • Fixed operation name casing to InviteUserToCompany

C4. addTalentToPool — mutation does not exist in backend

File: graphql/operations/talents/mutations.ts

Severity: Critical — runtime GraphQL error if called

Problem: The addTalentToPool mutation does not exist in the backend schema. Talent pool membership is managed via the Talent entity (createTalentInput.talentPoolId). The mutation was completely unused in the codebase.

Fix:

  • Removed the entire ADD_TALENT_TO_POOL mutation definition (lines 47-79)
  • Updated SYSTEM_ARCHITECTURE.md to remove references

C5. createCandidateWithProfile — mutation does not exist in backend

File: graphql/operations/candidate/mutations.ts

Severity: Critical — runtime GraphQL error if called

Problem: The mutation createCandidateWithProfile, its input type CreateCandidateWithProfileInput, and its return fields (resumeUrl, experiences, certificates, etc.) do not exist in the backend schema. This was a planned feature that was never implemented. The mutation was completely unused in the codebase.

Fix:

  • Removed the entire CREATE_CANDIDATE_WITH_PROFILE mutation definition (lines 62-119)
  • Updated SYSTEM_ARCHITECTURE.md to remove references

C6. parseCv — mutation does not exist in backend

Files:

  • graphql/operations/cv/mutations.ts
  • lib/hooks/useCvManager.ts
  • lib/hooks/useCvUploadAndParse.ts

Severity: Critical — runtime GraphQL error if called

Problem: The backend has no parseCv mutation. CV parsing is handled by the background-tasks service via the cvExtraction GraphQL subscription. The PARSE_CV mutation was only referenced by two hooks (useCvManager, useCvUploadAndParse).

Fix (Reverted 2026-03-04):

  • PARSE_CV mutation, useCvManager.ts, and useCvUploadAndParse.ts have been restored — they are needed for the CV upload and parse flow
  • These hooks are marked as deprecated in docs; new code should prefer useCvProcessor
  • Updated DOCS/CV_REFACTORING_SUMMARY.md and DOCS/CV_UNIFIED_WEBSOCKET_SERVICE.md — status set to "deprecated"

C7. TalentSource = INTERNAL — enum value does not exist

File: graphql/operations/talents/mutations.ts (line 51)

Severity: Critical — GraphQL validation error

Problem: The default value TalentSource = INTERNAL used in the ADD_TALENT_TO_POOL mutation does not exist in the backend TalentSource enum. Valid values include: MANUAL_ENTRY, INTERNAL_TRANSFER, CV_UPLOAD, LINKEDIN_IMPORT, etc.

Fix:

  • Resolved automatically by removing the ADD_TALENT_TO_POOL mutation in C4 (the only location using this invalid enum value)

C8. Jobseeker contacts page — entirely mock data

Files:

  • app/jobseeker/connections/contacts/page.tsx (rewritten)
  • app/jobseeker/connections/_mockData.ts (deleted)

Severity: Critical — production page shows static fake data

Problem: The entire jobseeker contacts page was populated from MOCK_CONTACTS hardcoded data. All CRUD operations had TODO comments and only called console.log. In production, job seekers saw static fake data that couldn't be interacted with. The backend has no jobseeker-specific contacts query (contacts are company-scoped).

Fix:

  • Replaced the 478-line mock page with a clean "Coming Soon" placeholder
  • Deleted the _mockData.ts file containing MOCK_CONTACTS and MockContact type
  • Page now shows a clear message that the feature is under development

C9. Stale closure — session idle timer never calls logout

File: contexts/UserAuthProvider.tsx

Severity: Critical — auto-logout after inactivity silently fails

Problem: startIdleTimer and checkSessionExpiry were wrapped in useCallback(fn, []) (empty deps) but both referenced logout() which is defined later in the file. The closures captured the initial undefined value of logout, so when the timer fired, it called undefined() and crashed silently. Users were never logged out after 5 hours of inactivity.

Additionally, line 75 had a stale comment saying "3 hours" when MAX_SESSION_AGE is 5 hours.

Fix:

  • Added a logoutRef = useRef<() => void>(() => {}) that always points to the current logout function
  • Changed all timer callbacks to call logoutRef.current() instead of logout() directly
  • logoutRef.current is updated after logout is defined, so the ref always has the correct function
  • Fixed stale comment from "3 hours" to "5 hours"

Files Changed Summary

FileAction
graphql/operations/schema/payment/fragments.tsFixed fragment type + field
graphql/operations/user/mutations.tsFixed 2 mutations (C2, C3)
graphql/operations/talents/mutations.tsRemoved dead mutation (C4/C7)
graphql/operations/candidate/mutations.tsRemoved dead mutation (C5)
graphql/operations/cv/mutations.tsRemoved dead mutation (C6)
contexts/ThemeContext.tsxFixed theme field references + added user.id
contexts/UserAuthProvider.tsxFixed stale closure with logoutRef
lib/types/user.tsRenamed themeId → theme
app/jobseeker/connections/contacts/page.tsxReplaced mock page with Coming Soon
app/jobseeker/connections/_mockData.tsDeleted
lib/hooks/useCvManager.tsRestored (deprecated)
lib/hooks/useCvUploadAndParse.tsRestored (deprecated)
DOCS/CV_REFACTORING_SUMMARY.mdUpdated deprecation status
DOCS/CV_UNIFIED_WEBSOCKET_SERVICE.mdUpdated deprecation status
SYSTEM_ARCHITECTURE.mdRemoved references to deleted mutations

High Severity Fixes

H1. CREATE_PLAN — wrong argument name + non-existent return fields

File: graphql/operations/payment/mutations.ts

Fix: Changed createPlan(createPlanInput: $input) to createPlan(input: $input). Removed stripePriceId and stripeProductId (don't exist on Plan), replaced with active.

H2. REMOVE_PLAN — wrong return type

File: graphql/operations/payment/mutations.ts

Fix: Backend returns SuccessResponse!, not { id }. Changed return fields to success and message.

H3. REMOVE_CANDIDATE_NOTE — wrong return type

File: graphql/operations/candidate/mutations.ts

Fix: Backend returns SuccessResponse!, not individual note fields. Changed return to success and message.

H4. GET_USER_ROLES — wrong field names on Company

File: graphql/operations/user/queries.ts

Fix: Changed company { name, logo } to company { companyName, avatar } — the correct field names on the Company type.

H5. GET_ADMIN_AI_OPERATION_LOGS — wrong argument + return shape

File: graphql/operations/credit/queries.ts

Fix: Changed argument from filter: AIOperationLogFilterInput (optional) to input: AdminAIOperationLogsInput! (required). Changed return shape to paginated format: { items: [...], totalCount, pageInfo: { hasNextPage, hasPreviousPage } }.

H6. adminAssignSubscription — non-existent isActive field

File: graphql/operations/credit/mutations.ts

Fix: Removed isActive from return selection — BillingSubscription has no isActive field.

H7. GET_TALENT — wrong field casing + non-existent field

File: graphql/operations/talents/queries.ts

Fix: Changed TalentPool { id name } (uppercase T) to talentPool { id name } (lowercase). Removed isActive which doesn't exist on the Talent type.

H8. Duplicate AllJobs operation name

File: graphql/operations/job/queries.ts

Fix: Renamed GET_ALL_JOBS_FOR_JOBSEEKER operation from query AllJobs to query AllJobsForJobseeker to avoid cache conflicts with GET_ALL_JOBS_PUBLIC which also used AllJobs.

H9. redirectToSignin leaves stale localStorage keys

File: graphql/apollo/apolloClient.ts

Fix: Added removal of selectedCompanyId, role, and createdAt in redirectToSignin() to match the full cleanup done by logout(). Prevents next session from inheriting the previous user's company context.

H10. Plan usage queries permanently disabled

File: app/jobseeker/credits/page.tsx

Fix: Removed hardcoded skip: true from GET_AI_OPERATION_USAGE_SUMMARY and GET_PLAN_USAGE_SUMMARY queries so they actually execute.

H11. useSubscriptionStatus — status-blind active detection

Files: lib/hooks/useSubscriptionStatus.ts, graphql/operations/payment/queries.ts, lib/types/payment.ts

Fix: Added status field to GET_USER_SUBSCRIPTIONS query. Updated the subscription finder to only consider ACTIVE and TRIALING statuses as active (rejecting PENDING_APPROVAL, UNPAID, etc.). Also replaced deprecated userId with companyId in the query. Added PENDING_APPROVAL to the SubscriptionStatus type.

H12. billingMode completely absent from frontend

Files: graphql/operations/schema/payment/fragments.ts, lib/types/payment.ts

Fix: Added billingMode field to the PLAN_FIELDS fragment and billingMode?: "PREPAID" | "POSTPAID" to the Plan TypeScript interface. This allows the frontend to distinguish enterprise POSTPAID plans from regular PREPAID plans.

H13. Inconsistent authorization header casing

Files: lib/hooks/useAIAgentChat.ts, lib/hooks/useAgentDocuments.ts

Fix: Changed authorization (lowercase) to Authorization (uppercase, standard) in WebSocket connectionParams to match all other hooks in the codebase.

H14. Voice/AI WebSocket URLs default to DEV in production

Files: lib/hooks/useVoiceChat.ts, lib/hooks/useAIAgentChat.ts, lib/hooks/useAgentDocuments.ts

Fix: Changed all fallback URLs from ai-dev.aiqlick.com to ai.aiqlick.com so that if env vars are unset in production, traffic routes to the production service rather than dev.

H15. Admin user list fetches ALL users without pagination

Status: Deferred — requires backend changes. The users query doesn't accept limit/offset arguments. Filed for backend team coordination.

H16. Pipeline member ID collision on same-name users

Files: lib/utils/pipeline.ts, lib/types/jobs.ts

Fix: Changed mapJobsToMembers and mapJobsToCards to use responsibleUser.id (database UUID) instead of firstName-lastName for member ID construction. Added id?: string to the responsibleUser type on Opportunity. Falls back to name-based ID if id is unavailable.

H17. Apollo upload split misses nested File objects

File: graphql/apollo/apolloClient.ts

Fix: Replaced flat Object.values(variables).some(...) check with a recursive hasFileInVariables() helper that traverses nested objects. Now correctly detects { input: { file: someFile } } patterns.

H18. Timezone free-text input accepts invalid values

File: components/meetings/CreateMeetingModal.tsx

Fix: Replaced TWInput (free-text) with TWSelect using a curated list of IANA timezone options (UTC, US timezones, European, Asian, Australian, NZ).


High Severity — Additional Files Changed

FileAction
graphql/operations/payment/mutations.tsFixed CREATE_PLAN + REMOVE_PLAN (H1, H2)
graphql/operations/candidate/mutations.tsFixed REMOVE_CANDIDATE_NOTE (H3)
graphql/operations/user/queries.tsFixed GET_USER_ROLES company fields (H4)
graphql/operations/credit/queries.tsFixed GET_ADMIN_AI_OPERATION_LOGS (H5)
graphql/operations/credit/mutations.tsFixed adminAssignSubscription (H6)
graphql/operations/talents/queries.tsFixed GET_TALENTS casing + field (H7)
graphql/operations/job/queries.tsFixed duplicate AllJobs name (H8)
graphql/apollo/apolloClient.tsFixed redirectToSignin cleanup + upload split (H9, H17)
app/jobseeker/credits/page.tsxEnabled usage queries (H10)
lib/hooks/useSubscriptionStatus.tsFixed status-blind detection (H11)
graphql/operations/payment/queries.tsAdded status to subscriptions query (H11)
graphql/operations/schema/payment/fragments.tsAdded billingMode to PLAN_FIELDS (H12)
lib/types/payment.tsAdded billingMode + PENDING_APPROVAL (H11, H12)
lib/hooks/useAIAgentChat.tsFixed auth header casing + prod URL (H13, H14)
lib/hooks/useAgentDocuments.tsFixed auth header casing + prod URL (H13, H14)
lib/hooks/useVoiceChat.tsFixed prod URL default (H14)
lib/utils/pipeline.tsFixed member ID collision (H16)
lib/types/jobs.tsAdded id to responsibleUser type (H16)
components/meetings/CreateMeetingModal.tsxReplaced timezone input with select (H18)

Medium Severity Fixes

M2. PENDING_APPROVAL missing from SubscriptionsTab UI

File: app/(shared)/userprofile/payment/components/SubscriptionsTab.tsx

Fix: Added PENDING_APPROVAL: { bg: "bg-amber-600", text: "PENDING APPROVAL" } to the statusColors map so subscriptions with this status display correctly.

M3. TalentSource type missing INVITED and SHARED values

File: lib/hooks/talents/useCompanyTalents.ts

Fix: Added "INVITED" and "SHARED" to the TalentSource union type to match backend enum.

M4. JWT token exposed in WebSocket URL query string

File: lib/hooks/useVoiceChat.ts

Fix: Added security comment. Full fix requires backend voice service to accept auth via first WS message instead of URL query string. Deferred for backend coordination.

M5. Deprecated userId still queried on BillingSubscription

File: graphql/operations/payment/queries.ts

Fix: Replaced all userId fields with companyId in GET_SUBSCRIPTION_HISTORY, GET_ACTIVE_SUBSCRIPTION, GET_USER_WITH_SUBSCRIPTIONS, and GET_SUBSCRIPTION queries. Invoice queries retain userId as it is still valid on the Invoice type.

M6. Deprecated updateCvSchema mutation

File: graphql/operations/cv/mutations.ts

Fix: Added @deprecated comment directing new code to use structured CV mutations. Mutation is still in active use by 5 components and cannot be removed yet.

M7. sendEmail checkbox not passed to mutations

File: app/company/resume/candidates/_components/modals/BookInterviewModal.tsx

Fix: Added sendEmail to the createSlotOffer mutation variables so the checkbox state is actually sent to the backend.

M8. CreateMeetingModal defaults timezone to "UTC"

File: components/meetings/CreateMeetingModal.tsx

Fix: Changed default timezone from hardcoded "UTC" to user's detected timezone via Intl.DateTimeFormat().resolvedOptions().timeZone. Also updated handleClose reset to detect timezone.

M9. BookInterviewModal hardcoded duration

File: app/company/resume/candidates/_components/modals/BookInterviewModal.tsx

Fix: Added interviewDuration state with a duration selector dropdown (15/30/45/60/90/120 minutes). Replaced hardcoded duration: 60 in both the availability query and create slot offer mutation.

M10. Reschedule allows multiple slots but only uses first

File: app/company/resume/candidates/_components/modals/BookInterviewModal.tsx

Fix: Added maxSlots variable — set to 1 in reschedule mode, 3 in normal mode. Updated slot limit in add button, validation, and display label.

M11. Calendar already disables past dates

Status: Already handled — isDatePast() check at line 553 disables past date buttons.

M12. Pipeline status change error toast

Status: Already handled — handleCandidateStatusChange in [jobId]/page.tsx has try-catch with addTWToast on failure (line 380).

M17. First poll triggers spurious notification toast

File: contexts/NotificationContext.tsx

Fix: Added isInitialLoadRef to skip toast on initial data load. The cache-and-network policy fires the effect multiple times on mount; the ref ensures the first settled value is recorded as baseline without triggering a toast.

M19. Synthetic notification hardcoded SYSTEM_UPDATE type

File: contexts/NotificationContext.tsx

Fix: Changed synthetic notification type from "SYSTEM_UPDATE" to "FEATURE_ANNOUNCEMENT" which is more appropriate for generic polling-detected notifications.

M20. Non-functional Kanban Assign/Share buttons

File: components/ux/KanbanJobCardAdapter.tsx

Fix: Replaced console.log TODO stubs with "Coming Soon" toast notifications so users get feedback instead of silent no-ops.

Files: components/navigation/CompanySidebar.tsx, components/navigation/JobSeekerSidebar.tsx

Fix: Changed href="#" to href="mailto:support@aiqlick.com" so the help link does something useful.

M22. Auth guard isOnboarding === false logic

File: components/auth/authGuard.tsx

Fix: Added clarifying comment. The strict === false check is intentional — it only redirects to onboarding when isOnboarding is explicitly false, allowing null/undefined to fall through to the role check.

M23. mousemove fires localStorage writes ~60 times/sec

File: contexts/UserAuthProvider.tsx

Fix: Added 5-second throttle to the activity handler using a lastActivityWriteRef. The resetIdleTimer() call (which writes to localStorage and resets the timeout) now fires at most once every 5 seconds instead of on every mouse movement.

M24-M27. Code quality cleanup

Files changed:

  • components/connections/SendConnectionRequestDialog.tsx — Removed 6 debug console.logs
  • app/onboarding/jobseeker/steps/Qualifications.tsx — Removed 5 drag debug console.logs with emojis
  • app/(shared)/userprofile/addCompanyPage.tsx — Removed 5 debug console.logs
  • app/(shared)/userprofile/hooks/useProfileForm.ts — Removed 2 debug console.logs
  • app/(shared)/userprofile/components/AvatarSection.tsx — Removed 1 debug console.log
  • components/ux/TWResponsiveCard/hooks/useFieldVisibility.ts — Removed 10 debug console.logs
  • app/(shared)/userprofile/payment/components/SubscriptionsTab.tsx — Removed 1 debug console.log
  • app/company/connections/collaboratingpartner/_components/CollaboratorsGrid.tsx — Replaced console.log with TODO comment
  • app/(shared)/userprofile/accountPage.tsx — Replaced console.log with "Coming Soon" toast
  • Mock flags (M27): All USE_MOCK_* flags already set to false — no changes needed

Medium Severity — Files Changed Summary

FileAction
app/(shared)/userprofile/payment/components/SubscriptionsTab.tsxAdded PENDING_APPROVAL status + removed console.log (M2, M25)
lib/hooks/talents/useCompanyTalents.tsAdded INVITED, SHARED to TalentSource (M3)
lib/hooks/useVoiceChat.tsAdded security comment for JWT in URL (M4)
graphql/operations/payment/queries.tsReplaced userId with companyId in 5 queries (M5)
graphql/operations/cv/mutations.tsAdded deprecation comment (M6)
app/company/resume/candidates/_components/modals/BookInterviewModal.tsxAdded sendEmail, duration control, reschedule slot limit (M7, M9, M10)
components/meetings/CreateMeetingModal.tsxAuto-detect timezone instead of UTC default (M8)
contexts/NotificationContext.tsxFixed first-poll toast + synthetic notification type (M17, M19)
components/ux/KanbanJobCardAdapter.tsxComing Soon toast for Assign/Share (M20)
components/navigation/CompanySidebar.tsxFixed dead help link (M21)
components/navigation/JobSeekerSidebar.tsxFixed dead help link (M21)
components/auth/authGuard.tsxAdded clarifying comment (M22)
contexts/UserAuthProvider.tsxThrottled mousemove activity tracking (M23)
components/connections/SendConnectionRequestDialog.tsxRemoved debug logs (M25)
app/onboarding/jobseeker/steps/Qualifications.tsxRemoved drag debug logs (M25)
app/(shared)/userprofile/addCompanyPage.tsxRemoved debug logs (M25)
app/(shared)/userprofile/hooks/useProfileForm.tsRemoved debug logs (M25)
app/(shared)/userprofile/components/AvatarSection.tsxRemoved debug log (M25)
components/ux/TWResponsiveCard/hooks/useFieldVisibility.tsRemoved 10 debug logs (M25)
app/company/connections/collaboratingpartner/_components/CollaboratorsGrid.tsxRemoved debug log (M25)
app/(shared)/userprofile/accountPage.tsxReplaced console.log with toast (M25)

Deferred Items

IssueReason
M4 (JWT in URL)Requires backend voice service changes
M6 (updateCvSchema removal)Still in active use by 5 components
H15 (Admin user pagination)Requires backend limit/offset support
M26 (window.confirm → modals)7 instances; requires designing a reusable confirm modal component