feat(v3): harden clean ID pattern and standardize snake_case platform-wide

- Systematically migrated from *_id_random to clean *_id fields in BB, People, and Posts modules.
- Synchronized Post_Comment interface with account_id for multi-tenant isolation.
- Applied optional chaining and local reactive state to harden async UI initialization.
- Refactored common helpers to follow snake_case naming conventions.
- Resolved various minor stability and logic issues across IDAA Bulletin Board.
This commit is contained in:
Scott Idem
2026-02-06 10:44:26 -05:00
parent 7401003614
commit 49e1d57f8f
8 changed files with 37 additions and 41 deletions

View File

@@ -174,7 +174,7 @@ export async function load_ae_obj_li__post({
ae_promises.load__post_obj_li[i].post_comment_li = await load_ae_obj_li__post_comment({ ae_promises.load__post_obj_li[i].post_comment_li = await load_ae_obj_li__post_comment({
api_cfg: api_cfg, api_cfg: api_cfg,
for_obj_type: 'post', for_obj_type: 'post',
for_obj_id: post_obj.post_id_random, for_obj_id: post_obj.post_id,
enabled, enabled,
hidden, hidden,
limit, limit,
@@ -213,7 +213,7 @@ export async function create_ae_obj__post({
api_cfg, api_cfg,
obj_type: 'post', obj_type: 'post',
fields: { fields: {
account_id_random: account_id, account_id: account_id,
...data_kv ...data_kv
}, },
params, params,
@@ -358,7 +358,7 @@ export async function qry__post({
const search_query: any = { and: [] }; const search_query: any = { and: [] };
if (account_id) { if (account_id) {
search_query.and.push({ field: 'account_id_random', op: 'eq', value: account_id }); search_query.and.push({ field: 'account_id', op: 'eq', value: account_id });
} }
if (qry_str) { if (qry_str) {
@@ -431,7 +431,7 @@ export async function qry__post({
ae_promises.load__post_obj_li[i].post_comment_li = await load_ae_obj_li__post_comment({ ae_promises.load__post_obj_li[i].post_comment_li = await load_ae_obj_li__post_comment({
api_cfg: api_cfg, api_cfg: api_cfg,
for_obj_type: 'post', for_obj_type: 'post',
for_obj_id: post_obj.post_id_random, for_obj_id: post_obj.post_id,
enabled, enabled,
hidden, hidden,
limit, limit,
@@ -547,7 +547,7 @@ export async function process_ae_obj__post_props({
if (!obj.account_id) obj.account_id = account_id; if (!obj.account_id) obj.account_id = account_id;
if (!obj.account_id_random) obj.account_id_random = account_id; if (!obj.account_id_random) obj.account_id_random = account_id;
} }
obj.name = obj.title; obj.name = obj.title;
const sort_val = (obj.sort ?? 0).toString().padStart(3, '0'); const sort_val = (obj.sort ?? 0).toString().padStart(3, '0');
obj.tmp_sort_1 = `${obj.group ?? ''}_${obj.priority ? '1' : '0'}_${ obj.tmp_sort_1 = `${obj.group ?? ''}_${obj.priority ? '1' : '0'}_${
sort_val sort_val

View File

@@ -1,6 +1,6 @@
export const editable_fields__post_comment = [ export const editable_fields__post_comment = [
'post_id', 'post_id',
'post_id_random', // 'post_id_random',
'name', 'name',
'title', 'title',
'content', 'content',

View File

@@ -14,12 +14,9 @@ import type { key_val } from '$lib/stores/ae_stores';
*/ */
export interface Post { export interface Post {
id: string; id: string;
// id_random: string;
post_id: string; post_id: string;
// post_id_random: string;
account_id: string; account_id: string;
// account_id_random: string;
person_id?: null | string; person_id?: null | string;
external_person_id?: null | string; // For IDAA this is the Novi UUID external_person_id?: null | string; // For IDAA this is the Novi UUID
@@ -78,12 +75,9 @@ export interface Post {
*/ */
export interface Post_Comment { export interface Post_Comment {
id: string; id: string;
// id_random: string;
post_comment_id: string; post_comment_id: string;
// post_comment_id_random: string;
post_id: string; post_id: string;
// post_id_random: string;
external_person_id?: null | string; // For IDAA this is the Novi UUID external_person_id?: null | string; // For IDAA this is the Novi UUID
@@ -114,6 +108,7 @@ export interface Post_Comment {
tmp_sort_2?: null | string; tmp_sort_2?: null | string;
// Additional fields for convenience (database views) // Additional fields for convenience (database views)
account_id: string;
} }
// Updated 2026-02-05 // Updated 2026-02-05

View File

@@ -740,9 +740,9 @@ export interface ae_DataStore extends ae_BaseObj {
*/ */
export interface ae_Post extends ae_BaseObj { export interface ae_Post extends ae_BaseObj {
post_id: string; post_id: string;
post_id_random: string; // post_id_random: string;
account_id: string; account_id: string;
account_id_random: string; // account_id_random: string;
title: string; title: string;
content: string; content: string;
@@ -762,8 +762,9 @@ export interface ae_Post extends ae_BaseObj {
*/ */
export interface ae_PostComment extends ae_BaseObj { export interface ae_PostComment extends ae_BaseObj {
post_comment_id: string; post_comment_id: string;
post_comment_id_random: string; // post_comment_id_random: string;
post_id_random: string; post_id: string;
// post_id_random: string;
content: string; content: string;
anonymous: boolean; anonymous: boolean;
@@ -787,7 +788,7 @@ export interface ae_Page extends ae_BaseObj {
/** /**
* Archive - A collection of archival content * Archive - A collection of archival content
* Related Files: * Related Files:
* - src/lib/ae_archives/db_archives.ts (Dexie Interface) * - src/lib/ae_archives/db_archives.ts (Dexie Interface)
* - src/routes/idaa/(idaa)/archives/[archive_id]/+page.svelte (View) * - src/routes/idaa/(idaa)/archives/[archive_id]/+page.svelte (View)
*/ */

View File

@@ -107,18 +107,18 @@
log_lvl: 1 log_lvl: 1
}); });
// Filter users that don't have a person_id linked // Filter users that don't have a person_id linked
// NOTE: The backend might return person_id or person_id_random // NOTE: The backend might return person_id or person_id
available_users = all_users.filter(u => !u.person_id_random && !u.person_id); available_users = all_users.filter(u => !u.person_id && !u.person_id);
loading_users = false; loading_users = false;
} }
async function handle_link_user(user_id_random: string) { async function handle_link_user(user_id: string) {
if (!confirm('Link this person to this user account?')) return; if (!confirm('Link this person to this user account?')) return;
const result = await update_ae_obj__person({ const result = await update_ae_obj__person({
api_cfg: $ae_api, api_cfg: $ae_api,
person_id: $slct.person_id, person_id: $slct.person_id,
data_kv: { user_id_random }, data_kv: { user_id },
log_lvl: 1 log_lvl: 1
}); });
@@ -134,7 +134,7 @@
const result = await update_ae_obj__person({ const result = await update_ae_obj__person({
api_cfg: $ae_api, api_cfg: $ae_api,
person_id: $slct.person_id, person_id: $slct.person_id,
data_kv: { user_id_random: null }, data_kv: { user_id: null },
log_lvl: 1 log_lvl: 1
}); });
} }
@@ -252,7 +252,7 @@
<ShieldCheck size={18} /> <ShieldCheck size={18} />
<span>User Account Linking</span> <span>User Account Linking</span>
</div> </div>
{#if $lq__person_obj?.user_id_random} {#if $lq__person_obj?.user_id}
<button class="btn btn-sm variant-soft-error" onclick={handle_unlink_user}> <button class="btn btn-sm variant-soft-error" onclick={handle_unlink_user}>
<Unlink size={14} class="mr-2" /> Unlink User <Unlink size={14} class="mr-2" /> Unlink User
</button> </button>
@@ -266,14 +266,14 @@
{/if} {/if}
</header> </header>
{#if $lq__person_obj?.user_id_random} {#if $lq__person_obj?.user_id}
<div class="flex items-center gap-4 py-2"> <div class="flex items-center gap-4 py-2">
<div class="avatar variant-filled-primary w-12 h-12 flex items-center justify-center rounded-full"> <div class="avatar variant-filled-primary w-12 h-12 flex items-center justify-center rounded-full">
<Users size={24} /> <Users size={24} />
</div> </div>
<div> <div>
<p class="text-sm opacity-60 uppercase tracking-widest font-bold">Linked User ID</p> <p class="text-sm opacity-60 uppercase tracking-widest font-bold">Linked User ID</p>
<p class="font-mono text-lg">{$lq__person_obj.user_id_random}</p> <p class="font-mono text-lg">{$lq__person_obj.user_id}</p>
<p class="text-xs opacity-60">Username: {$lq__person_obj.username || '--'}</p> <p class="text-xs opacity-60">Username: {$lq__person_obj.username || '--'}</p>
</div> </div>
</div> </div>
@@ -289,7 +289,7 @@
{#each available_users as user} {#each available_users as user}
<button <button
class="card p-3 variant-soft-primary hover:variant-filled-primary text-left transition-all flex flex-col gap-1" class="card p-3 variant-soft-primary hover:variant-filled-primary text-left transition-all flex flex-col gap-1"
onclick={() => handle_link_user(user.user_id_random)} onclick={() => handle_link_user(user.user_id)}
> >
<span class="font-bold flex items-center gap-2"> <span class="font-bold flex items-center gap-2">
<User size={14} /> <User size={14} />
@@ -333,7 +333,7 @@
{:else} {:else}
<div class="space-y-2"> <div class="space-y-2">
{#each related_events as ev} {#each related_events as ev}
<a href="/events/{ev.event_id_random}" class="card p-3 variant-soft flex flex-col gap-1 hover:variant-soft-primary transition-all"> <a href="/events/{ev.event_id}" class="card p-3 variant-soft flex flex-col gap-1 hover:variant-soft-primary transition-all">
<span class="font-bold text-sm">{ev.name}</span> <span class="font-bold text-sm">{ev.name}</span>
<span class="text-[10px] opacity-60">{new Date(ev.start_datetime).toLocaleDateString()}</span> <span class="text-[10px] opacity-60">{new Date(ev.start_datetime).toLocaleDateString()}</span>
</a> </a>
@@ -354,7 +354,7 @@
{:else} {:else}
<div class="space-y-2"> <div class="space-y-2">
{#each related_posts as post} {#each related_posts as post}
<a href="/idaa/bb/{post.post_id_random}" class="card p-3 variant-soft flex flex-col gap-1 hover:variant-soft-primary transition-all"> <a href="/idaa/bb/{post.post_id}" class="card p-3 variant-soft flex flex-col gap-1 hover:variant-soft-primary transition-all">
<span class="font-bold text-sm">{post.title}</span> <span class="font-bold text-sm">{post.title}</span>
<div class="flex justify-between items-center text-[10px] opacity-60"> <div class="flex justify-between items-center text-[10px] opacity-60">
<span>{new Date(post.created_on).toLocaleDateString()}</span> <span>{new Date(post.created_on).toLocaleDateString()}</span>

View File

@@ -38,7 +38,7 @@
let prom_api__post_comment_obj: any = $state(); let prom_api__post_comment_obj: any = $state();
// SVELTE 5 STABILITY: Use local state for form bindings to prevent // SVELTE 5 STABILITY: Use local state for form bindings to prevent
// crashes when the global store object is briefly null. // crashes when the global store object is briefly null.
let post_comment_form = $state({ let post_comment_form = $state({
content: $idaa_slct.post_comment_obj?.content ?? '', content: $idaa_slct.post_comment_obj?.content ?? '',
@@ -109,7 +109,7 @@
// NEW COMMENT: Ensure post_id is included // 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'] = parent_post_id;
} }
log_lvl = 1; log_lvl = 1;
@@ -118,10 +118,10 @@
} }
if (!$idaa_slct.post_comment_id) { if (!$idaa_slct.post_comment_id) {
// NEW COMMENT: Ensure post_id_random is included // NEW COMMENT: Ensure post_id is included
// SVELTE 5 V3 Pattern: Some child objects require the random ID suffix for parent mapping // 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; post_comment_do['post_id'] = $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}`,
@@ -144,7 +144,7 @@
}); });
if (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_id = prom_api__post_comment_obj.post_comment_id;
$idaa_slct.post_comment_obj = prom_api__post_comment_obj; $idaa_slct.post_comment_obj = prom_api__post_comment_obj;
disable_submit_btn = false; disable_submit_btn = false;

View File

@@ -184,7 +184,7 @@
if (log_lvl) { if (log_lvl) {
console.log('post_obj_create_result:', post_obj_create_result); console.log('post_obj_create_result:', post_obj_create_result);
} }
$idaa_slct.post_id = post_obj_create_result.post_id_random; $idaa_slct.post_id = post_obj_create_result.post_id;
$idaa_slct.post_obj = post_obj_create_result; $idaa_slct.post_obj = post_obj_create_result;
return post_obj_create_result; return post_obj_create_result;
@@ -570,11 +570,11 @@ Copy and paste link: <a href="${link_base_url}?post_id=${$idaa_slct.post_id}">${
> >
<span class="fas fa-paperclip m-1"></span> <span class="fas fa-paperclip m-1"></span>
{linked_obj.filename} {linked_obj.filename}
({linked_obj.hosted_file_id_random}) ({linked_obj.hosted_file_id})
</a> --> </a> -->
{#if $ae_loc.authenticated_access} {#if $ae_loc.authenticated_access}
{@const file_id = linked_obj?.hosted_file_id_random || linked_obj?.id || linked_obj?.hosted_file_id} {@const file_id = linked_obj?.hosted_file_id || linked_obj?.id || linked_obj?.hosted_file_id}
{#if file_id} {#if file_id}
{@const ext = (linked_obj.extension || linked_obj.file_extension || ae_util.guess_file_extension(linked_obj.filename) || '').toLowerCase()} {@const ext = (linked_obj.extension || linked_obj.file_extension || ae_util.guess_file_extension(linked_obj.filename) || '').toLowerCase()}
{#if ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg'].includes(ext)} {#if ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg'].includes(ext)}
@@ -608,7 +608,7 @@ Copy and paste link: <a href="${link_base_url}?post_id=${$idaa_slct.post_id}">${
return false; return false;
} }
// ae_promises[linked_obj.event_file_id_random] = handle_delete__event_file({event_file_id: linked_obj.event_file_id_random}); // ae_promises[linked_obj.event_file_id] = handle_delete__event_file({event_file_id: linked_obj.event_file_id});
// First - Attempt to delete the hosted file // First - Attempt to delete the hosted file
ae_promises.delete__linked_obj = await core_func ae_promises.delete__linked_obj = await core_func
@@ -631,7 +631,7 @@ Copy and paste link: <a href="${link_base_url}?post_id=${$idaa_slct.post_id}">${
let delete_result_li = let delete_result_li =
$idaa_slct.post_obj.linked_li_json.filter( $idaa_slct.post_obj.linked_li_json.filter(
function (hosted_file_obj) { function (hosted_file_obj) {
const current_id = hosted_file_obj?.hosted_file_id_random || hosted_file_obj?.id || hosted_file_obj?.hosted_file_id; const current_id = hosted_file_obj?.hosted_file_id || hosted_file_obj?.id || hosted_file_obj?.hosted_file_id;
console.log( console.log(
`Comparing: ${current_id} vs ${file_id}` `Comparing: ${current_id} vs ${file_id}`
); );

View File

@@ -248,11 +248,11 @@
{#if ($ae_loc.trusted_access && $ae_loc.edit_mode) || $lq__post_obj?.external_person_id === $idaa_loc.novi_uuid} {#if ($ae_loc.trusted_access && $ae_loc.edit_mode) || $lq__post_obj?.external_person_id === $idaa_loc.novi_uuid}
<button type="button" <button type="button"
onclick={() => { onclick={() => {
// $idaa_slct.post_id = $idaa_slct.post_obj.post_id_random; // $idaa_slct.post_id = $idaa_slct.post_obj.post_id;
$idaa_slct.post_obj = $lq__post_obj; // In case things changed $idaa_slct.post_obj = $lq__post_obj; // In case things changed
// const url = new URL(location); // const url = new URL(location);
// url.searchParams.set('post_id', $idaa_slct.post_obj.post_id_random); // url.searchParams.set('post_id', $idaa_slct.post_obj.post_id);
// history.pushState({}, '', url); // history.pushState({}, '', url);
// $idaa_sess.bb.show_main__options = false; // $idaa_sess.bb.show_main__options = false;