Documentation updates with IDAA ideas.
This commit is contained in:
515
documentation/AE__UI_UX_future_ideas.md
Normal file
515
documentation/AE__UI_UX_future_ideas.md
Normal file
@@ -0,0 +1,515 @@
|
||||
# Aether UI/UX — Future Ideas
|
||||
|
||||
> Collection of concrete UX improvements for the Aether frontend. Each entry includes
|
||||
> the rationale, current behavior, proposed change, and implementation notes.
|
||||
> **Date:** 2026-05-17
|
||||
|
||||
---
|
||||
|
||||
## IDAA Recovery Meetings
|
||||
|
||||
### 1. Guided empty state with active filters
|
||||
|
||||
**Current behavior:** When filters return 0 results, the page shows:
|
||||
"No recovery meetings found matching your criteria."
|
||||
The member has no indication whether this is a bug, genuinely no data, or just
|
||||
overly narrow filters.
|
||||
|
||||
**Proposed change:** When filters are active AND the result count is 0, show a
|
||||
helpful prompt instead of the bare message:
|
||||
|
||||
```
|
||||
No meetings found for these filters.
|
||||
Try broadening your search or [Clear all filters →]
|
||||
```
|
||||
|
||||
**Implementation notes:**
|
||||
- Add a `has_active_filters` derived in `+page.svelte` that checks whether any of
|
||||
`qry__physical`, `qry__virtual`, `qry__type`, or `qry__fulltext_str` is set.
|
||||
- In the template's `{:else}` block (line ~443), branch on `has_active_filters`:
|
||||
- `true` → show the guided message + "Clear Filters" button
|
||||
- `false` → show the existing escape-hatch flow (timed "Refresh Meeting Cache" button
|
||||
after 8 seconds, since zero unfiltered results always indicates a problem)
|
||||
- The "Clear Filters" button resets all four filter fields to `null`/`''` and bumps
|
||||
`search_version` to trigger a fresh unfiltered search.
|
||||
- Distinct from the `error` state — this is a successful search (`qry__status === 'done'`)
|
||||
with an empty result set.
|
||||
|
||||
---
|
||||
|
||||
### 2. Quick-filter chips below the search bar
|
||||
|
||||
**Current behavior:** Members toggle filters via small checkboxes (Virtual, In-person)
|
||||
and radio buttons (All, IDAA, Caduceus, Family Recovery). These require precise
|
||||
mouse/tap targeting and scanning several lines of filter UI to discover and use.
|
||||
|
||||
**Proposed change:** Add a row of preset chip buttons directly below the search input:
|
||||
|
||||
```
|
||||
[🖥 Virtual] [🏠 In-Person] [🩺 IDAA] [Caduceus] [Family Recovery] [All Types]
|
||||
```
|
||||
|
||||
- Each chip toggles the corresponding filter (`qry__virtual`, `qry__physical`, `qry__type`)
|
||||
and triggers an immediate search.
|
||||
- Selected chips get a filled/pressed style; unselected chips are outlined.
|
||||
- "All Types" is the default selected state (no type filter). Clicking another type
|
||||
chip deselects "All Types" (radio behavior for the type dimension). Virtual and
|
||||
In-person are independent toggles (checkbox behavior — can select both).
|
||||
- The existing checkboxes/radio buttons remain as the underlying state storage
|
||||
(`$idaa_loc.recovery_meetings.*`). The chips are a convenience layer — they write
|
||||
to the same store fields and call `handle_search_trigger()`.
|
||||
|
||||
**Implementation notes:**
|
||||
- Place in `ae_idaa_comp__event_obj_qry.svelte` between the search input row and the
|
||||
current filter rows.
|
||||
- Optionally hide the existing checkbox/radio filter rows when the chips are present
|
||||
(or keep both — the checkboxes serve as accessible form controls; the chips are
|
||||
the primary visual interaction).
|
||||
- On mobile, chips wrap to a second row naturally with `flex-wrap`.
|
||||
|
||||
---
|
||||
|
||||
### 3. Language: "Searching..." vs "Loading..."
|
||||
|
||||
**Current behavior:** The loading state always shows the same message:
|
||||
|
||||
```
|
||||
🔄 Searching...
|
||||
```
|
||||
|
||||
This appears on initial page load (when the user hasn't typed anything) and after
|
||||
the user clicks Search or toggles a filter. The word "Searching" implies the user
|
||||
initiated a search, which is misleading on initial page load — it's a cold cache
|
||||
load, not an active search.
|
||||
|
||||
**Proposed change:** Distinguish the two loading contexts:
|
||||
|
||||
| Context | Message |
|
||||
|---------|---------|
|
||||
| Initial page load (no filters, no search text) | "Loading meetings..." |
|
||||
| User clicked Search or toggled a filter | "Searching..." (keep current) |
|
||||
|
||||
**Implementation notes:**
|
||||
- In `+page.svelte` template around line 422, check whether `qry__fulltext_str` is
|
||||
empty AND no filter checkboxes/radios are active. If so, show "Loading meetings...";
|
||||
otherwise show "Searching...".
|
||||
- This is purely a label change — no logic changes needed. The condition can be the
|
||||
same `has_active_filters` derived from item #1.
|
||||
- Also update the list component's standalone loading state in
|
||||
`ae_idaa_comp__event_obj_li.svelte` line 556-558 to use the same distinction.
|
||||
|
||||
---
|
||||
|
||||
### 4. Filter row collapsing on mobile
|
||||
|
||||
**Current behavior:** The query bar has three filter rows (Location checkboxes,
|
||||
Type radios, Max/Sort selects) plus the search input row and the action button row.
|
||||
Combined, this takes roughly 200px of vertical space. On mobile — especially inside
|
||||
the Novi iframe on a phone — meeting cards are pushed below the fold.
|
||||
|
||||
**Proposed change:** On viewports below `md` (768px), collapse the Location and Type
|
||||
filter rows behind a "Filters ▾" toggle. The Max Results and Sort selects stay visible
|
||||
since they're used frequently. The action buttons (Show Hidden, Create Meeting, Export)
|
||||
move inside the collapsed panel or stay visible based on available width.
|
||||
|
||||
```
|
||||
[Search input............................] [Search]
|
||||
|
||||
[Filters ▾] [Max: 150 ▾] [Sort: Last Updated ▾]
|
||||
```
|
||||
|
||||
Clicking "Filters ▾" expands the panel with Location checkboxes and Type radios.
|
||||
|
||||
**Implementation notes:**
|
||||
- Use a `$state` boolean `show_filters` (session-only, resets on page load).
|
||||
- Wrap the filter rows in a `{#if show_filters}` block.
|
||||
- Persist in `$idaa_sess.recovery_meetings.show_filters_expanded` if you want the
|
||||
state to survive navigation within the module (same tab session).
|
||||
- The Tailwind `md:` breakpoint works for the collapse trigger: `class:hidden={!show_filters}`
|
||||
combined with `class:md:block` to always show on desktop.
|
||||
- Test inside the Novi iframe — Bootstrap v3 may add its own `hidden` behavior on
|
||||
`md` breakpoints that conflicts with Tailwind's.
|
||||
|
||||
---
|
||||
|
||||
### 5. Human-readable schedule line on cards
|
||||
|
||||
**Current behavior:** The meeting card displays weekdays as a flat, dense span list:
|
||||
|
||||
```
|
||||
Sunday Monday Wednesday Friday
|
||||
```
|
||||
|
||||
The timezone is shown separately as `(America/Chicago)`, and the start time is in
|
||||
a compact `7:00 PM` format. These three pieces of information are visually separated
|
||||
and require the member to mentally assemble the schedule.
|
||||
|
||||
**Proposed change:** Render a computed one-liner that combines them:
|
||||
|
||||
```
|
||||
🕐 Mondays, Wednesdays, Fridays at 7:00 PM CT
|
||||
```
|
||||
|
||||
- Weekday names are built from the `weekday_*` booleans on the event object.
|
||||
- "Mondays, Wednesdays" uses the range-joining convention (comma-separated, "and"
|
||||
before the last item for two days; "Mondays through Fridays" for consecutive spans
|
||||
of 3+ days).
|
||||
- Timezone abbreviation is extracted from `timezone` (e.g., `America/Chicago` → `CT`,
|
||||
`America/New_York` → `ET`). A small lookup table handles the common ones; fall back
|
||||
to the raw timezone string for unknown values.
|
||||
- If `timezone` is null/missing, fall back to the current flat display — don't
|
||||
silently drop information.
|
||||
- Today's meetings could optionally get a subtle "Today" badge or highlight (extra
|
||||
polish, not required for the initial version).
|
||||
|
||||
**Implementation notes:**
|
||||
- Add a `$derived` in `ae_idaa_comp__event_obj_li.svelte` that computes the schedule
|
||||
string from the event object's `weekday_*` fields, `recurring_start_time`, and
|
||||
`timezone`.
|
||||
- Helper function in `ae_util` for the weekday list → natural language string
|
||||
(e.g., `['Monday', 'Wednesday', 'Friday']` → `"Mondays, Wednesdays, and Fridays"`).
|
||||
- Helper function or small lookup for timezone → abbreviation.
|
||||
- Fall back to the current flat display when `timezone` is missing to avoid losing
|
||||
information.
|
||||
|
||||
---
|
||||
|
||||
### 6. Show result count during search, not just after
|
||||
|
||||
**Current behavior:** The result count badge ("Results: 25") only appears inside the
|
||||
list wrapper component (`ae_idaa_comp__event_obj_li.svelte` line 98-108) when the
|
||||
visible result list is non-empty. During loading, the user sees only a spinner with
|
||||
no indication of how many meetings exist or what the search is operating on.
|
||||
|
||||
**Proposed change:** Show a result count line at the page level (in `+page.svelte`)
|
||||
that is always visible once the first search completes:
|
||||
|
||||
```
|
||||
25 of 140 meetings ← after search completes, with result count + total
|
||||
Searching 140 meetings... ← during initial load (cold cache, no prior result)
|
||||
0 results for these filters ← empty but filters are active (ties into item #1)
|
||||
```
|
||||
|
||||
**Implementation notes:**
|
||||
- Lift the count display from the list component to `+page.svelte`, placed between
|
||||
the query bar (`Comp__event_obj_qry`) and the list wrapper.
|
||||
- The total count is available from the IDB fast path: after the initial unfiltered
|
||||
search populates `db_events.event`, the total is `db_events.event.count()` (or
|
||||
the count of records matching `account_id`).
|
||||
- The visible count is `event_id_li.length` after search completes.
|
||||
- Store the last known total in a `$state` variable so it persists across searches
|
||||
(the total changes infrequently). Refresh the total on the first search after
|
||||
page load.
|
||||
- Format: `{visible} of {total} meetings` when filters/search are active;
|
||||
`{visible} meetings` when browsing all (no active filters).
|
||||
- During loading with no prior results: show "Loading meetings..." (from item #3)
|
||||
rather than a count.
|
||||
|
||||
---
|
||||
|
||||
### 7. "Live Now" and "Starting Soon" indicators
|
||||
|
||||
**Current behavior:** Meetings are shown in a static list. To find one happening
|
||||
now, a member must scan the "When" line of multiple cards and compare the time
|
||||
to their own clock.
|
||||
|
||||
**Proposed change:** Add a high-visibility badge or pulse indicator for meetings
|
||||
that are currently in progress or starting in the next 15 minutes.
|
||||
|
||||
- "LIVE NOW" (Green pulse badge) → if `current_time` is within `[start, start + 1 hour]`.
|
||||
- "STARTING SOON" (Yellow badge) → if `current_time` is within `[start - 15 min, start]`.
|
||||
- On the card, move the "Join Zoom" or "Join Jitsi" button to the very top or
|
||||
make it significantly larger when the meeting is live.
|
||||
|
||||
**Implementation notes:**
|
||||
- Add a `$derived` state `is_live` and `is_starting_soon` to the card component.
|
||||
- Requires calculating "current time in meeting's timezone" using `Temporal` or
|
||||
a date helper.
|
||||
- Ensure the pulse animation is subtle and respects `prefers-reduced-motion`.
|
||||
|
||||
---
|
||||
|
||||
### 8. Local Timezone Conversion
|
||||
|
||||
**Current behavior:** Meetings show their native timezone (e.g., "7:00 PM America/Chicago").
|
||||
The "Your TZ" line is currently a placeholder and doesn't perform conversion.
|
||||
|
||||
**Proposed change:** Automatically detect the member's browser timezone and
|
||||
show the converted time if it differs from the meeting's native timezone.
|
||||
|
||||
```
|
||||
🕐 7:00 PM CT (8:00 PM ET your time)
|
||||
```
|
||||
|
||||
**Implementation notes:**
|
||||
- Use `Intl.DateTimeFormat().resolvedOptions().timeZone` to get the user's TZ.
|
||||
- If `user_tz !== meeting_tz`, perform the conversion.
|
||||
- If the conversion results in a different day (e.g., late night ET vs early morning Europe),
|
||||
prefix with "Tomorrow at..." or "Yesterday at...".
|
||||
|
||||
---
|
||||
|
||||
### 9. Favorites / "My Meetings"
|
||||
|
||||
**Current behavior:** Members scan the full list every time they want to find
|
||||
their regular weekly meeting.
|
||||
|
||||
**Proposed change:** Add a "Star" icon to every meeting card.
|
||||
- Starring a meeting adds it to a `favorites` list stored in `$idaa_loc`.
|
||||
- Favorited meetings are pinned to the top of the list by default, regardless
|
||||
of other sort orders.
|
||||
- Add a "Favorites" filter toggle in the query bar to show *only* starred meetings.
|
||||
|
||||
**Implementation notes:**
|
||||
- Store as an array of `event_id` strings in `$idaa_loc.recovery_meetings.favorites`.
|
||||
- Update the `visible_event_obj_li` derived in `+page.svelte` to prioritize
|
||||
these IDs in the sort logic.
|
||||
|
||||
---
|
||||
|
||||
### 10. "Add to Calendar" (iCal / Google)
|
||||
|
||||
**Current behavior:** Members must manually create calendar events if they
|
||||
want reminders for recurring meetings.
|
||||
|
||||
**Proposed change:** Add an "Add to Calendar" dropdown button on the meeting
|
||||
detail page (and optionally the card).
|
||||
- Generates a `.ics` file or a Google Calendar URL with the recurring rule
|
||||
(e.g., "Every Wednesday at 7pm").
|
||||
- Includes the meeting name, description, and the Zoom/Jitsi link in the location field.
|
||||
|
||||
**Implementation notes:**
|
||||
- Use a helper to generate RFC 5545 `RRULE` strings from the `weekday_*` and
|
||||
`recurring_pattern` fields.
|
||||
- Include the `attend_url` in the calendar event description for one-tap join
|
||||
from phone lock screens.
|
||||
|
||||
---
|
||||
|
||||
### 11. Geographic Search for In-Person Meetings
|
||||
|
||||
**Current behavior:** The only location filter is a binary "Physical" checkbox.
|
||||
Members must use fulltext search (e.g., "Chicago") to find local meetings.
|
||||
|
||||
**Proposed change:** Add a "City/State" search input or a map view.
|
||||
- When `Physical` is checked, show a "Near [City, State]" input.
|
||||
- Map view (optional): A toggle to switch from "List" to "Map" view, plotting
|
||||
meetings on a map using their `location_address_json` coordinates.
|
||||
|
||||
**Implementation notes:**
|
||||
- The `event` table has `location_address_json` which often contains city/state.
|
||||
- Simple implementation: a city-picker dropdown populated from the distinct
|
||||
`location_address_json->>'$.city'` values in the current result set.
|
||||
|
||||
---
|
||||
|
||||
### 12. Prominent "Join" button for virtual meetings
|
||||
|
||||
**Current behavior:** On each meeting card, the Zoom or Jitsi join link is rendered
|
||||
as a small `btn-sm` inside the content area, visually equivalent to other label/value
|
||||
rows. The "Meeting Details" button at the top of the card is rendered *larger* than the
|
||||
join link — meaning the primary action for a member who wants to attend a meeting right
|
||||
now is visually subordinate to a navigation link.
|
||||
|
||||
The copy-to-clipboard button for the join link is gated behind `$ae_loc.manager_access`,
|
||||
so regular members have no easy way to share the link with a sponsee.
|
||||
|
||||
**Proposed change:** For virtual meetings, elevate the join button to a full-width
|
||||
prominent CTA inside the card header area, directly below the meeting name and badges:
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ 📅 Monday Night IDAA Discussion 🖥 Virtual │
|
||||
│ │
|
||||
│ [ 🎥 Join Zoom Meeting ] ← full-width │
|
||||
│ [ 📋 Meeting Details ] │
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- The Join button uses `preset-filled` (solid) styling; Meeting Details uses
|
||||
`preset-outlined` (hollow). This makes the action hierarchy visually clear.
|
||||
- On mobile especially, a full-width join button is much easier to tap than a
|
||||
small inline link buried inside label rows.
|
||||
- Replace manager-only clipboard with a Web Share API button for all members on
|
||||
virtual meetings: `navigator.share({ title, url })` on mobile triggers the native
|
||||
OS share sheet. Fall back to clipboard copy on desktop (where `navigator.share`
|
||||
is often unavailable). This lets members easily send a meeting link to a sponsee.
|
||||
- Passcode, if present, moves to the Meeting Details page — exposing it in the
|
||||
list view is unnecessary and clutters the card.
|
||||
|
||||
**Implementation notes:**
|
||||
- In `ae_idaa_comp__event_obj_li.svelte`, move the Zoom/Jitsi attend block from
|
||||
the `event__content` section up into the `ae_options` div (line ~200), rendered
|
||||
only when `idaa_event_obj?.virtual` is true and an attend URL exists.
|
||||
- Keep the existing small label/link in `event__content` as a fallback for when
|
||||
the prominent button is not shown (non-virtual meetings may still have a URL).
|
||||
- Web Share: `{#if navigator?.share}` guard; wrap in a try/catch (user cancels
|
||||
the share sheet throws `AbortError`).
|
||||
- The Live Now / Starting Soon badges from item #7, when implemented, should also
|
||||
interact with this button — e.g., a pulsing green border when the meeting is live.
|
||||
|
||||
---
|
||||
|
||||
### 13. "Today's Meetings" section at the top of the list
|
||||
|
||||
**Current behavior:** The meeting list shows all results sorted by the selected sort
|
||||
order. To find a meeting happening today, a member must scan every card's "When" line
|
||||
and mentally compare it to the current day and time. There is no at-a-glance view
|
||||
of what's available right now or later today.
|
||||
|
||||
This is distinct from the "Live Now" badge in item #7 (which marks individual cards
|
||||
after they're already displayed in a long list). This is a dedicated section pinned
|
||||
above the main results.
|
||||
|
||||
**Proposed change:** Add a collapsible "Today" section at the very top of the results
|
||||
list that shows only meetings scheduled on the current day of the week, sorted by
|
||||
start time:
|
||||
|
||||
```
|
||||
▼ Today — Sunday, May 17 3 meetings
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Sunday Serenity Discussion 7:00 AM ET 🔴Live │
|
||||
│ IDAA Sunday Big Book 2:00 PM CT │
|
||||
│ Sunday Night IDAA 8:00 PM ET │
|
||||
└─────────────────────────────────────────────────┘
|
||||
|
||||
All Meetings (140)
|
||||
...
|
||||
```
|
||||
|
||||
- The section is collapsed by default if it's empty (no meetings today).
|
||||
- Meetings in the "Today" section also appear in the main list below — this is a
|
||||
quick-access shortcut, not a filter.
|
||||
- Past meetings (start time has already passed today) are dimmed but still shown;
|
||||
a meeting may still be in progress.
|
||||
|
||||
**Implementation notes:**
|
||||
- Compute current day-of-week in the browser: `new Date().getDay()` → 0=Sunday, 6=Saturday.
|
||||
Map to the `weekday_*` boolean fields on the event object (e.g., day 0 → `weekday_sunday`).
|
||||
- Filter `visible_event_obj_li` (already computed in the list wrapper) for items where
|
||||
the matching `weekday_*` field is truthy. Sort by `recurring_start_time`.
|
||||
- Store collapse state in `$idaa_sess.recovery_meetings.today_section_expanded` (session
|
||||
only; default true so it's visible on first load).
|
||||
- Renders correctly in the Novi iframe since it's just a filtered sub-list of existing
|
||||
data — no additional API calls needed.
|
||||
- If item #7 (Live Now) is implemented, the "Today" section naturally becomes the host
|
||||
for the live/starting-soon badges, since that's where members will look first.
|
||||
|
||||
---
|
||||
|
||||
### 14. Data freshness indicator *(low priority — deprioritized)*
|
||||
|
||||
**Note:** Meeting records change infrequently — once established, a meeting's schedule,
|
||||
type, and contact info are typically stable for months or years. The occasional update is
|
||||
usually minor wording. Surfacing a freshness indicator for data this static would add
|
||||
visual noise with very little member benefit. The existing error state (item #4 in the
|
||||
bug fix, distinct "Unable to load meetings") and the escape-hatch cache-reset button
|
||||
already handle the reliability-concern case. This idea is recorded for completeness but
|
||||
is not recommended for implementation.
|
||||
|
||||
---
|
||||
|
||||
### 15. "Confirmed meetings only" default filter
|
||||
|
||||
**Current behavior:** All meetings are shown by default, including those with
|
||||
`status === 'unknown'` (not yet confirmed by IDAA Central Office). The "Not Confirmed
|
||||
by IDAA" warning badge on those cards is alarming-looking but does nothing to prevent
|
||||
unverified meetings from dominating the list.
|
||||
|
||||
There is currently no filter to hide unconfirmed meetings. Members have no choice but
|
||||
to see them all.
|
||||
|
||||
**Business rationale:** IDAA staff want meeting chairs to submit their meeting info for
|
||||
verification. Defaulting to "confirmed only" creates a natural incentive: unconfirmed
|
||||
meetings disappear from the default member view, which encourages chairs to contact
|
||||
IDAA staff and get their meeting verified. It also gives members a cleaner, higher-
|
||||
confidence list by default — they're not seeing meetings that may be outdated or
|
||||
inactive.
|
||||
|
||||
**Proposed change:** Add a `qry__confirmed` filter field with three states:
|
||||
|
||||
| Value | Behavior |
|
||||
|-------|----------|
|
||||
| `'confirmed_only'` (default) | Hide meetings where `status === 'unknown'` |
|
||||
| `'all'` | Show all meetings, confirmed and unconfirmed |
|
||||
| `'unconfirmed_only'` | Show only unconfirmed (admin/staff use) |
|
||||
|
||||
The filter UI shows a simple toggle in the query bar:
|
||||
```
|
||||
[✓ Confirmed Only] ← default, shown as active chip or checkbox
|
||||
```
|
||||
|
||||
When the member switches to "All", the unconfirmed meetings appear with their warning
|
||||
badge (see item #15 for making that badge useful on mobile).
|
||||
|
||||
For trusted/admin users, the default should remain `'all'` so staff can see the full
|
||||
picture without having to change a setting.
|
||||
|
||||
**Implementation notes:**
|
||||
- Add `qry__confirmed: 'confirmed_only' | 'all' | 'unconfirmed_only'` to
|
||||
`$idaa_loc.recovery_meetings` defaults, defaulting to `'confirmed_only'`.
|
||||
Trusted users default to `'all'`.
|
||||
- Apply the filter in both the IDB fast path (`db_events.event.filter()`) and the
|
||||
API revalidation secondary filter in `handle_search_refresh`. IDB: check
|
||||
`ev.status !== 'unknown'` when `qry__confirmed === 'confirmed_only'`. API: same
|
||||
post-fetch client-side filter.
|
||||
- Pass `qry__confirmed` to `events_func.search__event` if the API supports a
|
||||
`status` filter param; otherwise handle it client-side only.
|
||||
- The `no_results_no_filters` derived (used for the escape-hatch button) should NOT
|
||||
treat `qry__confirmed === 'confirmed_only'` as an active filter — it's the default
|
||||
state, not a narrowing choice the member made. Only count it as a filter if the
|
||||
member explicitly switched it to `'all'` or `'unconfirmed_only'`.
|
||||
- Add a count badge to the toggle: "Confirmed Only (132 of 140)" so members can see
|
||||
how many unconfirmed meetings exist without having to switch the filter.
|
||||
|
||||
---
|
||||
|
||||
### 16. "Not Confirmed" status — inline explanation on mobile
|
||||
|
||||
**Current behavior:** Meetings with `status === 'unknown'` show a warning badge:
|
||||
`⚠ Not Confirmed by IDAA ⚠`. The badge has a `title` attribute with a full explanation
|
||||
(~2 sentences). Title tooltips are invisible on mobile — tapping the badge does nothing.
|
||||
Members on phones see a alarming-looking warning with no explanation of what it means
|
||||
or what they should do.
|
||||
|
||||
**Proposed change:** Make the badge tappable. On tap (or hover on desktop), show an
|
||||
inline explanation panel directly below the badge:
|
||||
|
||||
```
|
||||
⚠ Not Confirmed by IDAA [?]
|
||||
|
||||
↓ (on tap)
|
||||
|
||||
This meeting has not been confirmed by IDAA Central Office.
|
||||
Please reach out to the chair for current information.
|
||||
If this meeting is active, email info@idaa.org to confirm it.
|
||||
[✕ Close]
|
||||
```
|
||||
|
||||
**Implementation notes:**
|
||||
- Add a `$state show_unconfirmed_info = false` per card (scoped to the `{#each}` block).
|
||||
- Replace the `title` attribute with an `onclick` toggle that sets `show_unconfirmed_info`.
|
||||
- The explanation renders in a `{#if show_unconfirmed_info}` block directly below the
|
||||
badge row — a simple div with a rounded border and the existing tooltip text.
|
||||
- The `mailto:info@idaa.org` link in the explanation is already in the tooltip text;
|
||||
making it a real clickable link here rather than plain text in a tooltip is a direct
|
||||
improvement for mobile members who want to report a confirmed meeting.
|
||||
- This pattern also applies to other `title`-only tooltips on the page if they appear.
|
||||
- Note: with item #15 defaulting to "confirmed only", most members will never encounter
|
||||
this badge unless they switch to the "All" view. The inline explanation is still worth
|
||||
implementing for that audience, but the default filter reduces how often it's seen.
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- The fulltext search (`qry__fulltext_str`) searches against the `default_qry_str`
|
||||
field, which is a server-side composite that already includes contact info, day-of-week
|
||||
text, meeting type, location, and other metadata. The placeholder text in the search
|
||||
input is accurate — it genuinely searches contacts and schedule information despite
|
||||
those fields being stored in separate columns.
|
||||
- All changes must render correctly inside the Novi iframe context (Bootstrap v3.4.1
|
||||
CSS conflicts — see `CLIENT__IDAA_and_customized_mods.md` for known issues).
|
||||
- Mobile testing should cover Android Chrome specifically — the original "no meetings
|
||||
found" bug disproportionately affected mobile users with intermittent connections.
|
||||
|
||||
@@ -115,33 +115,40 @@ reactivity — only effects that actually read a changed field re-run.
|
||||
|
||||
---
|
||||
|
||||
### [Stores] IDB Content Version System (post June 10)
|
||||
Scaffold added to `store_versions.ts` (`IDB_CONTENT_VERSIONS` constant) — values defined but
|
||||
**not yet wired**. The mechanism mirrors `AE_LOC_VERSION` but targets Dexie table contents
|
||||
rather than localStorage keys.
|
||||
### [Stores] IDB Content Version System
|
||||
Scaffolded in `store_versions.ts` (`IDB_CONTENT_VERSIONS` constant + `check_and_clear_idb_table()`
|
||||
helper) and `core__idb_dexie.ts` (`check_and_clear_idb_tables()` batch helper). Mirrors
|
||||
`AE_LOC_VERSION` but targets Dexie table contents rather than localStorage keys.
|
||||
|
||||
**Why:** `db_save_ae_obj_li__ae_obj` uses a `properties_to_save` whitelist. When that whitelist
|
||||
changes (e.g. adding/removing a stored field), existing cached IDB records are stale but never
|
||||
automatically cleared. Users see the old shape until a record is individually refreshed.
|
||||
**Currently active:** `journals.journal_entry` (db_journals.ts), `events.event` (IDAA layout).
|
||||
All other tables are defined but not yet wired.
|
||||
|
||||
**How it will work:**
|
||||
- Each `db_*.ts` calls a helper (`core__idb_dexie.ts`) on open that checks a `_meta` IDB table
|
||||
- If stored version ≠ `IDB_CONTENT_VERSIONS[module][table]`, clear the table + update `_meta`
|
||||
- SWR repopulates from API on next access (same as any cold-start)
|
||||
**Real-world impact:** Stale IDB records from a `properties_to_save` change were the root cause
|
||||
of the IDAA Recovery Meetings "no meetings found" bug — a ~1-year unresolved issue (2025–2026).
|
||||
Fixed 2026-05-16 by wiring `events.event` into the IDAA layout and bumping its version to 2.
|
||||
See `BOOTSTRAP__AI_Agent_Quickstart.md` mistake #13 for the full postmortem.
|
||||
|
||||
**How it works:**
|
||||
- `check_and_clear_idb_table(db_table, 'module', 'table')` reads a localStorage key with the
|
||||
expected version from `IDB_CONTENT_VERSIONS`
|
||||
- On mismatch (or missing key), the Dexie table is cleared and the key is updated
|
||||
- SWR repopulates from API on next access — no explicit reload needed
|
||||
- Cost on version match: one `localStorage.getItem()` — effectively free
|
||||
- Bump a table's version in `IDB_CONTENT_VERSIONS` when `properties_to_save` changes shape
|
||||
|
||||
**IDAA consideration:**
|
||||
IDAA tables are already cleared by `indexedDB.deleteDatabase()` on sign-out/auth failure in
|
||||
`(idaa)/+layout.svelte`. The content version check is a *complementary* deploy-time reset, not
|
||||
a replacement. When wiring IDAA tables, ensure: (a) the check only runs on IDB open, not
|
||||
mid-session; (b) the `_meta` table is included in the `deleteDatabase()` wipe scope.
|
||||
a replacement.
|
||||
|
||||
**Tasks:**
|
||||
- [x] Write `check_and_clear_idb_tables()` helper in `core__idb_dexie.ts` (2026-05-14)
|
||||
- [x] Wire helper into `db_journals.ts` (pilot — `journal_entry: 2` clears stale content_md_html on first load) (2026-05-14)
|
||||
- [ ] Roll out to `db_events.ts`, `db_core.ts`
|
||||
- [x] Wire helper into `db_journals.ts` (pilot — `journal_entry: 2` cleared stale content_md_html) (2026-05-14)
|
||||
- [x] Wire `events.event` into IDAA layout `(idaa)/+layout.svelte` + bump version to 2 (2026-05-16)
|
||||
- [ ] Roll out to `db_events.ts` (module-wide: session, presenter, badge, device, location, file)
|
||||
- [ ] Roll out to `db_core.ts` (site_domain, person, user)
|
||||
- [ ] Roll out to IDAA modules (`db_posts.ts`, `db_archives.ts`) — verify auth-wipe interaction first
|
||||
- [ ] Update `store_versions.ts` comment from "NOT YET ACTIVE" to document the active mechanism
|
||||
- [ ] Consolidate the two `check_and_clear_idb_table*` helpers (single-table in `store_versions.ts`, batch in `core__idb_dexie.ts`)
|
||||
|
||||
### [Stores] Refactor — Phase 2c (deferred)
|
||||
Phases 1, 2a, 2b are complete (see ✅ Completed below). One phase remaining:
|
||||
|
||||
Reference in New Issue
Block a user