fix(idaa): audit and harden IDAA module components and types

- Updated ae_types.ts with missing IDAA-specific fields for Archives and Events (topic_name, archive_on, contact_li_json, etc.) using snake_case.
- Refactored bulletin board post filter to safely handle null archive_on dates.
- Fixed missing 'data' prop assignment in bulletin board list component to resolve type error.
- Corrected core_func.download_export__obj_type method name in recovery meetings export.
- Hardened safety checks for contact_li_json in recovery meetings view logic to prevent null property access.
- Mapped Jitsi meeting event data to internal snake_case variables and fixed input type assignments.
- Updated project documentation (TODO, GEMINI.md, .ae_brief) to reflect IDAA hardening progress.
This commit is contained in:
Scott Idem
2026-01-26 17:50:27 -05:00
parent 0a390c9505
commit ac14721bd0
9 changed files with 91 additions and 79 deletions

View File

@@ -62,7 +62,7 @@ export interface Event {
// IDAA Recovery Meetings:
// Currently only really used for IDAA
contact_li_json?: null | string[]; // full_name, email, phone_mobile, phone_home, phone_office, other_text
contact_li_json?: null | any[]; // full_name, email, phone_mobile, phone_home, phone_office, other_text
// contact_li_json_ext?: null|string;
external_person_id?: null | string;

View File

@@ -400,8 +400,9 @@ export interface ae_Event extends ae_BaseObj {
conference: boolean;
type?: string;
name: string;
summary?: string;
format?: string;
description?: string;
timezone?: string;
start_datetime?: string | Date;
@@ -418,6 +419,16 @@ export interface ae_Event extends ae_BaseObj {
cfg_json?: any;
data_json?: any;
// IDAA Recovery Meetings / Additional fields
contact_li_json?: any[];
external_person_id?: string;
physical?: boolean;
virtual?: boolean;
recurring?: boolean;
recurring_text?: string;
attend_url?: string;
attend_url_text?: string;
// Joined view fields
event_location_obj_li?: ae_EventLocation[];
event_session_obj_li?: ae_EventSession[];
@@ -732,11 +743,16 @@ export interface ae_Archive extends ae_BaseObj {
account_id_random: string;
archive_type?: string;
topic_id?: string;
topic_name?: string;
content_html?: string;
original_datetime?: string | Date;
original_location?: string;
archive_on?: string | Date;
archive_content_count?: number;
}
@@ -752,8 +768,11 @@ export interface ae_ArchiveContent extends ae_BaseObj {
content_html?: string;
url?: string;
duration?: string;
hosted_file_id_random?: string;
filename?: string;
subdirectory_path?: string;
}
/**

View File

@@ -54,12 +54,16 @@
}
// *** Functions and Logic
// Updated 2026-01-07
// Updated 2026-01-26 (Safety Refactor)
let lq__post_obj_li = $derived(liveQuery(async () => {
const now = new Date();
const results = await db_posts.post
.where('account_id').equals($slct.account_id ?? '')
.filter((x) => x.archive_on === null || new Date(x.archive_on) > now)
.filter((x) => {
if (!x.archive_on) return true;
const archiveDate = x.archive_on instanceof Date ? x.archive_on : new Date(x.archive_on);
return archiveDate > now;
})
.toArray();
return results;
}));
@@ -223,7 +227,7 @@
<!-- <h1>Bulletin Board {$lq__post_obj_li?.length}</h1> -->
{#if $lq__post_obj_li && $lq__post_obj_li?.length}
<Comp__post_obj_li {lq__post_obj_li} />
<Comp__post_obj_li {data} {lq__post_obj_li} />
{:else}
<p>No posts available to show.</p>
{/if}

View File

@@ -245,7 +245,7 @@
</button>
{:else}
<!-- This checks if the currently logged in Novi user has a matching UUID or email address. -->
{#if ($ae_loc.trusted_access && $ae_loc.edit_mode) || $lq__event_obj?.external_person_id === $idaa_loc.novi_uuid || ($lq__event_obj?.contact_li_json && $lq__event_obj?.contact_li_json[0]?.email === $idaa_loc.novi_email)}
{#if ($ae_loc.trusted_access && $ae_loc.edit_mode) || $lq__event_obj?.external_person_id === $idaa_loc.novi_uuid || ($lq__event_obj?.contact_li_json && $lq__event_obj.contact_li_json.length > 0 && $lq__event_obj.contact_li_json[0]?.email === $idaa_loc.novi_email)}
<button
type="button"
class="

View File

@@ -711,8 +711,9 @@
if (!confirm('Download exported data Excel file?')) {
return false;
}
// Use snake_case method name 'download_export__obj_type'
ae_promises.download__events_export =
core_func.handle_download_export__obj_type({
core_func.download_export__obj_type({
api_cfg: $ae_api,
get_obj_type: 'event',
for_obj_type: 'account',

View File

@@ -33,7 +33,7 @@
let domain: null | string = $state(null);
// Jitsi API & Sound Settings
let jitsi_api: any = null;
let jitsi_api: any = $state(null);
const jitsi_container_id = 'jitsi_meet_external_api_container';
let disable_incoming_msg_sound: boolean = $state(true);
let disable_participant_joined_sound: boolean = $state(false);
@@ -114,10 +114,13 @@
function add_jitsi_event_listeners(api: any) {
// --- Meeting/Participant State Changes ---
api.on('videoConferenceJoined', async (data: { id: string; displayName: string; roomName: string }) => {
console.log('Jitsi Event: videoConferenceJoined', data);
api.on('videoConferenceJoined', async (jitsi_data: { id: string; displayName: string; roomName: string }) => {
// Map Jitsi's camelCase to our internal snake_case
const { id: participant_id, displayName: participant_name, roomName: jitsi_room_name } = jitsi_data;
console.log('Jitsi Event: videoConferenceJoined', jitsi_data);
meeting_start_time = new Date();
if (jitsi_meeting_id === null) jitsi_meeting_id = `${data.roomName}-${Date.now()}`;
if (jitsi_meeting_id === null) jitsi_meeting_id = `${jitsi_room_name}-${Date.now()}`;
// Start duration timer
if (duration_timer_id) clearInterval(duration_timer_id);
@@ -133,7 +136,7 @@
}, 1000);
// Populate initial participant list
meeting_participants.set(data.id, { id: data.id, displayName: data.displayName, role: 'participant' });
meeting_participants.set(participant_id, { id: participant_id, displayName: participant_name, role: 'participant' });
api.getParticipantsInfo().forEach((p: { participantId: string; displayName: string }) => {
if (!meeting_participants.has(p.participantId)) {
meeting_participants.set(p.participantId, { id: p.participantId, displayName: p.displayName, role: 'participant' });
@@ -371,8 +374,8 @@
}
// Set initial value for the profile editor inputs
name_input = display_name;
email_input = email;
name_input = display_name ?? '';
email_input = email ?? '';
// --- All data fetched, now initialize Jitsi ---
await init_jitsi();