Skip to content

feat(panel-app): merge confirmation modal in ConnectionHub with MergeAccountsAction #287

@danielhe4rt

Description

@danielhe4rt

What to build

When the ConnectionHub component detects a pending merge conflict in the session (set by issue #286), display a confirmation modal showing the old account's details. On confirmation, execute a synchronous merge that transfers the current user's data to the old user, deletes the current user, and re-logs as the old user.

End-to-end behavior:

  1. User returns to ConnectionHub after OAuth callback stored a merge conflict in session
  2. ConnectionHub detects oauth_merge_pending in session
  3. Modal displays:
    • Old user's username
    • Old user's created_at date
    • Old user's message count (from ExternalIdentity messages relation)
    • Explanation: "Your current account will be absorbed by this account and you'll be re-logged automatically"
  4. User confirmsMergeAccountsAction executes inside DB::transaction:
    • Move all ExternalIdentities (model_type=user) from current user → old user (update model_id)
    • Sync tenant memberships: $oldUser->tenants()->syncWithoutDetaching($currentUser->tenants->pluck('id'))
    • If old user's first_login_at is null: update email, name, attempt username from current user's data
    • Set first_login_at on old user if null
    • Delete current user (hard delete)
    • Clear session merge state
  5. Auth::login($oldUser) — re-authenticate as the old user
  6. Redirect to ConnectionHub (user sees unified account)
  7. User cancels → clear session merge state, no changes made

Key implementation:

  • MergeAccountsAction — new action in identity module, accepts current user + old user
  • ConnectionHub Livewire component: check session on render, expose modal state
  • Modal UI using Filament Notification or Livewire modal pattern
  • Re-login after transaction commits

Reference:

  • ADR: app-modules/identity/docs/adr/0001-oauth-user-resolution-and-merge-strategy.md

Acceptance criteria

  • ConnectionHub detects pending merge from session and shows confirmation modal
  • Modal displays: old user's username, creation date, message count
  • On confirm: ExternalIdentities moved from current → old user
  • On confirm: tenant memberships synced (syncWithoutDetaching)
  • On confirm: old user enriched (email, name, try username) if first_login_at is null
  • On confirm: first_login_at set on old user if null
  • On confirm: current user hard-deleted
  • On confirm: re-login as old user, redirect to ConnectionHub
  • On cancel: session cleared, no DB changes, modal closes
  • All merge operations wrapped in DB::transaction
  • Feature test: full merge flow — confirm → identities moved, user deleted, re-logged
  • Feature test: merge cancel → no changes, session cleared
  • Feature test: merge with old user having first_login_at=null → info enriched
  • Feature test: merge with old user having first_login_at set → info NOT overwritten
  • Unit test: MergeAccountsAction transaction correctness

Blocked by

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions