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:
@@ -120,7 +120,7 @@ export interface Post_Comment {
|
|||||||
export class MySubClassedDexie extends Dexie {
|
export class MySubClassedDexie extends Dexie {
|
||||||
// We just tell the typing system this is the case
|
// We just tell the typing system this is the case
|
||||||
post!: Table<Post>;
|
post!: Table<Post>;
|
||||||
post_comment!: Table<Post_Comment>;
|
comment!: Table<Post_Comment>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('ae_posts_db');
|
super('ae_posts_db');
|
||||||
@@ -136,7 +136,7 @@ export class MySubClassedDexie extends Dexie {
|
|||||||
tmp_sort_1, tmp_sort_2,
|
tmp_sort_1, tmp_sort_2,
|
||||||
enable, hide, priority, sort, group, notes, created_on, updated_on, [updated_on+created_on], [created_on+updated_on]`,
|
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,
|
id, post_comment_id,
|
||||||
post_id,
|
post_id,
|
||||||
name,
|
name,
|
||||||
|
|||||||
@@ -91,66 +91,37 @@
|
|||||||
// Form Post object data outgoing
|
// Form Post object data outgoing
|
||||||
let post_comment_do: key_val = {};
|
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) {
|
if (!$idaa_slct.post_comment_id) {
|
||||||
|
// NEW COMMENT: Ensure post_id is included
|
||||||
const parent_post_id = $idaa_slct.post_id;
|
const parent_post_id = $idaa_slct.post_id;
|
||||||
post_comment_do['post_id'] = parent_post_id;
|
post_comment_do['post_id'] = parent_post_id;
|
||||||
post_comment_do['post_id_random'] = parent_post_id;
|
// post_comment_do['post_id_random'] = parent_post_id;
|
||||||
post_comment_do['enable'] = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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;
|
log_lvl = 1;
|
||||||
if (log_lvl) {
|
if (log_lvl) {
|
||||||
console.log(post_comment_do);
|
console.log('Final Payload (post_comment_do):', post_comment_do);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$idaa_slct.post_comment_id) {
|
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) {
|
if (log_lvl) {
|
||||||
console.log(
|
console.log(
|
||||||
`Current Post Comment Object List Length: ${$idaa_slct.post_comment_obj_li.length}`,
|
`Current Post Comment Object List Length: ${$idaa_slct.post_comment_obj_li.length}`,
|
||||||
@@ -166,25 +137,13 @@
|
|||||||
data_kv: post_comment_do,
|
data_kv: post_comment_do,
|
||||||
log_lvl: log_lvl
|
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) {
|
.catch(function (error: any) {
|
||||||
console.log('Something went wrong.');
|
console.log('Something went wrong.');
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return false;
|
return false;
|
||||||
// })
|
|
||||||
// .finally(async () => {
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (prom_api__post_comment_obj) {
|
||||||
$idaa_slct.post_comment_id = prom_api__post_comment_obj.post_comment_id_random;
|
$idaa_slct.post_comment_id = prom_api__post_comment_obj.post_comment_id_random;
|
||||||
$idaa_slct.post_comment_obj = prom_api__post_comment_obj;
|
$idaa_slct.post_comment_obj = prom_api__post_comment_obj;
|
||||||
|
|
||||||
@@ -192,7 +151,6 @@
|
|||||||
$idaa_sess.bb.show__inline_edit__post_comment_id = false;
|
$idaa_sess.bb.show__inline_edit__post_comment_id = false;
|
||||||
|
|
||||||
// Send notification to staff
|
// 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) {
|
if ($ae_loc.site_cfg_json?.bb_send_staff_new_email) {
|
||||||
send_staff_notification_email();
|
send_staff_notification_email();
|
||||||
}
|
}
|
||||||
@@ -203,27 +161,18 @@
|
|||||||
$ae_loc.site_cfg_json?.bb_send_poster_email &&
|
$ae_loc.site_cfg_json?.bb_send_poster_email &&
|
||||||
$idaa_slct.post_obj.notify
|
$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_poster_notification_email();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a notification to any other post commenters
|
// Send a notification to any other post commenters
|
||||||
if (!$ae_loc.administrator_access && $ae_loc.site_cfg_json?.bb_send_commenter_email) {
|
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) {
|
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) {
|
for (const post_comment of $idaa_slct.post_comment_obj_li) {
|
||||||
console.log('Post Comment ID:', post_comment.post_comment_id);
|
|
||||||
if (
|
if (
|
||||||
post_comment?.email &&
|
post_comment?.email &&
|
||||||
post_comment.post_comment_id !== $idaa_slct.post_comment_id &&
|
post_comment.post_comment_id !== $idaa_slct.post_comment_id &&
|
||||||
post_comment?.email !== $idaa_slct.post_comment_obj?.email
|
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({
|
send_poster_commenters_notification_email({
|
||||||
post_comment_obj: post_comment
|
post_comment_obj: post_comment
|
||||||
});
|
});
|
||||||
@@ -231,6 +180,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
disable_submit_btn = false;
|
||||||
|
}
|
||||||
|
|
||||||
return prom_api__post_comment_obj;
|
return prom_api__post_comment_obj;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -69,7 +69,7 @@
|
|||||||
let upload_complete = $state(false);
|
let upload_complete = $state(false);
|
||||||
let disable_submit_btn = $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) {
|
return function (event: T) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
fn(event);
|
fn(event);
|
||||||
@@ -102,7 +102,8 @@
|
|||||||
let post_do: key_val = {};
|
let post_do: key_val = {};
|
||||||
|
|
||||||
if (!$idaa_slct.post_id) {
|
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;
|
post_do['enable'] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,6 +366,11 @@ Copy and paste link: <a href="${link_base_url}?post_id=${$idaa_slct.post_id}">${
|
|||||||
}
|
}
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
// Initialization Layer
|
||||||
|
if (!orig_post_obj?.id && $idaa_slct.post_obj?.id) {
|
||||||
|
orig_post_obj = { ...$idaa_slct.post_obj };
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
orig_post_obj === null ||
|
orig_post_obj === null ||
|
||||||
orig_post_obj === undefined ||
|
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 ?? ''))
|
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.', $inspect(orig_post_obj));
|
||||||
|
if (log_lvl) {
|
||||||
console.log('Post object has changed from original.', orig_post_obj);
|
console.log('Post object has changed from original.', orig_post_obj);
|
||||||
console.log('Post object has changed.', $idaa_slct.post_obj);
|
console.log('Post object has changed.', $idaa_slct.post_obj);
|
||||||
|
}
|
||||||
obj_changed = true;
|
obj_changed = true;
|
||||||
} else if (
|
} else if (
|
||||||
obj_changed &&
|
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}
|
class:ae_create={!$idaa_slct.post_id}
|
||||||
bind:clientHeight={$ae_loc.iframe_height_modal_body}
|
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}
|
{#await prom_api__post_obj}
|
||||||
<div class="awaiting alert_msg_pulse" out:fade={{ duration: 2000 }}>Saving...</div>
|
<div class="awaiting alert_msg_pulse" out:fade={{ duration: 2000 }}>Saving...</div>
|
||||||
{:then}
|
{:then}
|
||||||
|
|||||||
@@ -26,6 +26,40 @@
|
|||||||
|
|
||||||
let { data, lq__post_obj_li }: Props = $props();
|
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(() => {
|
onMount(() => {
|
||||||
if (log_lvl) {
|
if (log_lvl) {
|
||||||
console.log(
|
console.log(
|
||||||
@@ -37,16 +71,14 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section class="bb_post_list flex flex-col gap-2 items-center justify-center w-full">
|
<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}
|
{#if visible_post_obj_li && visible_post_obj_li.length}
|
||||||
{#each $lq__post_obj_li as idaa_post_obj, index}
|
{#each visible_post_obj_li as idaa_post_obj, index}
|
||||||
{#if idaa_post_obj}
|
{#if idaa_post_obj}
|
||||||
<!-- This check for the idaa_post_obj is here in case the IDB entry is deleted. -->
|
<!-- This check for the idaa_post_obj is here in case the IDB entry is deleted. -->
|
||||||
<div
|
<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="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:dim={idaa_post_obj?.hide}
|
||||||
class:bg-warning-100={!idaa_post_obj?.enable}
|
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">
|
<header class="ae_header">
|
||||||
<h3
|
<h3
|
||||||
|
|||||||
@@ -200,27 +200,24 @@
|
|||||||
if (log_lvl) {
|
if (log_lvl) {
|
||||||
console.log('New post created:', results);
|
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 = {
|
$idaa_slct.post_obj = {
|
||||||
...results
|
...results
|
||||||
};
|
};
|
||||||
$idaa_sess.bb.edit__post_obj = $idaa_slct.post_id;
|
$idaa_sess.bb.edit__post_obj = clean_post_id;
|
||||||
$idaa_loc.bb.edit__post_obj = $idaa_slct.post_id;
|
$idaa_loc.bb.edit__post_obj = clean_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}`);
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('Error updating post:', error);
|
console.error('Error creating post:', error);
|
||||||
alert('Failed to update post.');
|
alert('Failed to create post.');
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
if ($idaa_slct.post_id) {
|
const final_id = $idaa_slct.post_id;
|
||||||
goto(`/idaa/bb/${$idaa_slct.post_id}`);
|
if (final_id) {
|
||||||
|
if (log_lvl) console.log(`Redirecting to new post: ${final_id}`);
|
||||||
|
goto(`/idaa/bb/${final_id}`);
|
||||||
} else {
|
} else {
|
||||||
alert('Failed to create new post.');
|
alert('Failed to create new post.');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user