The cached ticketmate_url was bound to the ticket's original
requester contact (whose magic link would auto-log THEM in). For
staff viewing the IssueResource on the consuming app, that's the
wrong identity — they want to land on the portal as themselves.
New TicketmateClient::loginAs($ticketId, $viewerEmail, $viewerName)
calls TM's new POST /api/v1/issues/login-as endpoint, which
upserts a portal-enabled Contact for the viewer's email and mints
a signed magic link bound to that Contact.
The "Open in TicketMate" buttons now fetch this on click:
- v3 Blade: wire:click="openInTicketmate(id)" → opens new tab
- v5 IssueResource Action: ->action(fn) → opens via $livewire->js
Falls back to the staff /admin URL when the viewer has no email
or the API call fails.