From 89c1decf8d0134de77129b12b8da76258f20e26c Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Wed, 10 Jun 2026 16:00:18 -0400 Subject: [PATCH] feat(badges): add CSV export to badge reports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an "Export CSV" report to the badge reports page. Generates a clean CSV client-side from Dexie cache — no backend call needed. - 3 filter presets: Printed+Clean (default), Printed all, All badges - Printed+Clean mirrors the manually-cleaned Axonius export (printed, non-hidden, non-test badge type) - Timezone selector: Eastern (default), Local, UTC — addresses the UTC→Eastern conversion needed for post-event client exports - 24 columns: identity fields, override pairs, print status, created_on - UTF-8 BOM for direct Excel open without import wizard - Auto-generated filename from event name + date + filter suffix Co-Authored-By: Claude Sonnet 4.6 --- .../(badges)/badges/reports/+page.svelte | 23 +- .../reports/reports_badge_export.svelte | 291 ++++++++++++++++++ 2 files changed, 312 insertions(+), 2 deletions(-) create mode 100644 src/routes/events/[event_id]/(badges)/badges/reports/reports_badge_export.svelte diff --git a/src/routes/events/[event_id]/(badges)/badges/reports/+page.svelte b/src/routes/events/[event_id]/(badges)/badges/reports/+page.svelte index 238056fd..1e7a831b 100644 --- a/src/routes/events/[event_id]/(badges)/badges/reports/+page.svelte +++ b/src/routes/events/[event_id]/(badges)/badges/reports/+page.svelte @@ -13,13 +13,14 @@ import { ae_loc } from '$lib/stores/ae_stores'; import { db_events } from '$lib/ae_events/db_events'; import { events_loc } from '$lib/stores/ae_events_stores'; -import { ArrowLeft, TrendingUp, Type, Gauge, LoaderCircle } from '@lucide/svelte'; +import { ArrowLeft, TrendingUp, Type, Gauge, FileSpreadsheet, LoaderCircle } from '@lucide/svelte'; import Reports_badge_long_names from './reports_badge_long_names.svelte'; import Reports_badge_print_throughput from './reports_badge_print_throughput.svelte'; +import Reports_badge_export from './reports_badge_export.svelte'; let event_id = $derived(page.params.event_id); -type ReportKey = 'long_names' | 'print_throughput'; +type ReportKey = 'long_names' | 'print_throughput' | 'export'; let active_report: ReportKey | null = $state(null); let lq__event_obj = $derived( @@ -107,6 +108,18 @@ let reprint_count = $derived( Print Throughput + {#if $lq__badge_li === undefined} @@ -118,6 +131,12 @@ let reprint_count = $derived( {:else if active_report === 'print_throughput'} + {:else if active_report === 'export'} + {:else}

Select a report above to view results.

{/if} diff --git a/src/routes/events/[event_id]/(badges)/badges/reports/reports_badge_export.svelte b/src/routes/events/[event_id]/(badges)/badges/reports/reports_badge_export.svelte new file mode 100644 index 00000000..5c4e35cb --- /dev/null +++ b/src/routes/events/[event_id]/(badges)/badges/reports/reports_badge_export.svelte @@ -0,0 +1,291 @@ + + +
+ +
+
+

Records

+
+ {#each ([['printed_clean', 'Printed + Clean'], ['printed', 'Printed (all)'], ['all', 'All badges']] as [FilterMode, string][]) as [mode, label] (mode)} + + {/each} +
+
+ +
+

Timestamps

+
+ {#each (['eastern', 'local', 'utc'] as TzMode[]) as tz (tz)} + + {/each} +
+
+
+ + +
+
+ {#if filtered_badges.length === 0} + No records match this filter. + {:else} + {filtered_badges.length} record{filtered_badges.length !== 1 ? 's' : ''} + {#if filter_mode !== 'all'} + ({print_count_total} printed) + {/if} + · {COLUMNS.length} columns · timestamps in {TZ_LABEL[tz_mode]} + {/if} +
+ + +
+ + +
+ + Columns included ({COLUMNS.length}) + +
+ {#each COLUMNS as col (col.header)} + + {col.header} + + {/each} +
+

+ Fields highlighted in primary color are override fields — values set by staff or attendee before printing. +

+
+ + +

+ {#if filter_mode === 'printed_clean'} + Printed + Clean: Printed badges with non-hidden records and no test badge types. This is the standard export sent to clients. + {:else if filter_mode === 'printed'} + Printed (all): All badges with at least one print, including hidden records and test types. + {:else} + All badges: Every badge record in the local cache, including unprinted, hidden, and test records. + {/if} + Rows without a value in external_id were added manually (not via CSV import). +

+