Saving initial changes to the Leads v3 project files and directories.
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
|
||||
## Overview
|
||||
|
||||
This is a major work in progress. It still needs the overall directory and file structure planned, documented, and created.
|
||||
|
||||
This module provides a comprehensive solution for event exhibitors to capture and manage leads. It supports exhibitor login, badge scanning for lead capture, manual lead entry, lead qualification (ranking/priority), and data export functionalities. It integrates with the Aether API for data synchronization and utilizes local caching for performance and offline capabilities.
|
||||
|
||||
## Key Features
|
||||
@@ -22,7 +24,7 @@ The core data structures managed by this module are `Exhibit` and `Exhibit_track
|
||||
|
||||
Represents an exhibitor's presence at an event. It contains configuration for lead retrieval, such as:
|
||||
|
||||
- `event_exhibit_id_random`: Unique identifier for the exhibit (primary key).
|
||||
- `event_exhibit_id`: Unique identifier for the exhibit (primary key).
|
||||
- `code`, `name`, `description`: Basic information about the exhibit.
|
||||
- `staff_passcode`: For staff login.
|
||||
- `leads_api_access`: Boolean indicating if lead retrieval API access is enabled.
|
||||
@@ -33,9 +35,9 @@ Represents an exhibitor's presence at an event. It contains configuration for le
|
||||
|
||||
Represents a single lead captured by an exhibitor. It links an exhibitor to an attendee (badge) and stores details about the interaction:
|
||||
|
||||
- `event_exhibit_tracking_id_random`: Unique identifier for the captured lead (primary key).
|
||||
- `event_exhibit_id_random`: Links to the `Exhibit` that captured the lead.
|
||||
- `event_badge_id_random`: Links to the attendee's `Badge` information.
|
||||
- `event_exhibit_tracking_id`: Unique identifier for the captured lead (primary key).
|
||||
- `event_exhibit_id`: Links to the `Exhibit` that captured the lead.
|
||||
- `event_badge_id`: Links to the attendee's `Badge` information.
|
||||
- `exhibitor_notes`: Notes added by the exhibitor about the lead.
|
||||
- `responses_json`: Responses to custom questions.
|
||||
- Contains duplicated badge information for convenience (e.g., `event_badge_full_name`, `event_badge_email`, `event_badge_professional_title`, `event_badge_affiliations`) to denormalize data for faster display.
|
||||
@@ -48,7 +50,7 @@ Represents a single lead captured by an exhibitor. It links an exhibitor to an a
|
||||
- `+page.svelte`: Renders the list of exhibits.
|
||||
- `+page.ts`: Loads the data for available exhibits using `events_func.handle_load_ae_obj_li__exhibit`.
|
||||
- `+layout.svelte`/`+layout.ts`: Provides a common layout and data for the module, including a submenu.
|
||||
- `/events/[event_id]/(leads)/exhibit/[slug]`: Dynamic route for managing leads for a specific exhibitor within an event. The `[slug]` corresponds to `event_exhibit_id_random`.
|
||||
- `/events/[event_id]/(leads)/exhibit/[slug]`: Dynamic route for managing leads for a specific exhibitor within an event. The `[slug]` corresponds to `event_exhibit_id`.
|
||||
- `+page.svelte`: The primary interface for an exhibitor, orchestrating lead capture and management components.
|
||||
- `+page.ts`: Loads specific `Exhibit` data and associated `Exhibit_tracking` (leads) using `events_func.handle_load_ae_obj_id__exhibit` and `events_func.handle_load_ae_obj_li__exhibit_tracking`.
|
||||
|
||||
@@ -63,7 +65,7 @@ Represents a single lead captured by an exhibitor. It links an exhibitor to an a
|
||||
## Technical Implementation
|
||||
|
||||
- **Svelte v5 with Runes:** The module adheres to Svelte v5's reactivity model, utilizing `$state` and `$derived` for reactive state management.
|
||||
- **ID Convention (`id_random`):** All frontend operations, including routing and data fetching, consistently use the string-based `event_exhibit_id_random` and `event_exhibit_tracking_id_random` as primary identifiers, rather than numeric `id` values.
|
||||
- **ID Convention (`id`):** All frontend operations, including routing and data fetching, consistently use the string-based `event_exhibit_id` and `event_exhibit_tracking_id` as primary identifiers, rather than numeric `id` values.
|
||||
- **API Interaction:** Data is fetched and synchronized with the backend FastAPI application through functions exposed in `src/lib/ae_events_functions.ts`.
|
||||
- **Local Database (Dexie.js):** Data for `Exhibit` and `Exhibit_tracking` is cached in the browser's IndexedDB using Dexie.js, defined in `src/lib/ae_events/db_events.ts`. This ensures data persistence and fast retrieval, especially for offline scenarios.
|
||||
- **Styling:** The UI is primarily styled using Tailwind CSS, having migrated from Skeleton UI to resolve previous rendering issues.
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
// SCENARIO 2: Fallback broad search
|
||||
if (event_id && !$events_loc.leads.qry__search_text) {
|
||||
return await db_events.exhibit
|
||||
.where('event_id_random')
|
||||
.where('event_id')
|
||||
.equals(event_id)
|
||||
.sortBy('name');
|
||||
}
|
||||
@@ -98,7 +98,7 @@
|
||||
if (!remote_first) {
|
||||
try {
|
||||
let local_results = await db_events.exhibit
|
||||
.where('event_id_random')
|
||||
.where('event_id')
|
||||
.equals(event_id)
|
||||
.filter((exhibit) => {
|
||||
if (qry_str) {
|
||||
@@ -135,7 +135,7 @@
|
||||
});
|
||||
|
||||
const local_ids = local_results
|
||||
.map((e) => String(e.id || e.event_exhibit_id_random))
|
||||
.map((e) => String(e.event_exhibit_id))
|
||||
.filter(Boolean);
|
||||
if (current_search_id === last_search_id) {
|
||||
untrack(() => {
|
||||
@@ -180,7 +180,7 @@
|
||||
|
||||
if (current_search_id === last_search_id) {
|
||||
const api_ids = results
|
||||
.map((e: any) => String(e.id || e.event_exhibit_id_random))
|
||||
.map((e: any) => String(e.event_exhibit_id))
|
||||
.filter(Boolean);
|
||||
untrack(() => {
|
||||
exhibit_id_li = api_ids;
|
||||
@@ -219,7 +219,7 @@
|
||||
{#each $lq__event_exhibit_obj_li as exhibit_obj}
|
||||
<a
|
||||
href="/events/{page.params
|
||||
.event_id}/leads/exhibit/{exhibit_obj.id_random}"
|
||||
.event_id}/leads/exhibit/{exhibit_obj.event_exhibit_id}"
|
||||
class="card card-hover p-4 flex flex-col items-center justify-center text-center space-y-2 preset-tonal"
|
||||
>
|
||||
<Store size="2em" />
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<script lang="ts">
|
||||
// Page for viewing/editing a single lead
|
||||
</script>
|
||||
|
||||
<h1 class="h1">Lead Details</h1>
|
||||
|
||||
<p>This page will show the details for a single lead.</p>
|
||||
@@ -47,7 +47,7 @@
|
||||
|
||||
if (exhibit_id && !$events_loc.leads.tracking__qry__search_text) {
|
||||
return await db_events.exhibit_tracking
|
||||
.where('event_exhibit_id_random')
|
||||
.where('event_exhibit_id')
|
||||
.equals(exhibit_id)
|
||||
.reverse()
|
||||
.sortBy('created_on');
|
||||
@@ -109,7 +109,7 @@
|
||||
if (!remote_first) {
|
||||
try {
|
||||
let local_results = await db_events.exhibit_tracking
|
||||
.where('event_exhibit_id_random')
|
||||
.where('event_exhibit_id')
|
||||
.equals(exhibit_id)
|
||||
.filter((tracking) => {
|
||||
if (qry_str) {
|
||||
@@ -163,7 +163,7 @@
|
||||
|
||||
const local_ids = local_results
|
||||
.map((e) =>
|
||||
String(e.id || e.event_exhibit_tracking_id_random)
|
||||
String(e.event_exhibit_tracking_id)
|
||||
)
|
||||
.filter(Boolean);
|
||||
if (current_search_id === last_search_id) {
|
||||
@@ -207,7 +207,7 @@
|
||||
if (current_search_id === last_search_id) {
|
||||
const api_ids = results
|
||||
.map((e: any) =>
|
||||
String(e.id || e.event_exhibit_tracking_id_random)
|
||||
String(e.event_exhibit_tracking_id)
|
||||
)
|
||||
.filter(Boolean);
|
||||
untrack(() => {
|
||||
|
||||
@@ -45,17 +45,17 @@
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
{#each $lq__event_exhibit_tracking_obj_li as tracking_obj}
|
||||
{#each $lq__event_exhibit_tracking_obj_li as event_tracking_obj}
|
||||
<a
|
||||
href={`/events/${page.params.event_id}/leads/exhibit/${tracking_obj.event_exhibit_id_random}/lead/${tracking_obj.id_random || tracking_obj.event_exhibit_tracking_id_random}`}
|
||||
href={`/events/${page.params.event_id}/leads/exhibit/${event_tracking_obj.event_exhibit_id}/lead/${event_tracking_obj.event_exhibit_tracking_id}`}
|
||||
class="card card-hover p-4 variant-filled-surface border-l-4 border-primary-500 flex flex-col md:flex-row gap-4 items-start md:items-center"
|
||||
>
|
||||
<div class="flex-grow space-y-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<User size="1.25em" class="text-primary-500" />
|
||||
<span class="text-xl font-bold">
|
||||
{tracking_obj.event_badge_full_name ||
|
||||
tracking_obj.event_badge_full_name_override ||
|
||||
{event_tracking_obj.event_badge_full_name ||
|
||||
event_tracking_obj.event_badge_full_name_override ||
|
||||
'Unknown Attendee'}
|
||||
</span>
|
||||
</div>
|
||||
@@ -63,31 +63,31 @@
|
||||
<div
|
||||
class="flex flex-wrap gap-x-4 gap-y-1 text-sm opacity-70"
|
||||
>
|
||||
{#if tracking_obj.event_badge_email}
|
||||
{#if event_tracking_obj.event_badge_email}
|
||||
<div class="flex items-center gap-1">
|
||||
<Mail size="1em" />
|
||||
{tracking_obj.event_badge_email}
|
||||
{event_tracking_obj.event_badge_email}
|
||||
</div>
|
||||
{/if}
|
||||
{#if tracking_obj.event_badge_affiliations}
|
||||
{#if event_tracking_obj.event_badge_affiliations}
|
||||
<div class="flex items-center gap-1">
|
||||
<MapPin size="1em" />
|
||||
{tracking_obj.event_badge_affiliations}
|
||||
{event_tracking_obj.event_badge_affiliations}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex items-center gap-1">
|
||||
<Clock size="1em" />
|
||||
{format_date(tracking_obj.created_on)}
|
||||
{format_date(event_tracking_obj.created_on)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if tracking_obj.exhibitor_notes}
|
||||
{#if event_tracking_obj.exhibitor_notes}
|
||||
<div
|
||||
class="mt-2 p-2 bg-surface-100-900 rounded text-sm italic border-l-2 border-surface-300-700"
|
||||
>
|
||||
<FileText size="1em" class="inline mr-1" />
|
||||
{ae_util.shorten_string({
|
||||
string: tracking_obj.exhibitor_notes,
|
||||
string: event_tracking_obj.exhibitor_notes,
|
||||
max_length: 100
|
||||
})}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
// Page for viewing/editing a single lead
|
||||
</script>
|
||||
|
||||
<h1 class="h1">Lead Details</h1>
|
||||
|
||||
<p>This page will show the details for a single lead. A Lead is actually in the event_exhibit_tracking table in the MariaDB or exhibit_tracking table in the Indexed DB ae_events_db.</p>
|
||||
@@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
// Page for viewing/editing a single lead
|
||||
</script>
|
||||
|
||||
<h1 class="h1">Lead Details</h1>
|
||||
|
||||
<p>This page will show the details for a single lead. A Lead is actually in the event_exhibit_tracking table in the MariaDB or exhibit_tracking table in the Indexed DB ae_events_db.</p>
|
||||
Reference in New Issue
Block a user