fix(pres_mgmt): restore POC + Presenter sign-in email and copy link flows

POC (session) sign-in was silently broken:
- sign_in_out.svelte calls session_sign_in() only when session_id is a query
  param; the URL was missing &session_id=... so the function never fired.
- Fixed in: email_sign_in__event_session URL and poc_sign_in_url derivation
  in session_view.svelte.

Presenter email routed to wrong page:
- email_sign_in__event_presenter built a URL to /presenter/[id] which has no
  sign-in handler. Changed to route to /session/[session_id] (same as the
  existing copy-link on the presenter page), including presenter_id and
  presentation_id params so presenter_sign_in() fires correctly.

Multi-session/multi-presentation person-centric auth:
- poc_is_authed in session_view.svelte now also checks
  auth__person.id === poc_person_id, so a POC signed in via any of their
  session links is automatically auth'd on all sessions where they are the POC.
- presenter_is_authed derived bool added to presenter page; includes person_id
  match so a presenter is recognised on all their presentations after one
  sign-in. All file upload and file list auth gates now use it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-06-12 16:27:45 -04:00
parent 10f7f04fbc
commit e05602b87b
4 changed files with 35 additions and 24 deletions

View File

@@ -597,10 +597,13 @@ export async function email_sign_in__event_presenter({
return null;
}
const subject = `Pres Mgmt Hub Sign In Link for Presenter: ${to_name ?? 'Presenter'}`;
// Routes to the session page (which has the sign-in handler mounted) not /presenter/[id]
// which has no sign-in handler. Includes presenter_id + presentation_id so the handler
// can grant presenter-level auth (not just session read access).
const sign_in_url = encodeURI(
`${base_url}/events/${event_id}/presenter/${event_presenter_id}?person_id=${person_id}&person_pass=${person_passcode}`
`${base_url}/events/${event_id}/session/${event_session_id}?person_id=${person_id}&person_pass=${person_passcode}&presenter_id=${event_presenter_id}&presentation_id=${event_presentation_id}`
);
const body_html = `<div>${to_name},<p>Your sign-in link for ${presentation_name ?? 'Presentation'} (Session: ${session_name ?? 'Session'}): <a href="${sign_in_url}">${sign_in_url}</a></p></div>`;
const body_html = `<div>${to_name},<p>Your sign-in link for ${presentation_name ?? 'Presentation'} (Session: ${session_name ?? 'Session'}): <a href="${sign_in_url}">${sign_in_url}</a></p><p>This link takes you to the session page — your presentation and file upload sections will be available after you sign in.</p></div>`;
return await api.send_email({
api_cfg,
from_email: 'noreply+presmgmt@oneskyit.com',

View File

@@ -829,7 +829,7 @@ export async function email_sign_in__event_session({
}) {
const subject = `Pres Mgmt Hub Sign In Link for ${session_name}`;
const sign_in_url = encodeURI(
`${base_url}/events/${event_id}/session/${event_session_id}?person_id=${person_id}&person_pass=${person_passcode}`
`${base_url}/events/${event_id}/session/${event_session_id}?person_id=${person_id}&person_pass=${person_passcode}&session_id=${event_session_id}`
);
const body_html = `<div>${to_name},<p>Your sign-in link for ${session_name}: <a href="${sign_in_url}">${sign_in_url}</a></p></div>`;
return await api.send_email({

View File

@@ -116,6 +116,19 @@ let lq__auth__event_presenter_obj = $derived(
})
);
// True if this person is authed as the presenter for this specific record.
// Also matches by person_id so a presenter signed in via one presentation link is
// automatically recognised on their other presentations at the same event.
let presenter_is_authed = $derived(
$ae_loc.trusted_access ||
!!events_auth_loc.current.auth__kv.presenter[$lq__event_presenter_obj?.event_presenter_id ?? ''] ||
!!events_auth_loc.current.auth__kv.session[$lq__event_presenter_obj?.event_session_id ?? ''] ||
(
!!events_auth_loc.current.auth__person.id &&
events_auth_loc.current.auth__person.id === $lq__event_presenter_obj?.person_id
)
);
// if (browser && $lq__event_presenter_obj) {
// console.log('Pres Mgmt [page]: +presenter.svelte');
// $events_slct.event_presenter_obj = $lq__event_presenter_obj;
@@ -202,7 +215,7 @@ let lq__auth__event_presenter_obj = $derived(
<span
class="flex flex-col items-end justify-center gap-0.25 font-normal">
<span class="text-sm text-gray-600 dark:text-gray-400">
{#if !$ae_loc.trusted_access && !events_auth_loc.current.auth__kv.presenter[$lq__event_presenter_obj?.event_presenter_id] && !events_auth_loc.current.auth__kv.session[$lq__event_presenter_obj.event_session_id]}
{#if !presenter_is_authed}
<!-- Only show the domain name of the email address. -->
<!-- ( -->
<!-- {$lq__event_presenter_obj.email.replace(/@.*$/, '@...')} -->
@@ -349,7 +362,7 @@ let lq__auth__event_presenter_obj = $derived(
</h3>
<!-- Yes... This is a duplicate of what is shown when they press "Presenter Files" button. It can be cleaned up later. -->
{#if $ae_loc.trusted_access || events_auth_loc.current.auth__kv.presenter[$lq__event_presenter_obj?.event_presenter_id]}
{#if presenter_is_authed}
<Comp_event_files_upload
class_li="border border-surface-200-800 rounded-xl p-4 bg-surface-50-900 hover:bg-surface-100-900 transition-colors duration-200"
link_to_type="event_presenter"
@@ -380,15 +393,8 @@ let lq__auth__event_presenter_obj = $derived(
<Element_manage_event_file_li_wrap
link_to_type={'event_presenter'}
link_to_id={$lq__event_presenter_obj?.event_presenter_id}
allow_basic={events_auth_loc.current.auth__kv.presenter[
$lq__event_presenter_obj?.event_presenter_id
] ||
events_auth_loc.current.auth__kv.presenter[
$lq__event_presenter_obj?.event_presenter_id
]}
allow_moderator={events_auth_loc.current.auth__kv.presenter[
$lq__event_presenter_obj?.event_presenter_id
]}
allow_basic={presenter_is_authed}
allow_moderator={presenter_is_authed}
container_class_li={''} />
</div>
{:else if pres_mgmt_loc.current.show_content__presenter_view == 'manage_files' && $ae_loc.authenticated_access}
@@ -405,7 +411,7 @@ let lq__auth__event_presenter_obj = $derived(
</span>
</h3>
{#if $ae_loc.public_access || events_auth_loc.current.auth__kv.presenter[$lq__event_presenter_obj?.event_presenter_id]}
{#if presenter_is_authed || $ae_loc.public_access}
<Comp_event_files_upload
class_li="border border-surface-200-800 rounded-xl p-4 bg-surface-50-900 hover:bg-surface-100-900 transition-colors duration-200"
link_to_type="event_presenter"
@@ -437,13 +443,8 @@ let lq__auth__event_presenter_obj = $derived(
<Element_manage_event_file_li_wrap
link_to_type={'event_presenter'}
link_to_id={$lq__event_presenter_obj?.event_presenter_id}
allow_basic={$ae_loc.public_access ||
events_auth_loc.current.auth__kv.presenter[
$lq__event_presenter_obj?.event_presenter_id
]}
allow_moderator={events_auth_loc.current.auth__kv.presenter[
$lq__event_presenter_obj?.event_presenter_id
]}
allow_basic={presenter_is_authed || $ae_loc.public_access}
allow_moderator={presenter_is_authed}
container_class_li={''} />
</div>
</div>

View File

@@ -145,10 +145,11 @@ let default_end_datetime = $derived(event_start_date ? `${event_start_date}T09:0
// Derived helpers for the POC sign-in URL (used by both copy link and email).
// WHY: poc_person_passcode gates access for the session POC without requiring a full account.
// session_id is required as a query param so sign_in_out.svelte calls session_sign_in().
let poc_sign_in_url = $derived(
$lq__event_session_obj?.poc_person_id && $lq__event_session_obj?.poc_person_passcode
? encodeURI(
`${$ae_loc.url_origin}/events/${$lq__event_session_obj.event_id}/session/${$lq__event_session_obj.event_session_id}?person_id=${$lq__event_session_obj.poc_person_id}&person_pass=${$lq__event_session_obj.poc_person_passcode}`
`${$ae_loc.url_origin}/events/${$lq__event_session_obj.event_id}/session/${$lq__event_session_obj.event_session_id}?person_id=${$lq__event_session_obj.poc_person_id}&person_pass=${$lq__event_session_obj.poc_person_passcode}&session_id=${$lq__event_session_obj.event_session_id}`
)
: null
);
@@ -158,8 +159,14 @@ let poc_sign_in_url = $derived(
let person_options_loaded = $derived(Object.keys($slct.person_obj_kv ?? {}).length > 0);
// Whether the current session POC is signed in (auth'd) on this device.
// Also grants access by person_id so a POC signed in via one session link is automatically
// auth'd on all other sessions where their person record is the assigned POC.
let poc_is_authed = $derived(
events_auth_loc.current.auth__kv.session[$lq__event_session_obj?.event_session_id] === true
events_auth_loc.current.auth__kv.session[$lq__event_session_obj?.event_session_id] === true ||
(
!!events_auth_loc.current.auth__person.id &&
events_auth_loc.current.auth__person.id === $lq__event_session_obj?.poc_person_id
)
);
// QR Code Generation Logic