Skip to content

feat: show verification status on contributor profile (CM-1808) - #4987

Merged
gaspergrom merged 17 commits into
mainfrom
feat/CM-1808-verified-by-info-ui
Oct 16, 2026
Merged

gaspergrom merged 17 commits into
mainfrom
feat/CM-1808-verified-by-info-ui

Conversation

@gaspergrom

@gaspergrom gaspergrom commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • 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.

Issue

Closes linuxfoundation/lfx-cdp-insights-ops#486

Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
…work history (CM-1808)

Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
…he top (CM-1808)

Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Copilot AI balanced review requested due to automatic review settings October 16, 2026 05:16
@cursor

cursor Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

PR Summary

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.

Copilot AI left a comment

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.

🟡 Changes recommended

Verification timestamp preservation and consistency, plus popover accessibility and positioning, need correction.

4 open findings
What changed in this PR

Adds verification details to contributor profiles, backed by timestamps on identities and work experiences.

Changes:

  • Adds nullable verification timestamps and updates public API write paths.
  • Introduces status icons, detail popovers, and stale-identity warnings.
  • Preserves display-row source and verification metadata during grouping.
File Description
services/​libs/​types/​src/​organizations.ts Adds verification timestamp and display-row source.
services/​libs/​types/​src/​members.ts Adds identity verification timestamp.
services/​libs/​types/​src/​db/​member-organizations.ts Types timestamp storage and stamping options.
services/​libs/​types/​src/​db/​member-identities.ts Types timestamp storage and stamping options.
services/​libs/​data-access-layer/​src/​utils.ts Adds database-time expression.
services/​libs/​data-access-layer/​src/​members/​organizations.ts Handles verification timestamps on writes.
services/​libs/​data-access-layer/​src/​members/​identities.ts Handles timestamp stamping and clearing.
frontend/​src/​shared/​modules/​verification/​verification.helpers.ts Derives statuses, sources, and stale warnings.
frontend/​src/​shared/​modules/​verification/​components/​verification-status.vue Adds verification icon and detail popover.
frontend/​src/​modules/​organization/​types/​Organization.ts Extends sources and verification metadata.
frontend/​src/​modules/​member/​types/​Member.ts Adds identity metadata and formatting cleanup.
frontend/​src/​modules/​contributor/​types/​Contributor.ts Adds identity verification metadata.
frontend/​src/​modules/​contributor/​helpers/​contributor.helpers.ts Selects verification metadata for grouped emails.
frontend/​src/​modules/​contributor/​components/​details/​work-history/​contributor-details-work-history-item.vue Displays work-history verification status.
frontend/​src/​modules/​contributor/​components/​details/​identity/​contributor-details-identity-item.vue Replaces verification badge with status popover.
backend/​src/​utils/​mapper.ts Preserves grouped display-row source.
backend/​src/​services/​member/​memberIdentityService.ts Prevents input-controlled timestamp stamping.
backend/​src/​database/​migrations/​V1792090000__add-verified-at-to-member-identities-and-organizations.sql Adds nullable timestamp columns without backfill.
backend/​src/​api/​public/​v1/​members/​work-experiences/​verifyMemberWorkExperience.ts Stamps verification across grouped rows.
backend/​src/​api/​public/​v1/​members/​work-experiences/​updateMemberWorkExperience.ts Enables verification stamping on updates.
backend/​src/​api/​public/​v1/​members/​work-experiences/​createMemberWorkExperience.ts Requests stamping for verified creation.
backend/​src/​api/​public/​v1/​members/​identities/​verifyMemberIdentity.ts Enables verification timestamp stamping.
backend/​src/​api/​public/​v1/​members/​identities/​createMemberIdentity.ts Stamps new and matching verified identities.
backend/​src/​api/​public/​v1/​members/​createMember.ts Requests stamping for verified identities.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +7 to +9
:aria-label="`Verification status: ${title}`"
@mouseenter="updatePlacement"
@focus="openOnFocus"
const isOpen = ref(false);

