feat(events): restore PDF→webp convert button in event file table
Adds a per-row "Convert PDF → Image" button in ae_comp__event_file_obj_tbl.
Only shown when edit_mode is on, the file is a PDF, and the session
type_code is 'poster' — poster sessions need images in the Launcher modal
(which uses <img>, not a PDF viewer).
Calls GET /v3/action/hosted_file/{id}/convert_file (pdf2image, 3840px wide,
first page, saves as a new hosted_file linked to the same parent object).
Per-row status tracking: idle → converting → done | error with retry.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -65,6 +65,53 @@
|
||||
let horiz_scroll_warning: boolean = $state(false);
|
||||
let horiz_check_element: HTMLElement | null = $state(null);
|
||||
|
||||
// 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: Record<string, any> = $state({});
|
||||
|
||||
async function handle_convert_pdf_to_image(event_file_obj: any) {
|
||||
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 = event_file_obj.event_session_id ? 'event_session'
|
||||
: event_file_obj.event_presentation_id ? 'event_presentation'
|
||||
: 'event';
|
||||
const link_to_id = 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)}`
|
||||
+ `&link_to_id=${encodeURIComponent(link_to_id)}`
|
||||
+ `&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';
|
||||
}
|
||||
}
|
||||
|
||||
// Check if element is scrolling horizontally
|
||||
$effect(() => {
|
||||
if (
|
||||
@@ -437,7 +484,44 @@
|
||||
max_filename={50}
|
||||
classes="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500 min-w-72"
|
||||
/>
|
||||
<!-- {event_file_obj?.filename} -->
|
||||
<!-- 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 class="mt-1">
|
||||
{#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}
|
||||
</td>
|
||||
<td
|
||||
class="px-4 py-2 flex flex-col gap-0.5"
|
||||
|
||||
Reference in New Issue
Block a user