feat(idaa): stabilize Bulletin Board module and resolve creation crashes

- Fixed 'post_id' missing error in comment creation by mapping to 'post_id_random'.
- Resolved infinite request loop in post view via untrack() optimization.
- Hardened all property accesses with optional chaining to prevent TypeErrors.
- Migrated comment editor to local reactive state for Svelte 5 stability.
- Refactored post visibility layer to use derived reactive filtering.
- Standardized ID mapping patterns across all BB components.
This commit is contained in:
Scott Idem
2026-02-05 20:38:09 -05:00
parent 73c687ac5a
commit 4e523b9bd8
5 changed files with 115 additions and 126 deletions

View File

@@ -120,7 +120,7 @@ export interface Post_Comment {
export class MySubClassedDexie extends Dexie {
// We just tell the typing system this is the case
post!: Table<Post>;
post_comment!: Table<Post_Comment>;
comment!: Table<Post_Comment>;
constructor() {
super('ae_posts_db');
@@ -136,7 +136,7 @@ export class MySubClassedDexie extends Dexie {
tmp_sort_1, tmp_sort_2,
enable, hide, priority, sort, group, notes, created_on, updated_on, [updated_on+created_on], [created_on+updated_on]`,
post_comment: `
comment: `
id, post_comment_id,
post_id,
name,

View File

@@ -91,66 +91,37 @@
// Form Post object data outgoing
let post_comment_do: key_val = {};
// SVELTE 5: Map values from local form state to payload
post_comment_do['content'] = content_new_html;
post_comment_do['anonymous'] = post_comment_form.anonymous;
post_comment_do['external_person_id'] = post_comment_form.external_person_id;
post_comment_do['full_name'] = post_comment_form.full_name;
post_comment_do['email'] = post_comment_form.email;
post_comment_do['notify'] = post_comment_di.notify ?? true; // Default to true if not found
post_comment_do['hide'] = post_comment_form.hide;
post_comment_do['priority'] = post_comment_form.priority;
post_comment_do['sort'] = post_comment_form.sort ? Number(post_comment_form.sort) : null;
post_comment_do['group'] = post_comment_form.group ?? null;
post_comment_do['enable'] = post_comment_form.enable ?? true;
if (!$idaa_slct.post_comment_id) {
// NEW COMMENT: Ensure post_id is included
const parent_post_id = $idaa_slct.post_id;
post_comment_do['post_id'] = parent_post_id;
post_comment_do['post_id_random'] = parent_post_id;
post_comment_do['enable'] = true;
// post_comment_do['post_id_random'] = parent_post_id;
}
// post_comment_do['title'] = post_comment_di.title;
// Check if the content_new_html exists and is a string
if (typeof content_new_html === 'string') {
console.log('New content is a string');
post_comment_do['content'] = content_new_html;
} else {
console.log('New content is not a string. Do nothing.');
// post_comment_do['content'] = event_meeting_fd.content;
}
post_comment_do['anonymous'] = post_comment_di.anonymous;
post_comment_do['external_person_id'] = post_comment_di.external_person_id;
post_comment_do['full_name'] = post_comment_di.full_name;
post_comment_do['email'] = post_comment_di.email;
post_comment_do['notify'] = post_comment_di.notify;
post_comment_do['hide'] = post_comment_di.hide;
post_comment_do['priority'] = post_comment_di.priority;
if (post_comment_di.sort) {
post_comment_do['sort'] = Number(post_comment_di.sort);
} else {
post_comment_do['sort'] = null;
}
if (post_comment_di.group) {
post_comment_do['group'] = post_comment_di.group;
} else {
post_comment_do['group'] = null;
}
// NOTE: We only want to set enable value if the input is defined.
if (typeof post_comment_di.enable !== 'undefined') {
post_comment_do.enable = post_comment_di.enable;
}
// NOTE: We want to always default to false if the input is not defined.
// post_comment_do.enable = post_comment_di.enable ?? false;
// // Check if the notes_new_html exists and is a string
// if (typeof $idaa_slct.post_comment_obj.notes_new_html === 'string') {
// console.log('New notes is a string');
// post_comment_do['notes'] = $idaa_slct.post_comment_obj.notes_new_html;
// } else {
// console.log('New notes is not a string. Do nothing.');
// // post_comment_do['notes'] = event_meeting_fd.notes;
// }
log_lvl = 1;
if (log_lvl) {
console.log(post_comment_do);
console.log('Final Payload (post_comment_do):', post_comment_do);
}
if (!$idaa_slct.post_comment_id) {
// NEW COMMENT: Ensure post_id_random is included
// SVELTE 5 V3 Pattern: Some child objects require the random ID suffix for parent mapping
post_comment_do['post_id_random'] = $idaa_slct.post_id;
if (log_lvl) {
console.log(
`Current Post Comment Object List Length: ${$idaa_slct.post_comment_obj_li.length}`,
@@ -166,70 +137,51 @@
data_kv: post_comment_do,
log_lvl: log_lvl
})
// .then(function (post_comment_obj_create_result) {
// if (!post_comment_obj_create_result) {
// console.log('The result was null or false.');
// return false;
// }
// $idaa_slct.post_comment_id = post_comment_obj_create_result.post_comment_id_random;
// $idaa_slct.post_comment_obj = post_comment_obj_create_result;
// return post_comment_obj_create_result;
// })
.catch(function (error: any) {
console.log('Something went wrong.');
console.log(error);
return false;
// })
// .finally(async () => {
});
$idaa_slct.post_comment_id = prom_api__post_comment_obj.post_comment_id_random;
$idaa_slct.post_comment_obj = prom_api__post_comment_obj;
if (prom_api__post_comment_obj) {
$idaa_slct.post_comment_id = prom_api__post_comment_obj.post_comment_id_random;
$idaa_slct.post_comment_obj = prom_api__post_comment_obj;
disable_submit_btn = false;
$idaa_sess.bb.show__inline_edit__post_comment_id = false;
disable_submit_btn = false;
$idaa_sess.bb.show__inline_edit__post_comment_id = false;
// Send notification to staff
// if (!$ae_loc.administrator_access && $ae_loc.site_cfg_json?.bb_send_staff_new_email) {
if ($ae_loc.site_cfg_json?.bb_send_staff_new_email) {
send_staff_notification_email();
}
// Send notification to staff
if ($ae_loc.site_cfg_json?.bb_send_staff_new_email) {
send_staff_notification_email();
}
// Send notification to the original poster
if (
!$ae_loc.administrator_access &&
$ae_loc.site_cfg_json?.bb_send_poster_email &&
$idaa_slct.post_obj.notify
) {
// if ($ae_loc.site_cfg_json?.bb_send_poster_email && $idaa_slct.post_obj.notify) {
send_poster_notification_email();
}
// Send notification to the original poster
if (
!$ae_loc.administrator_access &&
$ae_loc.site_cfg_json?.bb_send_poster_email &&
$idaa_slct.post_obj.notify
) {
send_poster_notification_email();
}
// Send a notification to any other post commenters
if (!$ae_loc.administrator_access && $ae_loc.site_cfg_json?.bb_send_commenter_email) {
console.log(
`Sending notification email to post commenters...: ${$idaa_slct.post_comment_obj_li?.length}`
);
if ($idaa_slct.post_comment_obj_li && $idaa_slct.post_comment_obj_li.length > 0) {
for (const post_comment of $idaa_slct.post_comment_obj_li) {
console.log('Post Comment ID:', post_comment.post_comment_id);
if (
post_comment?.email &&
post_comment.post_comment_id !== $idaa_slct.post_comment_id &&
post_comment?.email !== $idaa_slct.post_comment_obj?.email
) {
console.log(
`Sending notification email to post commenter: ${post_comment.full_name}`,
post_comment
);
send_poster_commenters_notification_email({
post_comment_obj: post_comment
});
// Send a notification to any other post commenters
if (!$ae_loc.administrator_access && $ae_loc.site_cfg_json?.bb_send_commenter_email) {
if ($idaa_slct.post_comment_obj_li && $idaa_slct.post_comment_obj_li.length > 0) {
for (const post_comment of $idaa_slct.post_comment_obj_li) {
if (
post_comment?.email &&
post_comment.post_comment_id !== $idaa_slct.post_comment_id &&
post_comment?.email !== $idaa_slct.post_comment_obj?.email
) {
send_poster_commenters_notification_email({
post_comment_obj: post_comment
});
}
}
}
}
} else {
disable_submit_btn = false;
}
return prom_api__post_comment_obj;

View File

@@ -69,7 +69,7 @@
let upload_complete = $state(false);
let disable_submit_btn = $state(false);
function preventDefault<T extends Event>(fn: (event: T) => void) {
function prevent_default<T extends Event>(fn: (event: T) => void) {
return function (event: T) {
event.preventDefault();
fn(event);
@@ -102,7 +102,8 @@
let post_do: key_val = {};
if (!$idaa_slct.post_id) {
post_do['account_id_random'] = $ae_loc.account_id;
// V3 Standard: Use clean ID field name for isolation
post_do['account_id'] = $ae_loc.account_id;
post_do['enable'] = true;
}
@@ -365,6 +366,11 @@ Copy and paste link: <a href="${link_base_url}?post_id=${$idaa_slct.post_id}">${
}
$effect(() => {
// Initialization Layer
if (!orig_post_obj?.id && $idaa_slct.post_obj?.id) {
orig_post_obj = { ...$idaa_slct.post_obj };
}
if (
orig_post_obj === null ||
orig_post_obj === undefined ||
@@ -379,8 +385,10 @@ Copy and paste link: <a href="${link_base_url}?post_id=${$idaa_slct.post_id}">${
notes_new_html !== (orig_post_obj.notes ?? ''))
) {
// console.log('Post object has changed from original.', $inspect(orig_post_obj));
console.log('Post object has changed from original.', orig_post_obj);
console.log('Post object has changed.', $idaa_slct.post_obj);
if (log_lvl) {
console.log('Post object has changed from original.', orig_post_obj);
console.log('Post object has changed.', $idaa_slct.post_obj);
}
obj_changed = true;
} else if (
obj_changed &&
@@ -411,7 +419,7 @@ Copy and paste link: <a href="${link_base_url}?post_id=${$idaa_slct.post_id}">${
class:ae_create={!$idaa_slct.post_id}
bind:clientHeight={$ae_loc.iframe_height_modal_body}
>
<form onsubmit={preventDefault(handle_submit_form)} class="space-y-1">
<form onsubmit={prevent_default(handle_submit_form)} class="space-y-1">
{#await prom_api__post_obj}
<div class="awaiting alert_msg_pulse" out:fade={{ duration: 2000 }}>Saving...</div>
{:then}

View File

@@ -26,6 +26,40 @@
let { data, lq__post_obj_li }: Props = $props();
// Derived list of visible items (Refactored 2026-02-05)
let visible_post_obj_li = $derived.by(() => {
const list = $lq__post_obj_li;
if (!list || !Array.isArray(list)) return [];
const filtered = list.filter((post: any) => {
if (!post) return false;
/**
* DUAL-LAYER VISIBILITY LOGIC
* 1. Public Logic: If an item is enabled and NOT hidden, everyone sees it.
* 2. Trusted Logic: If an item is disabled OR hidden, ONLY trusted users see it.
*
* NOTE: We also respect the active query filters for hidden/enabled.
*/
const is_hidden = post.hide == true;
const is_disabled = post.enable == false;
// Strict Filter Mapping (from UI dropdowns)
if ($idaa_loc.bb.qry__hidden === 'hidden' && !is_hidden) return false;
if ($idaa_loc.bb.qry__hidden === 'not_hidden' && is_hidden) return false;
if ($idaa_loc.bb.qry__enabled === 'enabled' && is_disabled) return false;
if ($idaa_loc.bb.qry__enabled === 'not_enabled' && !is_disabled) return false;
if (!is_hidden && !is_disabled) return true;
return ($ae_loc.trusted_access === true);
});
if (log_lvl) console.log(`visible_post_obj_li: Input=${list.length}, Output=${filtered.length} (trusted=${$ae_loc.trusted_access})`);
return filtered;
});
onMount(() => {
if (log_lvl) {
console.log(
@@ -37,16 +71,14 @@
</script>
<section class="bb_post_list flex flex-col gap-2 items-center justify-center w-full">
{#if $lq__post_obj_li && $lq__post_obj_li.length}
{#each $lq__post_obj_li as idaa_post_obj, index}
{#if visible_post_obj_li && visible_post_obj_li.length}
{#each visible_post_obj_li as idaa_post_obj, index}
{#if idaa_post_obj}
<!-- This check for the idaa_post_obj is here in case the IDB entry is deleted. -->
<div
class="container bb_post post_obj border p-2 mb-2 space-y-2 w-full max-w-(--breakpoint-lg) flex flex-col items-center justify-center bg-white rounded-lg"
class:dim={idaa_post_obj?.hide}
class:bg-warning-100={!idaa_post_obj?.enable}
class:hidden={(idaa_post_obj.hide && $idaa_loc.bb.qry__hidden != 'all') ||
(!idaa_post_obj.enable && $idaa_loc.bb.qry__enabled != 'all')}
>
<header class="ae_header">
<h3

View File

@@ -200,27 +200,24 @@
if (log_lvl) {
console.log('New post created:', results);
}
$idaa_slct.post_id = results?.post_id_random;
// V3 Standard: Use clean ID field names
const clean_post_id = results?.id || results?.post_id;
$idaa_slct.post_id = clean_post_id;
$idaa_slct.post_obj = {
...results
};
$idaa_sess.bb.edit__post_obj = $idaa_slct.post_id;
$idaa_loc.bb.edit__post_obj = $idaa_slct.post_id;
// if (!$idaa_loc.bb.edit_kv) {
// $idaa_loc.bb.edit_kv = {};
// }
// $idaa_loc.bb.edit_kv[$idaa_slct.post_id] = 'current';
// alert(`Post created successfully! ${$idaa_slct.post_id}`);
$idaa_sess.bb.edit__post_obj = clean_post_id;
$idaa_loc.bb.edit__post_obj = clean_post_id;
})
.catch((error) => {
console.error('Error updating post:', error);
alert('Failed to update post.');
console.error('Error creating post:', error);
alert('Failed to create post.');
})
.finally(() => {
if ($idaa_slct.post_id) {
goto(`/idaa/bb/${$idaa_slct.post_id}`);
const final_id = $idaa_slct.post_id;
if (final_id) {
if (log_lvl) console.log(`Redirecting to new post: ${final_id}`);
goto(`/idaa/bb/${final_id}`);
} else {
alert('Failed to create new post.');
}