From dea6621164f685d998eeddcd74322036a8162b4c Mon Sep 17 00:00:00 2001 From: Chris Bongers Date: Tue, 13 Jan 2026 15:24:28 +0200 Subject: [PATCH 1/3] fix: anon user --- src/common/schema/opportunityMatch.ts | 7 ++++ src/schema/opportunity.ts | 50 +++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/common/schema/opportunityMatch.ts b/src/common/schema/opportunityMatch.ts index 47030e8da..de268c63d 100644 --- a/src/common/schema/opportunityMatch.ts +++ b/src/common/schema/opportunityMatch.ts @@ -18,6 +18,13 @@ export type FeedbackClassification = z.infer< typeof feedbackClassificationSchema >; +export const anonymousUserContextSchema = z.object({ + seniority: z.string().nullable().optional(), + locationCountry: z.string().nullable().optional(), +}); + +export type AnonymousUserContext = z.infer; + export const opportunityScreeningAnswersSchema = z.object({ id: z.uuid(), answers: z diff --git a/src/schema/opportunity.ts b/src/schema/opportunity.ts index 1bb5aab21..4392200df 100644 --- a/src/schema/opportunity.ts +++ b/src/schema/opportunity.ts @@ -372,6 +372,20 @@ export const typeDefs = /* GraphQL */ ` edges: [OpportunityMatchEdge!]! } + """ + Anonymous user context data captured at feedback submission time + """ + type AnonymousUserContext { + """ + User's seniority/experience level (e.g., junior, senior, staff) + """ + seniority: String + """ + User's country (general location, not specific city) + """ + locationCountry: String + } + type FeedbackClassification { platform: Int! category: Int! @@ -379,6 +393,10 @@ export const typeDefs = /* GraphQL */ ` urgency: Int! screening: String! answer: String! + """ + Anonymous user context captured at feedback submission + """ + userContext: AnonymousUserContext } type FeedbackClassificationEdge { @@ -1830,23 +1848,49 @@ export const resolvers: IResolvers = traceResolvers< where: { opportunityId, }, - select: ['feedback'], + select: ['feedback', 'userId'], }), ); + // Gather unique userIds and fetch their anonymous context + const userIds = [...new Set(matches.map((m) => m.userId))]; + const userContextMap = new Map< + string, + { seniority: string | null; locationCountry: string | null } + >(); + + const users = await ctx.con.getRepository(User).find({ + where: { id: In(userIds) }, + select: ['id', 'experienceLevel', 'flags'], + }); + + for (const user of users) { + const flags = (user.flags ?? {}) as Record; + userContextMap.set(user.id, { + seniority: user.experienceLevel ?? null, + locationCountry: (flags.country as string) ?? null, + }); + } + // Extract feedback items with recruiter platform classification const allFeedback = matches - .flatMap((match) => match.feedback ?? []) + .flatMap((match) => + (match.feedback ?? []).map((f) => ({ + ...f, + userId: match.userId, + })), + ) .filter( (f) => f.classification?.platform === FeedbackPlatform.RECRUITER, ) - .map(({ screening, answer, classification }) => ({ + .map(({ screening, answer, classification, userId }) => ({ screening, answer, platform: classification!.platform, category: classification!.category, sentiment: classification!.sentiment, urgency: classification!.urgency, + userContext: userContextMap.get(userId) ?? {}, })); const totalCount = allFeedback.length; From d86d1d1644ffb30fc5affcee73b6906648f5b0b9 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Tue, 13 Jan 2026 17:58:59 +0000 Subject: [PATCH 2/3] fix: prioritize candidatePreference location country over flags.country - Fetch UserCandidatePreference with location relation - Use preferenceLocation.country as primary source, fallback to flags.country - Ensures user's explicit location preference takes precedence Co-authored-by: Chris Bongers --- src/schema/opportunity.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/schema/opportunity.ts b/src/schema/opportunity.ts index 4392200df..4ed9d5d6f 100644 --- a/src/schema/opportunity.ts +++ b/src/schema/opportunity.ts @@ -1864,11 +1864,31 @@ export const resolvers: IResolvers = traceResolvers< select: ['id', 'experienceLevel', 'flags'], }); + // Fetch candidatePreferences with location relation + const candidatePreferences = await ctx.con + .getRepository(UserCandidatePreference) + .find({ + where: { userId: In(userIds) }, + relations: ['location'], + select: ['userId'], + }); + + const preferenceMap = new Map( + candidatePreferences.map((pref) => [pref.userId, pref]), + ); + for (const user of users) { const flags = (user.flags ?? {}) as Record; + const preference = preferenceMap.get(user.id); + const preferenceLocation = preference?.location + ? await preference.location + : null; + userContextMap.set(user.id, { seniority: user.experienceLevel ?? null, - locationCountry: (flags.country as string) ?? null, + // Prioritize candidatePreference location country over flags.country + locationCountry: + preferenceLocation?.country ?? (flags.country as string) ?? null, }); } From 2d0f636e21605eafa37d8c2ccc7d2e27846f0593 Mon Sep 17 00:00:00 2001 From: Chris Bongers Date: Wed, 14 Jan 2026 13:23:36 +0200 Subject: [PATCH 3/3] fix: uniform renders --- src/schema/opportunity.ts | 55 +++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/src/schema/opportunity.ts b/src/schema/opportunity.ts index 4ed9d5d6f..85dd05303 100644 --- a/src/schema/opportunity.ts +++ b/src/schema/opportunity.ts @@ -1852,46 +1852,39 @@ export const resolvers: IResolvers = traceResolvers< }), ); - // Gather unique userIds and fetch their anonymous context + // Gather unique userIds and fetch their anonymous context with candidate preferences const userIds = [...new Set(matches.map((m) => m.userId))]; - const userContextMap = new Map< - string, - { seniority: string | null; locationCountry: string | null } - >(); const users = await ctx.con.getRepository(User).find({ where: { id: In(userIds) }, select: ['id', 'experienceLevel', 'flags'], + relations: ['candidatePreference', 'candidatePreference.location'], }); - // Fetch candidatePreferences with location relation - const candidatePreferences = await ctx.con - .getRepository(UserCandidatePreference) - .find({ - where: { userId: In(userIds) }, - relations: ['location'], - select: ['userId'], - }); - - const preferenceMap = new Map( - candidatePreferences.map((pref) => [pref.userId, pref]), + const userContextMap = new Map( + await Promise.all( + users.map(async (user) => { + const flags = (user.flags ?? {}) as Record; + const preference = await user.candidatePreference; + const preferenceLocation = preference?.location + ? await preference.location + : null; + + return [ + user.id, + { + seniority: user.experienceLevel ?? null, + // Prioritize candidatePreference location country over flags.country + locationCountry: + preferenceLocation?.country ?? + (flags.country as string) ?? + null, + }, + ] as const; + }), + ), ); - for (const user of users) { - const flags = (user.flags ?? {}) as Record; - const preference = preferenceMap.get(user.id); - const preferenceLocation = preference?.location - ? await preference.location - : null; - - userContextMap.set(user.id, { - seniority: user.experienceLevel ?? null, - // Prioritize candidatePreference location country over flags.country - locationCountry: - preferenceLocation?.country ?? (flags.country as string) ?? null, - }); - } - // Extract feedback items with recruiter platform classification const allFeedback = matches .flatMap((match) =>