From a74effa6ff2b80e6ed5d60341330c6f113303cc7 Mon Sep 17 00:00:00 2001
From: Scott Idem
Date: Tue, 2 Jun 2026 11:37:48 -0400
Subject: [PATCH] feat(badges): add Cvent Splash XLSX import mode; fix
server-side upload timeout
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Add 'Cvent Splash XLSX (registrant export)' upload mode hitting the new
/event/{id}/badge/import/splash_xlsx endpoint
- Admin controls: begin_at/end_at/return_detail (shared with Zoom mode) +
import_status_filter (splash only, default 'Attending')
- File picker accept attribute switches between .csv and .xlsx per mode
- Set timeout=300000 and retry_count=1 on both server-side upload paths to
prevent false 'no response' failures on slow imports; upsert-by-email on
the backend makes retries safe but a single attempt is sufficient
- Replace misleading 0/0 progress bar with an indeterminate progress bar
during server-side processing; real counter kept for client-side CSV mode
- Show 'Processing on server…' message once upload completes and server work begins
Co-Authored-By: Claude Sonnet 4.6
---
.../badges/ae_comp__badge_upload_form.svelte | 119 +++++++++++++++---
1 file changed, 103 insertions(+), 16 deletions(-)
diff --git a/src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_upload_form.svelte b/src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_upload_form.svelte
index 53512290..58c48e0f 100644
--- a/src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_upload_form.svelte
+++ b/src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_upload_form.svelte
@@ -23,10 +23,11 @@ let upload_status: string = $state('idle'); // idle, loading, processing, succes
let upload_message: string = $state('');
let processed_badges_count: number = $state(0);
let total_badges_in_file: number = $state(0);
-let upload_mode: 'client_csv' | 'axonius_zoom' = $state('axonius_zoom');
+let upload_mode: 'client_csv' | 'axonius_zoom' | 'axonius_splash_xlsx' = $state('axonius_zoom');
let begin_at: number | null = $state(null);
let end_at: number | null = $state(null);
let return_detail: boolean = $state(false);
+let import_status_filter: string = $state('Attending'); // splash only; set to '' to import all statuses
// A very basic CSV parser that assumes the first row is headers
function parse_csv(text: string): key_val[] {
@@ -64,6 +65,72 @@ async function handle_upload(event: Event) {
return;
}
+ // Server-side import for Cvent Splash XLSX
+ if (upload_mode === 'axonius_splash_xlsx') {
+ upload_status = 'loading';
+ upload_message = 'Uploading to server...';
+
+ const formData = new FormData();
+ formData.append('file', selected_file, selected_file.name);
+
+ const params: any = {};
+ if (begin_at !== null && begin_at !== undefined) params.begin_at = String(begin_at);
+ if (end_at !== null && end_at !== undefined) params.end_at = String(end_at);
+ if (import_status_filter !== 'Attending') params.import_status_filter = import_status_filter;
+ if (return_detail) params.return_detail = String(true);
+
+ const task_id = (crypto && (crypto as any).randomUUID)
+ ? (crypto as any).randomUUID()
+ : String(Date.now());
+
+ try {
+ upload_status = 'processing';
+ upload_message = 'Processing on server… this may take a minute or two.';
+ const endpoint = `/event/${event_id}/badge/import/splash_xlsx`;
+ const result = await api.post_object({
+ api_cfg: $ae_api,
+ endpoint,
+ form_data: formData,
+ params,
+ return_meta: true,
+ task_id,
+ timeout: 300000, // 5 min — server-side import can be slow; no retry to avoid duplicate imports
+ retry_count: 1,
+ log_lvl: 0
+ });
+
+ if (!result) {
+ upload_status = 'error';
+ upload_message = 'Server import failed (no response).';
+ if (onerror) onerror(result);
+ return;
+ }
+
+ const meta = result.meta ?? result;
+ if (meta && meta.success === false) {
+ upload_status = 'error';
+ upload_message = meta?.details?.message || 'Server import failed';
+ if (onerror) onerror(result);
+ return;
+ }
+
+ const processed = meta?.processed ?? meta?.data_list_count ?? (Array.isArray(result?.data) ? result.data.length : 0);
+ processed_badges_count = processed || 0;
+ total_badges_in_file = meta?.total ?? processed_badges_count;
+
+ upload_status = 'success';
+ upload_message = `Server processed ${processed_badges_count} badges.`;
+ if (onsuccess) onsuccess();
+ } catch (err: any) {
+ upload_status = 'error';
+ upload_message = `Upload failed: ${err?.message || 'Unknown error'}`;
+ console.error('Upload error:', err);
+ if (onerror) onerror(err);
+ }
+
+ return;
+ }
+
// Server-side import for Axonius Zoom CSV
if (upload_mode === 'axonius_zoom') {
upload_status = 'loading';
@@ -83,6 +150,7 @@ async function handle_upload(event: Event) {
try {
upload_status = 'processing';
+ upload_message = 'Processing on server… this may take a minute or two.';
const endpoint = `/event/${event_id}/badge/import/zoom_csv`;
const result = await api.post_object({
api_cfg: $ae_api,
@@ -91,6 +159,8 @@ async function handle_upload(event: Event) {
params,
return_meta: true,
task_id,
+ timeout: 300000, // 5 min — server-side import can be slow; no retry to avoid duplicate imports
+ retry_count: 1,
log_lvl: 0
});
@@ -208,23 +278,27 @@ function handle_cancel() {
badge_type_code.
-->
- Upload the standard Axonius Zoom event tickets CSV export file here. This will trigger server-side processing to import the new and changed badges. This can take a few seconds to a few minutes.
+ {#if upload_mode === 'axonius_splash_xlsx'}
+ Upload the Cvent Splash XLSX registrant export file here. Only registrants with status "Attending" are imported by default. Server-side processing can take a few seconds to a few minutes.
+ {:else}
+ Upload the standard Axonius Zoom event tickets CSV export file here. This will trigger server-side processing to import the new and changed badges. This can take a few seconds to a few minutes.
+ {/if}