refactor(badges): move hide toggle + print count editor to individual badge view
Hide/Unhide and print count edit belong on the per-badge page (print controls staff section), not the search list — the list was getting too crowded. - ae_comp__badge_obj_li: removed hide toggle, print count input, and the ae_api/events_func imports that were only there to support them - ae_comp__badge_print_controls: added Hide Badge button (Trusted, top of staff section) and Print Count editor (Admin+, below hide); both reuse the existing save_field/field_save_status pattern for consistent spinner/done/error feedback Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -112,6 +112,7 @@ let is_auth = $derived($ae_loc.authenticated_access === true);
|
||||
// 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);
|
||||
let is_admin = $derived($ae_loc.administrator_access === true);
|
||||
|
||||
// --- Per-template controls config ---
|
||||
// Stored in event_badge_template.other_json as { controls_cfg: { shown?, auth_editable? } }.
|
||||
@@ -1554,6 +1555,72 @@ let allow_tracking_open = $derived(
|
||||
|
||||
<div class="ctrl-accordion" class:open={staff_open}>
|
||||
<div class="ctrl-accordion-inner space-y-1 pt-0.5">
|
||||
<!-- === HIDE BADGE ===
|
||||
Removes this badge from search results for all users.
|
||||
Use for duplicates, test records, or badges that should not be printed.
|
||||
Trusted + Edit Mode. Visible even when badge is already hidden so staff
|
||||
can recover it without needing to enable Show Hidden in search first. -->
|
||||
<div class="flex items-center gap-2 px-2 py-1">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm w-full justify-between"
|
||||
class:preset-tonal-warning={$lq__event_badge_obj?.hide}
|
||||
class:preset-tonal-surface={!$lq__event_badge_obj?.hide}
|
||||
disabled={field_save_status['hide'] === 'saving'}
|
||||
onclick={() => save_field('hide', { hide: !$lq__event_badge_obj?.hide })}
|
||||
title={$lq__event_badge_obj?.hide
|
||||
? 'Badge is hidden — click to unhide and restore to search results'
|
||||
: 'Hide this badge from search results'}>
|
||||
<span class="flex items-center gap-1.5">
|
||||
{#if field_save_status['hide'] === 'saving'}
|
||||
<LoaderCircle size="13" class="animate-spin shrink-0" />
|
||||
{$lq__event_badge_obj?.hide ? 'Unhiding…' : 'Hiding…'}
|
||||
{:else if $lq__event_badge_obj?.hide}
|
||||
<Eye size="13" class="shrink-0" />
|
||||
Unhide Badge
|
||||
{:else}
|
||||
<EyeOff size="13" class="shrink-0" />
|
||||
Hide Badge
|
||||
{/if}
|
||||
</span>
|
||||
{#if field_save_status['hide'] === 'done'}
|
||||
<Check size="13" class="text-success-500 shrink-0" />
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- === PRINT COUNT (Admin+) ===
|
||||
Correction tool for when the physical count diverges from the DB
|
||||
(e.g. badge was printed offline or kiosk count reset). Does NOT
|
||||
change print_first_datetime / print_last_datetime — timestamps
|
||||
are only written by the actual Print Badge action. -->
|
||||
{#if is_admin}
|
||||
<div class="px-2 py-1">
|
||||
<p class="field-label mb-1">
|
||||
Print Count
|
||||
<span class="ml-1 text-[9px] font-normal text-gray-400 normal-case tracking-normal">Admin · timestamps unchanged</span>
|
||||
</p>
|
||||
<div class="flex items-center gap-2">
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
value={print_count}
|
||||
onblur={(e) => {
|
||||
const v = Math.max(0, Math.round(Number((e.currentTarget as HTMLInputElement).value)));
|
||||
if (v !== print_count) save_field('print_count', { print_count: v });
|
||||
}}
|
||||
onkeydown={(e) => { if (e.key === 'Enter') (e.currentTarget as HTMLInputElement).blur(); }}
|
||||
disabled={field_save_status['print_count'] === 'saving'}
|
||||
class="input input-sm w-20 text-center font-mono" />
|
||||
{#if field_save_status['print_count'] === 'saving'}
|
||||
<LoaderCircle size="13" class="animate-spin opacity-70" />
|
||||
{:else if field_save_status['print_count'] === 'done'}
|
||||
<Check size="13" class="text-success-500" />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Chrome visibility toggle: hides page header + sys bar for a clean workspace.
|
||||
Keyboard shortcut [H] does the same thing from anywhere on the page. -->
|
||||
<div class="flex items-center gap-2 px-2 py-1">
|
||||
|
||||
@@ -19,11 +19,10 @@ let {
|
||||
hide_badge_type = false
|
||||
}: Props = $props();
|
||||
|
||||
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
||||
import { ae_loc } from '$lib/stores/ae_stores';
|
||||
import { events_loc } from '$lib/stores/ae_events_stores';
|
||||
import { badges_loc } from '$lib/stores/ae_events_stores__badges.svelte';
|
||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||
import { events_func } from '$lib/ae_events/ae_events_functions';
|
||||
import {
|
||||
Check,
|
||||
Eye,
|
||||
@@ -42,55 +41,9 @@ let copy_status: Record<string, 'idle' | 'copied'> = $state({});
|
||||
|
||||
// Access level shortcuts
|
||||
let is_trusted = $derived($ae_loc.trusted_access === true);
|
||||
let is_admin = $derived($ae_loc.administrator_access === true);
|
||||
let is_public = $derived($ae_loc.public_access === true); // public passcode or higher — may print first prints
|
||||
let is_edit_mode = $derived($ae_loc.edit_mode === true);
|
||||
|
||||
// Per-badge async action states
|
||||
let hide_status: Record<string, 'idle' | 'loading'> = $state({});
|
||||
let print_count_status: Record<string, 'idle' | 'saving'> = $state({});
|
||||
|
||||
async function toggle_badge_hide(badge_obj: any) {
|
||||
const id = badge_obj.event_badge_id;
|
||||
if (!id || hide_status[id] === 'loading') return;
|
||||
hide_status[id] = 'loading';
|
||||
try {
|
||||
await events_func.update_ae_obj__event_badge({
|
||||
api_cfg: $ae_api,
|
||||
event_id: badge_obj.event_id,
|
||||
event_badge_id: id,
|
||||
data_kv: { hide: !badge_obj.hide },
|
||||
log_lvl
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Failed to toggle hide:', e);
|
||||
}
|
||||
hide_status[id] = 'idle';
|
||||
}
|
||||
|
||||
// Admin-only: edit the raw print count. Does NOT touch timestamps — those are
|
||||
// only set by the actual print action. This is a correction tool (e.g. "badge
|
||||
// was printed offline, count needs to match reality").
|
||||
async function save_print_count(badge_obj: any, new_count: number) {
|
||||
const id = badge_obj.event_badge_id;
|
||||
if (!id || print_count_status[id] === 'saving') return;
|
||||
const count = Math.max(0, Math.round(new_count));
|
||||
if (count === (badge_obj.print_count ?? 0)) return; // no-op
|
||||
print_count_status[id] = 'saving';
|
||||
try {
|
||||
await events_func.update_ae_obj__event_badge({
|
||||
api_cfg: $ae_api,
|
||||
event_id: badge_obj.event_id,
|
||||
event_badge_id: id,
|
||||
data_kv: { print_count: count },
|
||||
log_lvl
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Failed to update print count:', e);
|
||||
}
|
||||
print_count_status[id] = 'idle';
|
||||
}
|
||||
|
||||
/**
|
||||
* Obscures an email address for display to non-trusted users.
|
||||
* e.g. john.doe@example.com → joh***@example.com
|
||||
@@ -320,25 +273,6 @@ let visible_badge_obj_li = $derived(
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
<!-- 1.5. Hide/Unhide badge: Trusted + Edit Mode -->
|
||||
{#if is_trusted && is_edit_mode}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => toggle_badge_hide(event_badge_obj)}
|
||||
disabled={hide_status[event_badge_obj.event_badge_id] === 'loading'}
|
||||
class="btn btn-sm flex items-center gap-1 border {event_badge_obj.hide ? 'preset-tonal-warning border-warning-200-800' : 'preset-tonal-surface border-surface-300-700'}"
|
||||
title={event_badge_obj.hide ? 'Unhide badge' : 'Hide badge'}>
|
||||
{#if hide_status[event_badge_obj.event_badge_id] === 'loading'}
|
||||
<LoaderCircle size="1em" class="animate-spin" />
|
||||
{:else if event_badge_obj.hide}
|
||||
<Eye size="1em" />
|
||||
{:else}
|
||||
<EyeOff size="1em" />
|
||||
{/if}
|
||||
<span class="hidden sm:inline">{event_badge_obj.hide ? 'Unhide' : 'Hide'}</span>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<!-- 2. Direct Review link: Trusted + Edit Mode (navigates to /review) -->
|
||||
{#if is_trusted && is_edit_mode}
|
||||
<a
|
||||
@@ -417,22 +351,7 @@ let visible_badge_obj_li = $derived(
|
||||
</span>
|
||||
<span class="flex items-center gap-1">
|
||||
<span class="font-bold opacity-50">PC:</span>
|
||||
{#if is_admin}
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
value={print_count}
|
||||
onblur={(e) => save_print_count(event_badge_obj, Number((e.currentTarget as HTMLInputElement).value))}
|
||||
onkeydown={(e) => { if (e.key === 'Enter') (e.currentTarget as HTMLInputElement).blur(); }}
|
||||
disabled={print_count_status[event_badge_obj.event_badge_id] === 'saving'}
|
||||
class="w-14 rounded border border-surface-300 bg-surface-100 px-1 text-center font-mono text-[10px] dark:border-surface-700 dark:bg-surface-800"
|
||||
title="Edit print count (Admin+). Does not change timestamps." />
|
||||
{#if print_count_status[event_badge_obj.event_badge_id] === 'saving'}
|
||||
<LoaderCircle size="0.8em" class="animate-spin opacity-70" />
|
||||
{/if}
|
||||
{:else}
|
||||
{print_count}
|
||||
{/if}
|
||||
{print_count}
|
||||
</span>
|
||||
<span class="flex items-center gap-1">
|
||||
<span class="font-bold opacity-50">FP:</span>
|
||||
|
||||
Reference in New Issue
Block a user