const updatePlacement = (e: Event) => {
placement.value = (e.currentTarget as HTMLElement).getBoundingClientRect().top < 240 ? 'bottom' : 'top';
id: i.id || generateUUIDv1(),
tenantId: DEFAULT_TENANT_ID,
value: normalizeMemberIdentityValue(i.value),
verifiedAt: i.stampVerifiedAt ? SQL_NOW : null,
updatedAt: ts,
// NOT NULL DEFAULT false — must set while column is in INSERT list
verified: o.verified ?? false,
verifiedAt: o.stampVerifiedAt ? ts : null,
… (CM-1808)

Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
…ape (CM-1808)

Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
…AL (CM-1808)

Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Copilot AI balanced review requested due to automatic review settings October 16, 2026 05:32
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>

Copilot AI left a comment

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.

🔵 Needs a closer look

Hover dismissal and insufficient status-icon contrast leave accessibility issues unresolved.

4 open findings
Previously missed (2)

In code that hasn't changed since last review

Medium severity LfPopover immediately closes on the trigger's mouseleave (`frontend/src/ui-kit/popover/Popover.v…

frontend/​src/​shared/​modules/​verification/​components/​verification-status.vue:2

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.

Medium severity These status-icon colors have contrast ratios of 2.63:1, 2.47:1, and 2.97:1 against white, below…

frontend/​src/​shared/​modules/​verification/​components/​verification-status.vue:135

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.

🧠 Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 16, 2026 05:38
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>

Copilot AI left a comment

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.

🔵 Needs a closer look

Verification popovers dismiss when the pointer leaves the trigger, preventing magnification users from moving onto the details to read them.

4 open findings
Previously missed (1)

In code that hasn't changed since last review

Medium severity Keep popover open while pointer moves onto teleported content

frontend/​src/​shared/​modules/​verification/​components/​verification-status.vue:2

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.

🧠 Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 16, 2026 05:45
…d clip (CM-1808)

Signed-off-by: Gašper Grom <gasper.grom@gmail.com>

Copilot AI left a comment

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.

🔵 Needs a closer look

Verification popovers close when the pointer leaves the trigger, preventing accessible hovering over their details.

4 open findings
Previously missed (1)

In code that hasn't changed since last review

Medium severity Popover closes when moving from trigger to teleported content

frontend/​src/​shared/​modules/​verification/​components/​verification-status.vue:2

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.

🧠 Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 16, 2026 05:51

Copilot AI left a comment

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.

🔵 Needs a closer look

Verification popovers dismiss when the pointer leaves the trigger, preventing users from hovering over their details.

4 open findings
Previously missed (1)

In code that hasn't changed since last review

Medium severity Popover closes when moving pointer from trigger to teleported panel

frontend/​src/​shared/​modules/​verification/​components/​verification-status.vue:2

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.

🧠 Review effort: Balanced

@themarolt themarolt left a comment

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.

looks good - one question on how admin-verified identities get labelled

if (!entity.verified) {
return 'unverified';
}
if (!entity.verifiedBy || SYSTEM_VERIFIERS.has(entity.verifiedBy)) {

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.

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?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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.

Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Copilot AI balanced review requested due to automatic review settings October 16, 2026 07:15

Copilot AI left a comment

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.

🔵 Needs a closer look

Verification popovers disappear before users can move the pointer onto their contents, limiting accessibility.

4 open findings
Previously missed (1)

In code that hasn't changed since last review

Medium severity Popover closes before users can move to its details

frontend/​src/​shared/​modules/​verification/​components/​verification-status.vue:2

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.

🧠 Review effort: Balanced

@gaspergrom
gaspergrom merged commit 6e6d030 into main Oct 16, 2026
19 checks passed
@gaspergrom
gaspergrom deleted the feat/CM-1808-verified-by-info-ui branch October 16, 2026 07:31
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