refactor: v2 badge render display-only + print button to controls panel

- ae_comp__badge_obj_view_v2.svelte: removed all inline edit-mode logic
  (floating Edit/Save/Cancel panel, placeholder list, input fields, save/cancel
  functions). V2 is now purely a display component — editing happens in the right
  panel (ae_comp__badge_print_controls.svelte) via liveQuery reactivity.
  display_* values are now $derived directly from lq__event_badge_obj.
  Fixes effective_badge_type_code CSS class (was always empty in previous v2).

- ae_comp__badge_print_controls.svelte: added "Print Badge" button at the top of
  the panel. Increments print_count, fires window.print(), then navigates to badge
  search. This is now the canonical print action for v2; the header "Print Now"
  button is a shortcut that calls window.print() only (no count tracking).
  Clarified $ae_loc.edit_mode comment — global AE Edit Mode is not a badge-specific
  edit toggle; used here only to gate reprints.

- print/+page.svelte: added clarifying comments on is_edit_mode (global vs local)
  and the header "Print Now" button (shortcut only, no count tracking).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-03-12 13:48:08 -04:00
parent 2198c55e27
commit c7063806b7
3 changed files with 270 additions and 691 deletions

View File

@@ -18,7 +18,8 @@
import type { key_val } from '$lib/stores/ae_stores';
import { ae_api, ae_loc } from '$lib/stores/ae_stores';
import { events_func } from '$lib/ae_events_functions';
import { Pencil, Check, X, LoaderCircle, ChevronDown } from 'lucide-svelte';
import { browser } from '$app/environment';
import { Pencil, Check, X, LoaderCircle, ChevronDown, Printer } from 'lucide-svelte';
interface Props {
event_id: string;
@@ -55,6 +56,58 @@
// professional_title_override, affiliations_override, and location_override.
let is_trusted = $derived($ae_loc.trusted_access === true);
// IMPORTANT: $ae_loc.edit_mode is the GLOBAL AE Edit Mode — a UI preference that
// reveals editable fields, debug info, and advanced options across the whole app.
// It is NOT the same as the badge-specific edit state (which no longer exists in
// the v2 badge render — all badge editing happens here in the controls panel).
// This component NEVER writes to $ae_loc.edit_mode. Read-only usage only:
// — used here to allow reprinting an already-printed badge when global edit mode is active.
let is_global_edit_mode = $derived($ae_loc.edit_mode === true);
// --- Print ---
let print_count = $derived($lq__event_badge_obj?.print_count ?? 0);
let is_printed = $derived(print_count >= 1);
// Print is available to Trusted+ when: not yet printed, OR global edit mode is on (reprint).
let can_print = $derived(is_trusted && (!is_printed || is_global_edit_mode));
type PrintStatus = 'idle' | 'loading' | 'done' | 'error';
let print_status: PrintStatus = $state('idle');
async function handle_print_badge() {
if (!$lq__event_badge_obj?.event_badge_id) return;
print_status = 'loading';
const now = new Date().toISOString();
const is_first_print = print_count === 0;
const data_to_update: key_val = {
print_count: print_count + 1,
print_last_datetime: now
};
if (is_first_print) data_to_update.print_first_datetime = now;
try {
await events_func.update_ae_obj__event_badge({
api_cfg: $ae_api,
event_id,
event_badge_id: $lq__event_badge_obj.event_badge_id,
data_kv: data_to_update,
log_lvl
});
print_status = 'done';
// Trigger browser print dialog after count is recorded
if (browser) window.print();
// Brief success flash, then return to badge search
await new Promise<void>(r => setTimeout(r, 1000));
// Full navigation back to badge search — avoids goto() lint rule in child components
if (browser) window.location.href = `/events/${event_id}/badges`;
} catch (err) {
console.error('Badge print controls: print error:', err);
print_status = 'error';
setTimeout(() => { print_status = 'idle'; }, 3000);
}
}
// --- Badge type list from template ---
// Each item: { code: string, name: string }. Used for the badge type dropdown.
let badge_type_code_li = $derived.by((): { code: string; name: string }[] => {
@@ -265,6 +318,45 @@
<!-- ============================================================
Main panel
============================================================ -->
<!-- Print button — canonical print action for v2. Increments print_count, fires
window.print(), then navigates back to badge search. Only shown to Trusted+
when not yet printed, OR when global AE Edit Mode is active (allows reprints).
The header "Print Now" button is a shortcut that calls window.print() only —
it does NOT track print count. Always use this button for the official print. -->
{#if can_print}
<div class="mb-3 pb-3 border-b border-gray-200 dark:border-gray-700">
<button
type="button"
class="btn btn-sm w-full flex items-center justify-center gap-2"
class:preset-filled-primary={print_status === 'idle'}
class:preset-tonal-surface={print_status === 'loading'}
class:preset-filled-success={print_status === 'done'}
class:preset-tonal-error={print_status === 'error'}
onclick={handle_print_badge}
disabled={print_status === 'loading' || print_status === 'done'}
title={is_printed ? `Reprint badge (printed ${print_count}×)` : 'Print badge now'}
data-testid="badge-print-btn"
>
{#if print_status === 'loading'}
<LoaderCircle size="14" class="animate-spin" /> Printing…
{:else if print_status === 'done'}
<Check size="14" /> Printed!
{:else if print_status === 'error'}
Error — try again
{:else}
<Printer size="14" />
{is_printed ? `Reprint (${print_count}×)` : 'Print Badge'}
{/if}
</button>
{#if is_printed && print_status === 'idle'}
<p class="text-[10px] text-amber-600 dark:text-amber-400 text-center mt-1">
Already printed {print_count}×
</p>
{/if}
</div>
{/if}
<div class="space-y-2 text-sm">
<!-- === NAME === -->

View File

@@ -52,6 +52,11 @@
// Access level shortcuts
let is_trusted = $derived($ae_loc.trusted_access === true);
// IMPORTANT: $ae_loc.edit_mode is the GLOBAL AE Edit Mode — a UI preference that
// reveals editable fields, debug info, and advanced options app-wide. It is NOT
// a badge-specific edit toggle. Never write to it from badge components.
// Used here only to gate the header "Print Now" shortcut (allow reprints in edit mode).
let is_edit_mode = $derived($ae_loc.edit_mode === true);
// Print state derived from badge
@@ -186,7 +191,11 @@
{use_v2 ? 'v2' : 'v1'}
</button>
<!-- 1. Print Now: Trusted+, not printed OR Edit Mode (reprint) -->
<!-- 1. Print Now (header shortcut): calls window.print() only — does NOT track
print_count. The canonical "Print Badge" button in the right panel controls
increments the count, fires window.print(), then redirects to badge search.
Use this header button only as a quick re-trigger of the print dialog. -->
<!-- Trusted+, not printed OR global Edit Mode active (reprint) -->
{#if is_trusted && (!is_printed || is_edit_mode)}
<button
type="button"