Badges: per-badge locked font sizes via cfg_json
Allows coordinators to pre-tune font sizes for attendees with long names and have those sizes apply automatically on every kiosk, not just one machine. - ae_types.ts: add cfg_json to ae_EventBadge interface - db_events.ts: add cfg_json to Badge Dexie interface - ae_events__event_badge.ts: add cfg_json to properties_to_save so it is persisted to IndexedDB on load and returned by the API - print/+page.svelte: on first load per badge, read cfg_json.font_sizes and initialize font_size_name/title/affiliations/location state from saved values (guarded by _font_sizes_loaded_for to avoid clobbering user adjustments on background liveQuery refreshes) - ae_comp__badge_print_controls.svelte: add lock_font_sizes() and reset_font_sizes_to_auto() functions; add Lock Sizes / Auto reset UI in the Staff adjustments section (trusted-only); button shows warning style when sizes are unsaved vs success when locked; status indicator shows what is currently locked Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -632,6 +632,7 @@ export const properties_to_save = [
|
||||
'print_last_datetime',
|
||||
'allow_tracking',
|
||||
'agree_to_tc',
|
||||
'cfg_json',
|
||||
'other_1_code',
|
||||
'other_2_code',
|
||||
'other_3_code',
|
||||
|
||||
@@ -170,6 +170,7 @@ export interface Badge {
|
||||
|
||||
// passcode?: null|string;
|
||||
|
||||
cfg_json?: null | string;
|
||||
// data_json?: null|string;
|
||||
|
||||
default_qry_str?: null | string;
|
||||
|
||||
@@ -507,6 +507,7 @@ export interface ae_EventBadge extends ae_BaseObj {
|
||||
agree_to_tc?: boolean | null;
|
||||
|
||||
ticket_list?: any[] | null;
|
||||
cfg_json?: any;
|
||||
data_json?: any;
|
||||
default_qry_str?: string | null;
|
||||
}
|
||||
|
||||
@@ -624,6 +624,113 @@ let is_dirty_badge_type = $derived(
|
||||
null)
|
||||
);
|
||||
|
||||
// --- Lock / Reset font sizes to badge cfg_json ---
|
||||
// "Lock Sizes" saves the current font_size_* values into event_badge.cfg_json.font_sizes.
|
||||
// Any kiosk that opens this badge will then load those sizes instead of auto-sizing.
|
||||
// "Reset to Auto" saves null for all fields, restoring auto-sizing everywhere.
|
||||
// Trusted-only — attendees at the badge table can't permanently alter the layout.
|
||||
type LockStatus = 'idle' | 'saving' | 'done' | 'error';
|
||||
let lock_sizes_status: LockStatus = $state('idle');
|
||||
|
||||
// True when at least one size is non-null (i.e. user has adjusted something)
|
||||
let has_any_size_override = $derived(
|
||||
font_size_name !== null ||
|
||||
font_size_title !== null ||
|
||||
font_size_affiliations !== null ||
|
||||
font_size_location !== null
|
||||
);
|
||||
|
||||
// Detect whether current state differs from what is saved in badge cfg_json
|
||||
// so we can highlight the button when there are unsaved size changes.
|
||||
let saved_font_sizes = $derived.by(() => {
|
||||
try {
|
||||
const cfg = typeof $lq__event_badge_obj?.cfg_json === 'string'
|
||||
? JSON.parse($lq__event_badge_obj.cfg_json)
|
||||
: ($lq__event_badge_obj?.cfg_json ?? {});
|
||||
return cfg?.font_sizes ?? null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
let sizes_are_dirty = $derived(
|
||||
font_size_name !== (saved_font_sizes?.name ?? null) ||
|
||||
font_size_title !== (saved_font_sizes?.title ?? null) ||
|
||||
font_size_affiliations !== (saved_font_sizes?.affiliations ?? null) ||
|
||||
font_size_location !== (saved_font_sizes?.location ?? null)
|
||||
);
|
||||
|
||||
async function lock_font_sizes() {
|
||||
if (!$lq__event_badge_obj?.event_badge_id) return;
|
||||
lock_sizes_status = 'saving';
|
||||
try {
|
||||
// Merge into existing cfg_json — preserve any other keys that may be there
|
||||
const existing_cfg = (() => {
|
||||
try {
|
||||
return typeof $lq__event_badge_obj.cfg_json === 'string'
|
||||
? JSON.parse($lq__event_badge_obj.cfg_json)
|
||||
: ($lq__event_badge_obj.cfg_json ?? {});
|
||||
} catch { return {}; }
|
||||
})();
|
||||
const new_cfg = {
|
||||
...existing_cfg,
|
||||
font_sizes: {
|
||||
name: font_size_name,
|
||||
title: font_size_title,
|
||||
affiliations: font_size_affiliations,
|
||||
location: font_size_location
|
||||
}
|
||||
};
|
||||
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: { cfg_json: JSON.stringify(new_cfg) },
|
||||
log_lvl
|
||||
});
|
||||
lock_sizes_status = 'done';
|
||||
setTimeout(() => { lock_sizes_status = 'idle'; }, 1500);
|
||||
} catch (err) {
|
||||
console.error('Badge print controls: lock font sizes error:', err);
|
||||
lock_sizes_status = 'error';
|
||||
setTimeout(() => { lock_sizes_status = 'idle'; }, 2500);
|
||||
}
|
||||
}
|
||||
|
||||
async function reset_font_sizes_to_auto() {
|
||||
// Clear all local size state
|
||||
font_size_name = null;
|
||||
font_size_title = null;
|
||||
font_size_affiliations = null;
|
||||
font_size_location = null;
|
||||
if (!$lq__event_badge_obj?.event_badge_id) return;
|
||||
// Persist the reset back to the badge — removes the locked sizes server-side
|
||||
lock_sizes_status = 'saving';
|
||||
try {
|
||||
const existing_cfg = (() => {
|
||||
try {
|
||||
return typeof $lq__event_badge_obj.cfg_json === 'string'
|
||||
? JSON.parse($lq__event_badge_obj.cfg_json)
|
||||
: ($lq__event_badge_obj.cfg_json ?? {});
|
||||
} catch { return {}; }
|
||||
})();
|
||||
const new_cfg = { ...existing_cfg };
|
||||
delete new_cfg.font_sizes;
|
||||
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: { cfg_json: Object.keys(new_cfg).length ? JSON.stringify(new_cfg) : null },
|
||||
log_lvl
|
||||
});
|
||||
lock_sizes_status = 'done';
|
||||
setTimeout(() => { lock_sizes_status = 'idle'; }, 1500);
|
||||
} catch (err) {
|
||||
console.error('Badge print controls: reset font sizes error:', err);
|
||||
lock_sizes_status = 'error';
|
||||
setTimeout(() => { lock_sizes_status = 'idle'; }, 2500);
|
||||
}
|
||||
}
|
||||
|
||||
// TC modal ref for the lead scanning terms & conditions dialog
|
||||
let tc_dialog_ref: HTMLDialogElement | undefined;
|
||||
|
||||
@@ -1476,6 +1583,50 @@ let allow_tracking_open = $derived(
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- === LOCK FONT SIZES ===
|
||||
Saves the current font_size_* values into event_badge.cfg_json.font_sizes
|
||||
so they survive page reloads and apply on every kiosk, not just this machine.
|
||||
Use this for attendees with long names that auto-sizing can't handle well.
|
||||
Trusted-only. -->
|
||||
<div class="space-y-1 px-1 pt-1">
|
||||
<p class="field-label px-1">Locked Font Sizes</p>
|
||||
<div class="flex gap-1.5">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-xs flex-1 transition-colors {lock_sizes_status === 'done' ? 'preset-filled-success' : lock_sizes_status === 'error' ? 'preset-tonal-error' : lock_sizes_status === 'saving' ? 'preset-tonal-surface' : sizes_are_dirty ? 'preset-filled-warning' : 'preset-tonal-surface'}"
|
||||
disabled={lock_sizes_status === 'saving' || !has_any_size_override}
|
||||
onclick={lock_font_sizes}
|
||||
title="Save current font sizes to this badge record — applies on all kiosks">
|
||||
{#if lock_sizes_status === 'saving'}
|
||||
<LoaderCircle size="11" class="mr-1 animate-spin" /> Saving…
|
||||
{:else if lock_sizes_status === 'done'}
|
||||
<Check size="11" class="mr-1" /> Locked
|
||||
{:else if lock_sizes_status === 'error'}
|
||||
Error
|
||||
{:else}
|
||||
🔒 Lock Sizes
|
||||
{/if}
|
||||
</button>
|
||||
{#if saved_font_sizes || has_any_size_override}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-xs preset-tonal-warning shrink-0"
|
||||
disabled={lock_sizes_status === 'saving'}
|
||||
onclick={reset_font_sizes_to_auto}
|
||||
title="Reset all font sizes to auto and clear saved sizes from badge record">
|
||||
↺ Auto
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{#if saved_font_sizes}
|
||||
<p class="px-1 text-[9px] text-green-600 dark:text-green-400">
|
||||
Sizes locked on this badge
|
||||
{#if saved_font_sizes.name}· Name {saved_font_sizes.name}px{/if}
|
||||
{#if saved_font_sizes.title}· Title {saved_font_sizes.title}px{/if}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Print Position Offset
|
||||
Per-browser calibration for physical printer alignment.
|
||||
Stored in localStorage by the parent page — each workstation keeps
|
||||
|
||||
@@ -100,11 +100,41 @@ function send_review_email() {
|
||||
// Controls live in Comp_badge_print_controls (right panel) via $bindable().
|
||||
// Constants and adjust logic are defined there; only the state lives here so
|
||||
// the values can be forwarded to both the controls and the badge render.
|
||||
//
|
||||
// Initialization order: badge cfg_json.font_sizes takes precedence (persisted per-badge),
|
||||
// falling back to null (auto) on first load. The controls panel's "Lock Sizes" button
|
||||
// saves current sizes back to cfg_json so they survive page reloads and cross-kiosk.
|
||||
let font_size_name: number | null = $state(null);
|
||||
let font_size_title: number | null = $state(null);
|
||||
let font_size_affiliations: number | null = $state(null);
|
||||
let font_size_location: number | null = $state(null);
|
||||
|
||||
// Track whether we've applied the saved sizes for this badge yet.
|
||||
// Prevents re-applying on every liveQuery tick after the user adjusts sizes.
|
||||
let _font_sizes_loaded_for: string | null = null;
|
||||
|
||||
$effect(() => {
|
||||
const badge = $lq__event_badge_obj;
|
||||
if (!badge?.event_badge_id) return;
|
||||
// Only apply once per badge ID — don't clobber user adjustments on background refreshes
|
||||
if (_font_sizes_loaded_for === badge.event_badge_id) return;
|
||||
_font_sizes_loaded_for = badge.event_badge_id;
|
||||
try {
|
||||
const cfg = typeof badge.cfg_json === 'string'
|
||||
? JSON.parse(badge.cfg_json)
|
||||
: (badge.cfg_json ?? {});
|
||||
const fs = cfg?.font_sizes;
|
||||
if (fs) {
|
||||
font_size_name = fs.name ?? null;
|
||||
font_size_title = fs.title ?? null;
|
||||
font_size_affiliations = fs.affiliations ?? null;
|
||||
font_size_location = fs.location ?? null;
|
||||
}
|
||||
} catch {
|
||||
// Malformed cfg_json — stay with null (auto) for all fields
|
||||
}
|
||||
});
|
||||
|
||||
// Per-browser print workstation tweaks.
|
||||
// Each workstation/printer is calibrated independently — values are stored in
|
||||
// localStorage so they survive page reloads and browser restarts without any
|
||||
|
||||
Reference in New Issue
Block a user