Harden Licensed User dropdown logic and resolve data parsing errors
- Implemented dual-format parsing for 'license_li_json' (handles raw string and IDB objects). - Added fallback lookup by 'event_exhibit_id_random' for robust record resolution. - Fixed reactivity for licensee dropdown population. - Hardened default selection logic based on Aether access levels.
This commit is contained in:
@@ -57,8 +57,13 @@
|
||||
} else {
|
||||
// 2. Licensed User logic
|
||||
try {
|
||||
const licenses = JSON.parse($lq__exhibit_obj.license_li_json || '[]');
|
||||
const found = licenses.find((l: any) => l.email.toLowerCase() === email.toLowerCase().trim());
|
||||
// Determine raw JSON string
|
||||
const raw_json = $lq__exhibit_obj?.license_li_json;
|
||||
|
||||
// Parse if string, otherwise use empty array
|
||||
const licenses = typeof raw_json === 'string' ? JSON.parse(raw_json || '[]') : (Array.isArray(raw_json) ? raw_json : []);
|
||||
|
||||
const found = licenses.find((l: any) => l.email?.toLowerCase() === email.toLowerCase().trim());
|
||||
|
||||
if (found && found.passcode === user_passcode) {
|
||||
// SUCCESS
|
||||
|
||||
@@ -24,7 +24,16 @@
|
||||
let exhibit_obj: any = $state(null);
|
||||
|
||||
onMount(() => {
|
||||
const observable = liveQuery(() => db_events.exhibit.get(exhibit_id));
|
||||
const observable = liveQuery(async () => {
|
||||
if (!exhibit_id) return null;
|
||||
// 1. Try primary lookup
|
||||
let res = await db_events.exhibit.get(exhibit_id);
|
||||
// 2. Fallback to random ID index
|
||||
if (!res) {
|
||||
res = await db_events.exhibit.where('event_exhibit_id_random').equals(exhibit_id).first();
|
||||
}
|
||||
return res;
|
||||
});
|
||||
const subscription = observable.subscribe((value) => {
|
||||
exhibit_obj = value;
|
||||
});
|
||||
@@ -34,23 +43,30 @@
|
||||
// Reactive list derived from the exhibit state (Licensed Exhibit Leads Users)
|
||||
let licensee_li = $derived.by(() => {
|
||||
try {
|
||||
const licenses = JSON.parse(exhibit_obj?.license_li_json || '[]');
|
||||
return Array.isArray(licenses) ? licenses : [];
|
||||
const raw = exhibit_obj?.license_li_json;
|
||||
if (!raw) return [];
|
||||
|
||||
// If it's already an array, return it. If it's a string, parse it.
|
||||
if (Array.isArray(raw)) return raw;
|
||||
if (typeof raw === 'string') return JSON.parse(raw || '[]');
|
||||
|
||||
return [];
|
||||
} catch (e) {
|
||||
console.error('Failed to parse licensee_li_json', e);
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
// Default selection logic: Aether Admins go to "all", Licensees go to "my"
|
||||
$effect(() => {
|
||||
// Wait for object to load
|
||||
// Wait for object to load and check if initialized
|
||||
if (!exhibit_obj) return;
|
||||
|
||||
if ($events_loc.leads.tracking__qry__licensee_email === 'all' && !$ae_loc.administrator_access) {
|
||||
untrack(() => {
|
||||
untrack(() => {
|
||||
if ($events_loc.leads.tracking__qry__licensee_email === 'all' && !$ae_loc.administrator_access) {
|
||||
$events_loc.leads.tracking__qry__licensee_email = 'my';
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function handle_search_trigger() {
|
||||
|
||||
@@ -220,36 +220,38 @@
|
||||
</div>
|
||||
|
||||
<div class="card p-0 divide-y divide-surface-500/10 overflow-hidden shadow-md">
|
||||
<!-- Licenses -->
|
||||
<div class="p-0">
|
||||
<button
|
||||
class="w-full p-4 flex items-center justify-between hover:bg-surface-500/5 transition-colors group"
|
||||
onclick={() => show_license_mgmt = !show_license_mgmt}
|
||||
>
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="bg-primary-500/10 p-2 rounded-lg text-primary-500"><Users size="1.2em" /></div>
|
||||
<div class="text-left">
|
||||
<div class="font-bold text-sm">Staff Licenses</div>
|
||||
<div class="text-xs opacity-50">Manage assigned staff and codes</div>
|
||||
<!-- Licenses (Administrator Access Only for now) -->
|
||||
{#if $ae_loc.administrator_access}
|
||||
<div class="p-0">
|
||||
<button
|
||||
class="w-full p-4 flex items-center justify-between hover:bg-surface-500/5 transition-colors group"
|
||||
onclick={() => show_license_mgmt = !show_license_mgmt}
|
||||
>
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="bg-primary-500/10 p-2 rounded-lg text-primary-500"><Users size="1.2em" /></div>
|
||||
<div class="text-left">
|
||||
<div class="font-bold text-sm">Exhibit Staff Licenses</div>
|
||||
<div class="text-xs opacity-50">Manage assigned staff and codes (Admin Only)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#if show_license_mgmt}
|
||||
<ChevronDown size="1.2em" class="opacity-20" />
|
||||
{:else}
|
||||
<ChevronRight size="1.2em" class="opacity-20 group-hover:translate-x-1 transition-transform" />
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
{#if show_license_mgmt}
|
||||
<ChevronDown size="1.2em" class="opacity-20" />
|
||||
{:else}
|
||||
<ChevronRight size="1.2em" class="opacity-20 group-hover:translate-x-1 transition-transform" />
|
||||
<div class="p-4 bg-surface-500/5 border-t border-surface-500/10 animate-in fade-in slide-in-from-top-2">
|
||||
<Comp_exhibit_license_list
|
||||
{exhibit_id}
|
||||
license_li_json={$lq__exhibit_obj?.license_li_json ?? '[]'}
|
||||
license_max={$lq__exhibit_obj?.license_max}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
{#if show_license_mgmt}
|
||||
<div class="p-4 bg-surface-500/5 border-t border-surface-500/10 animate-in fade-in slide-in-from-top-2">
|
||||
<Comp_exhibit_license_list
|
||||
{exhibit_id}
|
||||
license_li_json={$lq__exhibit_obj?.license_li_json ?? '[]'}
|
||||
license_max={$lq__exhibit_obj?.license_max}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Custom Questions -->
|
||||
<div class="p-0">
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
import { liveQuery } from 'dexie';
|
||||
import { db_events } from '$lib/ae_events/db_events';
|
||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||
import { ae_api, ae_loc } from '$lib/stores/ae_stores';
|
||||
import Element_ae_crud_v2 from '$lib/elements/element_ae_crud_v2.svelte';
|
||||
import {
|
||||
User,
|
||||
Mail,
|
||||
@@ -20,7 +22,9 @@
|
||||
ShieldCheck,
|
||||
Star,
|
||||
LoaderCircle,
|
||||
ListTodo
|
||||
ListTodo,
|
||||
Edit,
|
||||
Eye
|
||||
} from 'lucide-svelte';
|
||||
|
||||
const exhibit_tracking_id = $derived(page.params.exhibit_tracking_id);
|
||||
@@ -32,6 +36,16 @@
|
||||
})
|
||||
);
|
||||
|
||||
let lq__exhibit_obj = $derived(
|
||||
liveQuery(async () => {
|
||||
const exhibit_id = page.params.exhibit_id;
|
||||
if (!exhibit_id) return null;
|
||||
return await db_events.exhibit.get(exhibit_id);
|
||||
})
|
||||
);
|
||||
|
||||
let is_edit_mode = $state(false);
|
||||
|
||||
// Helper to format date using Aether utility
|
||||
function format_date(date: any) {
|
||||
if (!date) return '';
|
||||
@@ -57,12 +71,29 @@
|
||||
<h1 class="text-lg font-bold">Lead Profile</h1>
|
||||
</div>
|
||||
|
||||
{#if $lq__lead_obj?.priority}
|
||||
<span class="badge variant-filled-warning font-bold flex items-center gap-1">
|
||||
<Star size="1em" fill="currentColor" />
|
||||
Priority
|
||||
</span>
|
||||
{/if}
|
||||
<div class="flex items-center gap-2">
|
||||
{#if $lq__lead_obj}
|
||||
<button
|
||||
class="btn btn-sm"
|
||||
class:variant-filled-primary={is_edit_mode}
|
||||
class:variant-ghost-surface={!is_edit_mode}
|
||||
onclick={() => is_edit_mode = !is_edit_mode}
|
||||
>
|
||||
{#if is_edit_mode}
|
||||
<Eye size="1.2em" class="mr-1" /> View
|
||||
{:else}
|
||||
<Edit size="1.2em" class="mr-1" /> Edit
|
||||
{/if}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if $lq__lead_obj?.priority}
|
||||
<span class="badge variant-filled-warning font-bold flex items-center gap-1">
|
||||
<Star size="1em" fill="currentColor" />
|
||||
Priority
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="w-full max-w-5xl p-4 sm:p-6 space-y-6">
|
||||
@@ -144,7 +175,18 @@
|
||||
<h3 class="text-lg font-bold uppercase tracking-wider">Exhibitor Notes</h3>
|
||||
</div>
|
||||
<div class="bg-surface-500/5 p-5 rounded-xl border border-surface-500/10 min-h-[120px]">
|
||||
{#if $lq__lead_obj.exhibitor_notes}
|
||||
{#if is_edit_mode}
|
||||
<Element_ae_crud_v2
|
||||
api_cfg={$ae_api}
|
||||
object_type="event_exhibit_tracking"
|
||||
object_id={exhibit_tracking_id}
|
||||
field_name="exhibitor_notes"
|
||||
field_type="textarea"
|
||||
current_field_value={$lq__lead_obj.exhibitor_notes}
|
||||
textarea_rows={6}
|
||||
display_block={true}
|
||||
/>
|
||||
{:else if $lq__lead_obj.exhibitor_notes}
|
||||
<p class="whitespace-pre-wrap leading-relaxed">{$lq__lead_obj.exhibitor_notes}</p>
|
||||
{:else}
|
||||
<div class="h-full flex items-center justify-center italic opacity-30 text-sm">
|
||||
@@ -172,6 +214,20 @@
|
||||
<span class="text-sm opacity-60">Captured By</span>
|
||||
<span class="font-mono text-[10px]">{$lq__lead_obj.external_person_id || 'Unknown'}</span>
|
||||
</div>
|
||||
|
||||
{#if is_edit_mode}
|
||||
<div class="flex justify-between items-center pt-2 border-t border-surface-500/10">
|
||||
<span class="text-xs opacity-60 font-bold">Priority Lead</span>
|
||||
<Element_ae_crud_v2
|
||||
api_cfg={$ae_api}
|
||||
object_type="event_exhibit_tracking"
|
||||
object_id={exhibit_tracking_id}
|
||||
field_name="priority"
|
||||
field_type="boolean"
|
||||
current_field_value={$lq__lead_obj.priority}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -188,11 +244,25 @@
|
||||
|
||||
<!-- Status Indicator -->
|
||||
<div class="card p-4 flex items-center gap-3 variant-soft shadow-sm">
|
||||
<ShieldCheck size="1.5em" class={$lq__lead_obj.enable ? 'text-success-500' : 'text-error-500'} />
|
||||
<div>
|
||||
<div class="font-bold">{$lq__lead_obj.enable ? 'Record Enabled' : 'Record Disabled'}</div>
|
||||
<div class="text-[10px] opacity-50 uppercase font-black">Visibility Status</div>
|
||||
</div>
|
||||
{#if is_edit_mode}
|
||||
<div class="flex-1 flex items-center justify-between">
|
||||
<div class="font-bold text-sm">Enabled</div>
|
||||
<Element_ae_crud_v2
|
||||
api_cfg={$ae_api}
|
||||
object_type="event_exhibit_tracking"
|
||||
object_id={exhibit_tracking_id}
|
||||
field_name="enable"
|
||||
field_type="boolean"
|
||||
current_field_value={$lq__lead_obj.enable}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<ShieldCheck size="1.5em" class={$lq__lead_obj.enable ? 'text-success-500' : 'text-error-500'} />
|
||||
<div>
|
||||
<div class="font-bold">{$lq__lead_obj.enable ? 'Record Enabled' : 'Record Disabled'}</div>
|
||||
<div class="text-[10px] opacity-50 uppercase font-black">Visibility Status</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user