feat(events): add PDF→webp convert button to event file list view
Mirrors the convert button added to the table view (ae_comp__event_file_obj_tbl). The list view (element_manage_event_file_li) is the primary Pres Mgmt UI for managing event files per object (session, presenter, location, etc.). Same conditions: edit_mode on, extension=pdf, event_session_type_code=poster. Per-row status: idle → converting → done | error with retry. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -69,6 +69,53 @@
|
||||
});
|
||||
|
||||
let clipboard_success = $state(false);
|
||||
|
||||
// PDF → Image conversion state (keyed by event_file_id)
|
||||
// WHY: Poster sessions display files in the Launcher modal via <img> tag — PDFs can't render there.
|
||||
// Presenters upload PDFs which must be converted server-side to high-res webp images.
|
||||
// Button is only shown in edit_mode for PDF files linked to a poster-type session.
|
||||
// Backend: /v3/action/hosted_file/{id}/convert_file runs pdf2image (3840px wide, first page only)
|
||||
// and saves the result as a new hosted_file record linked to the same parent object.
|
||||
type ConvertStatus = 'idle' | 'converting' | 'done' | 'error';
|
||||
let convert_status_kv: Record<string, ConvertStatus> = $state({});
|
||||
let convert_result_kv: key_val = $state({});
|
||||
|
||||
async function handle_convert_pdf_to_image(event_file_obj: key_val) {
|
||||
const file_id = event_file_obj.event_file_id;
|
||||
convert_status_kv[file_id] = 'converting';
|
||||
try {
|
||||
// Link the new image to the most specific parent available
|
||||
const link_to_type_val = event_file_obj.event_session_id ? 'event_session'
|
||||
: event_file_obj.event_presentation_id ? 'event_presentation'
|
||||
: 'event';
|
||||
const link_to_id_val = event_file_obj.event_session_id
|
||||
|| event_file_obj.event_presentation_id
|
||||
|| event_file_obj.event_id;
|
||||
const filename_no_ext = (event_file_obj.filename ?? 'poster_image').replace(/\.pdf$/i, '');
|
||||
const url = `${$ae_api.base_url}/v3/action/hosted_file/${event_file_obj.hosted_file_id}/convert_file`
|
||||
+ `?link_to_type=${encodeURIComponent(link_to_type_val)}`
|
||||
+ `&link_to_id=${encodeURIComponent(link_to_id_val)}`
|
||||
+ `&filename_no_ext=${encodeURIComponent(filename_no_ext)}`
|
||||
+ `&to_type=webp`;
|
||||
const resp = await fetch(url, {
|
||||
headers: {
|
||||
'x-aether-api-key': $ae_api.api_secret_key,
|
||||
'x-account-id': String($ae_api.account_id ?? '')
|
||||
}
|
||||
});
|
||||
const body = await resp.json();
|
||||
if (resp.ok && body?.data) {
|
||||
convert_result_kv[file_id] = body.data;
|
||||
convert_status_kv[file_id] = 'done';
|
||||
} else {
|
||||
console.error('[convert_pdf] API error:', body);
|
||||
convert_status_kv[file_id] = 'error';
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[convert_pdf] Fetch failed:', err);
|
||||
convert_status_kv[file_id] = 'error';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="float-right flex flex-row items-center">
|
||||
@@ -191,6 +238,45 @@
|
||||
classes="btn btn-sm lg:btn-md preset-tonal-primary hover:preset-filled-primary-500 min-w-72 lg:min-w-96"
|
||||
/>
|
||||
|
||||
<!-- PDF → webp convert button: only for poster sessions in edit mode -->
|
||||
{#if $ae_loc.edit_mode && event_file_obj?.extension === 'pdf' && event_file_obj?.event_session_type_code === 'poster'}
|
||||
<div>
|
||||
{#if !convert_status_kv[event_file_obj.event_file_id] || convert_status_kv[event_file_obj.event_file_id] === 'idle'}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm preset-tonal-warning border border-warning-500"
|
||||
title="Convert this PDF to a high-res webp image for use in the Launcher poster display."
|
||||
onclick={() => handle_convert_pdf_to_image(event_file_obj)}
|
||||
>
|
||||
<span class="fas fa-file-image mx-1"></span>
|
||||
Convert PDF → Image
|
||||
</button>
|
||||
{:else if convert_status_kv[event_file_obj.event_file_id] === 'converting'}
|
||||
<span class="btn btn-sm preset-tonal-surface opacity-60 cursor-wait">
|
||||
<span class="fas fa-spinner fa-spin mx-1"></span>
|
||||
Converting…
|
||||
</span>
|
||||
{:else if convert_status_kv[event_file_obj.event_file_id] === 'done'}
|
||||
<span class="btn btn-sm preset-tonal-success" title="Conversion complete. New webp hosted_file created: {convert_result_kv[event_file_obj.event_file_id]?.filename ?? ''}">
|
||||
<span class="fas fa-check mx-1"></span>
|
||||
Done — {convert_result_kv[event_file_obj.event_file_id]?.filename ?? 'image created'}
|
||||
</span>
|
||||
{:else if convert_status_kv[event_file_obj.event_file_id] === 'error'}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm preset-tonal-error border border-error-500"
|
||||
title="Conversion failed. Click to retry."
|
||||
onclick={() => {
|
||||
convert_status_kv[event_file_obj.event_file_id] = 'idle';
|
||||
}}
|
||||
>
|
||||
<span class="fas fa-exclamation-triangle mx-1"></span>
|
||||
Failed — Retry?
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if ae_tmp.show__direct_download}
|
||||
<div
|
||||
class="px-4 py-2 flex flex-col gap-0.5 bg-surface-100/50 rounded-lg border border-surface-500/10"
|
||||
|
||||
Reference in New Issue
Block a user