feat: leads re-enable flow — detect removed leads on scan + Remove/Restore buttons

- QR scanner (single + multi): detect previously-removed leads via IDB enable flag;
  route to 'reenable' state instead of duplicate error; offer Re-activate button
- API fallback: if create fails and no IDB record, search API for disabled tracking
  record by event_exhibit_id + event_badge_id (adds qry_badge_id param to
  search__exhibit_tracking)
- Lead detail page: Replace raw enable checkbox with Remove Lead (two-click confirm,
  navigates back after) and Restore Lead card (shown when enable is falsy)
- Fix flash of disabled records in leads list: filter !enable in both filtered_lead_li
  derived and local IDB fast-path in handle_search_refresh
- eslint.config.js: disable svelte/no-navigation-without-resolve (no base path configured)
- Also includes _random field annotation cleanup (db_events, ae_types), iframe layout
  fixes, badge view tweaks, test updates, and doc updates from prior session

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-03-20 18:18:10 -04:00
parent 4586e809d7
commit 6662e82f40
18 changed files with 505 additions and 266 deletions

View File

@@ -458,6 +458,7 @@ export async function search__exhibit_tracking({
fulltext_search_qry_str = null,
qry_group = null,
qry_external_person_id = null,
qry_badge_id = null,
enabled = 'enabled',
hidden = 'all',
view = 'default',
@@ -473,6 +474,7 @@ export async function search__exhibit_tracking({
fulltext_search_qry_str?: string | null;
qry_group?: string | null;
qry_external_person_id?: string | null;
qry_badge_id?: string | null;
enabled?: 'enabled' | 'all' | 'not_enabled';
hidden?: 'hidden' | 'all' | 'not_hidden';
view?: string;
@@ -498,6 +500,7 @@ export async function search__exhibit_tracking({
if (qry_group) search_query.and.push({ field: 'group', op: 'eq', value: qry_group });
if (qry_external_person_id) search_query.and.push({ field: 'external_person_id', op: 'eq', value: qry_external_person_id });
if (qry_badge_id) search_query.and.push({ field: 'event_badge_id', op: 'eq', value: qry_badge_id });
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 0 });

View File

@@ -16,12 +16,12 @@ export interface Event {
id: string;
// id_random: string;
event_id: string;
event_id_random: string;
event_id_random: string; // NO LONGER USE "_random"
code: string;
account_id: string;
account_id_random: string;
account_id_random: string; // NO LONGER USE "_random"
conference: boolean;
type: string;
@@ -114,7 +114,7 @@ export interface Badge {
id: string;
// id_random: string;
event_badge_id: string;
event_badge_id_random: string;
event_badge_id_random: string; // NO LONGER USE "_random"
event_id: string;
event_id_random: string;
@@ -210,10 +210,10 @@ export interface Badge_template {
// id_random: string;
event_badge_template_id?: null | string;
event_badge_template_id_random?: null | string;
event_badge_template_id_random?: null | string; // NO LONGER USE "_random"
event_id: string;
event_id_random: string;
event_id_random: string; // NO LONGER USE "_random"
name: string;
description?: null | string;
@@ -274,14 +274,14 @@ export interface Badge_template {
// Updated 2024-10-16
export interface Device {
id: string;
// id_random: string;
// id_random: string; // NO LONGER USE "_random"
event_device_id: string;
// event_device_id_random: string;
// event_device_id_random: string; // NO LONGER USE "_random"
event_id: string;
// event_id_random: string;
// event_id_random: string; // NO LONGER USE "_random"
event_location_id?: string;
// event_location_id_random?: string;
// event_location_id_random?: string; // NO LONGER USE "_random"
code?: string;
name: string;
@@ -342,12 +342,12 @@ export interface Device {
export interface Exhibit {
id: string;
id_random: string;
id_random: string; // NO LONGER USE "_random"
event_exhibit_id: string;
event_exhibit_id_random: string;
event_exhibit_id_random: string; // NO LONGER USE "_random"
event_id: string;
event_id_random: string;
event_id_random: string; // NO LONGER USE "_random"
code: string;
name: string;
@@ -380,16 +380,16 @@ export interface Exhibit {
export interface Exhibit_tracking {
id: string;
id_random: string;
id_random: string; // NO LONGER USE "_random"
event_exhibit_tracking_id: string;
event_exhibit_tracking_id_random: string;
event_exhibit_tracking_id_random: string; // NO LONGER USE "_random"
event_exhibit_id: string;
event_exhibit_id_random: string;
event_exhibit_id_random: string; // NO LONGER USE "_random"
event_badge_id: string;
event_badge_id_random: string;
event_badge_id_random: string; // NO LONGER USE "_random"
event_person_id: string;
event_person_id_random?: string;
event_person_id_random?: string; // NO LONGER USE "_random"
event_id: string;
event_id_random: string;
@@ -442,28 +442,28 @@ export interface Exhibit_tracking {
export interface EventFile {
id: string;
id_random: string;
id_random: string; // NO LONGER USE "_random"
event_file_id: string;
event_file_id_random?: string;
event_file_id_random?: string; // NO LONGER USE "_random"
hosted_file_id: string;
hosted_file_id_random?: string;
hosted_file_id_random?: string; // NO LONGER USE "_random"
hash_sha256: string;
for_type?: string;
for_id?: string;
for_id_random?: string;
for_id_random?: string; // NO LONGER USE "_random"
event_id: string;
event_id_random?: string;
event_id_random?: string; // NO LONGER USE "_random"
event_session_id?: string;
event_session_id_random?: string;
event_session_id_random?: string; // NO LONGER USE "_random"
event_presentation_id?: string;
event_presentation_id_random?: string;
event_presentation_id_random?: string; // NO LONGER USE "_random"
event_presenter_id?: string;
event_presenter_id_random?: string;
event_presenter_id_random?: string; // NO LONGER USE "_random"
event_location_id?: string;
event_location_id_random?: string;
event_location_id_random?: string; // NO LONGER USE "_random"
filename: string;
extension: string;
@@ -517,7 +517,7 @@ export interface Location {
id: string;
// id_random: string;
event_location_id: string;
event_location_id_random: string;
event_location_id_random: string; // NO LONGER USE "_random"
external_id?: null | string;
code?: null | string;
@@ -525,7 +525,7 @@ export interface Location {
type_code?: string;
event_id: string;
event_id_random: string;
event_id_random: string; // NO LONGER USE "_random"
name: string;
description?: null | string;
@@ -571,23 +571,23 @@ export interface Presentation {
id: string;
// id_random: string;
event_presentation_id: string;
event_presentation_id_random: string;
event_presentation_id_random: string; // NO LONGER USE "_random"
external_id?: null | string;
code?: null | string;
for_type?: string;
for_id?: string;
for_id_random?: string;
for_id_random?: string; // NO LONGER USE "_random"
type_code?: string;
event_id: string;
event_id_random: string;
event_id_random: string; // NO LONGER USE "_random"
event_session_id: string;
event_session_id_random: string;
event_session_id_random: string; // NO LONGER USE "_random"
event_abstract_id?: null | string;
event_abstract_id_random?: null | string;
event_abstract_id_random?: null | string; // NO LONGER USE "_random"
abstract_code?: null | string;
@@ -630,23 +630,23 @@ export interface Presenter {
id: string;
// id_random: string;
event_presenter_id: string;
event_presenter_id_random: string;
event_presenter_id_random: string; // NO LONGER USE "_random"
external_id?: string;
code?: string;
event_id: string;
event_id_random: string;
event_id_random: string; // NO LONGER USE "_random"
event_session_id: string;
event_session_id_random: string;
event_session_id_random: string; // NO LONGER USE "_random"
event_person_id?: null | string;
event_person_id_random?: null | string;
event_person_id_random?: null | string; // NO LONGER USE "_random"
event_presentation_id: string;
event_presentation_id_random: string;
event_presentation_id_random: string; // NO LONGER USE "_random"
person_id?: null | string;
person_id_random?: null | string;
person_profile_id?: null | string;
person_profile_id_random?: null | string; // The new table person_profile will be used soon...
person_id_random?: null | string; // NO LONGER USE "_random"
person_profile_id?: null | string; // The new table person_profile will be used soon...
person_profile_id_random?: null | string; // NO LONGER USE "_random"
pronouns?: null | string;
informal_name?: null | string;
@@ -731,24 +731,24 @@ export interface Session {
id: string;
// id_random: string;
event_session_id: string;
event_session_id_random: string;
event_session_id_random: string; // NO LONGER USE "_random"
external_id: null | string;
code: null | string;
for_type: string;
for_id: string;
for_id_random: string;
for_id_random: string; // NO LONGER USE "_random"
type_code?: string;
event_id: string;
event_id_random: string;
event_id_random: string; // NO LONGER USE "_random"
event_location_id?: null | string;
event_location_id_random?: null | string;
event_location_id_random?: null | string; // NO LONGER USE "_random"
poc_person_id?: null | string;
poc_person_id_random?: null | string;
poc_person_id_random?: null | string; // NO LONGER USE "_random"
poc_agree?: null | boolean; // General catchall for agreement or consent by the POC
poc_kv_json?: null | key_val; // Key value list of the POC by type (examples: 'advocate', 'chair', 'champion', 'moderator', 'organizer')
@@ -829,6 +829,7 @@ export class MySubClassedDexie extends Dexie {
constructor() {
super('ae_events_db');
this.version(6).stores({
// NO LONGER USE "_random"
event: `
id, event_id, event_id_random,
code,
@@ -841,7 +842,6 @@ export class MySubClassedDexie extends Dexie {
tmp_sort_1, tmp_sort_2,
enable, hide, priority, sort, group, notes, created_on, updated_on`,
// badge: '++id, full_name, email' // Primary key and indexed props
badge: `
event_badge_id_random, event_badge_id, id,
event_id, event_id_random,