You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat: show verification status on contributor profile (CM-1808) - #4987
Adds a nullable verifiedAt (timestamptz) to memberIdentities and memberOrganizations (migration V1792090000). The migration is metadata-only and does not backfill, so existing rows stay null.
Identity inserts and updates and work-experience updates stamp verifiedAt with the database now() in the same statement as updatedAt. Work-experience inserts use the same app timestamp as createdAt and updatedAt. The public v1 identity verify and create paths, member create, and work-experience verify, create, and update paths set it. Unverifying a row clears it. verifiedAt is not returned by public v1 responses, so the LFX One contract is unchanged.
When the CDP UI creates or verifies an identity with verified: true and no verifiedBy (contributor add, internal identity create/update), verifiedBy is set to the logged-in user's email and verifiedAt is stamped. Update only does this on an unverified-to-verified change.
Grouped work experiences in the internal list also return rowSource, the source of the display row. The joined source is unchanged.
Contributor profile: identity and work-history rows show a status icon (verified, auto-verified, unverified) with a hover and focus popover. The popover shows Verified by, Verified on, Source, and Last updated. Identities also show a warning when they were updated after verification. This replaces the old verified badge.
Auto-verified means verified with no verifiedBy, or with a system verifier (platform values, lf-auth0, lfxOne). Legacy rows have no verifiedAt, so the popover hides "Verified on" and never shows the stale warning.
Adds the OrganizationSource values the popover needs for source labels (project-registry, lfxOne, cvent, tnc, meetings, committees).
Deploy order: run the migration before backend and workers roll out. DAL inserts now name the verifiedAt column.
Known follow-up
Merging members that share an identity keeps the primary copy's verifiedBy and drops the secondary's verifiedBy and verifiedAt (already true for verifiedBy before this PR). Unmerge re-inserts identities without them. After a merge, a manually verified identity can show as auto-verified with no date.
Medium Risk
Touches identity and work-experience verification writes and requires the migration before deploy; behavior is gated by explicit stamping so legacy rows stay null, but wrong rollout order could break inserts that reference verifiedAt.
Overview
Adds verifiedAt on member identities and work experiences so the UI can show when something was verified and whether it changed afterward. A migration adds nullable verifiedAt columns (no backfill). DAL insert/update paths accept stampVerifiedAt to set verifiedAt to now() when verifying and clear it when unverifying; callers on public v1 verify/create/update and CDP MemberIdentityService wire that in, including defaulting verifiedBy to the logged-in user when the UI verifies without a verifier.
Contributor profile replaces the simple verified badge with a shared LfVerificationStatus control (verified / auto-verified / unverified) and a popover for verifier, date, source, and last updated, plus a stale hint when an identity’s updatedAt is after verifiedAt. Grouped work experiences also expose rowSource for source labeling in that popover.
Reviewed by Cursor Bugbot for commit 6936a4f. Bugbot is set up for automated code reviews on this repo. Configure here.
LfPopover immediately closes on the trigger's mouseleave (frontend/src/ui-kit/popover/Popover.vue:8,78–80) and unmounts its teleported panel. Moving the pointer toward the verification details therefore dismisses them, preventing users, particularly those using magnification, from hovering the content to read it. Keep the popover open while either the trigger or panel is hovered, with a short delayed close to bridge the gap.
These status-icon colors have contrast ratios of 2.63:1, 2.47:1, and 2.97:1 against white, below…
These status-icon colors have contrast ratios of 2.63:1, 2.47:1, and 2.97:1 against white, below the 3:1 minimum for meaningful non-text graphics. The rows rely on these icons to convey verification status, making it harder for low-vision users to perceive. Use darker colors; the suggested replacements preserve the gray, green, and blue distinctions and each exceeds 3:1 against white.
LfPopover closes immediately when the pointer leaves its trigger, and its content is teleported outside that trigger (frontend/src/ui-kit/popover/Popover.vue:8,13–20,78–80). Moving onto these verification details therefore dismisses them, preventing users of screen magnification from following the pointer to read the card. Add hover persistence so it stays open over either the trigger or card, with a short, cancellable close delay to cross the gap. Preserve focus opening and Escape dismissal.
Keep the popover open while either its trigger or its content is hovered. LfPopover immediately hides it on the trigger's mouseleave, while the panel is teleported outside that trigger (frontend/src/ui-kit/popover/Popover.vue:7–20,78–80). Moving the pointer toward the verification details therefore closes them, preventing users—including those using screen magnification—from hovering over the details to read them. Add coordinated trigger/panel hover handling with a short close delay to let the pointer cross the gap.
LfPopover immediately closes on the trigger's mouseleave (frontend/src/ui-kit/popover/Popover.vue:8), while its panel is teleported outside the trigger. Moving the pointer from this icon toward the verification details therefore dismisses them. This prevents users, including those using screen magnification, from hovering over the content and violates WCAG 1.4.13. Keep the panel open while either the trigger or panel is hovered, with a cancellable close delay to allow crossing the gap.
The reason will be displayed to describe this comment to others. Learn more.
identities added or edited in CDP itself go through MemberIdentityService.create/update, and the UI only sends verified: true with no verifiedBy (e.g. contributor-add.vue). with !entity.verifiedBy mapping to auto-verified, a manually added identity shows "automatically verified by the LFX platform because it came through a connected data source", which isn't what happened. could we either set verifiedBy from the current user on those internal paths, or soften the copy for the no-verifier case? (or am I missing something?)
The reason will be displayed to describe this comment to others. Learn more.
Good catch, you're right. Went with setting verifiedBy on the internal paths (6936a4f): when the CDP UI creates or verifies an identity with verified: true and no verifiedBy, MemberIdentityService.create/createMultiple/update and MemberRepository.create (contributor add) now set it to the logged-in user's email and stamp verifiedAt. Update only does this when the identity goes from unverified to verified, so an existing LFX One verifier isn't overwritten.
Existing rows with no verifiedBy (about 22M, almost all from integrations) still show as auto-verified, since there's no way to tell the manual ones apart.
The hover panel disappears when the pointer leaves the status icon. Popover.vue:8,78–80 immediately hides it on the trigger's mouseleave, and its teleported content has no hover handlers. Users cannot move onto the details to read them with magnification or select the verifier text. Keep the panel open while either the trigger or panel is hovered, with a short close delay that is canceled when entering either area.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
verifiedAt(timestamptz) tomemberIdentitiesandmemberOrganizations(migrationV1792090000). The migration is metadata-only and does not backfill, so existing rows stay null.verifiedAtwith the databasenow()in the same statement asupdatedAt. Work-experience inserts use the same app timestamp ascreatedAtandupdatedAt. The public v1 identity verify and create paths, member create, and work-experience verify, create, and update paths set it. Unverifying a row clears it.verifiedAtis not returned by public v1 responses, so the LFX One contract is unchanged.verified: trueand noverifiedBy(contributor add, internal identity create/update),verifiedByis set to the logged-in user's email andverifiedAtis stamped. Update only does this on an unverified-to-verified change.rowSource, the source of the display row. The joinedsourceis unchanged.verifiedBy, or with a system verifier (platform values,lf-auth0,lfxOne). Legacy rows have noverifiedAt, so the popover hides "Verified on" and never shows the stale warning.OrganizationSourcevalues the popover needs for source labels (project-registry, lfxOne, cvent, tnc, meetings, committees).verifiedAtcolumn.Known follow-up
verifiedByand drops the secondary'sverifiedByandverifiedAt(already true forverifiedBybefore this PR). Unmerge re-inserts identities without them. After a merge, a manually verified identity can show as auto-verified with no date.Issue
Closes linuxfoundation/lfx-cdp-insights-ops#486