Improve IDAA BB post editing

This commit is contained in:
Scott Idem
2026-05-01 17:34:18 -04:00
parent 878ff91c30
commit 20bf1d94eb
8 changed files with 481 additions and 430 deletions

View File

@@ -0,0 +1,228 @@
import type { key_val } from '$lib/stores/ae_stores';
export interface BbPostIdentityDefaults {
external_person_id?: string | null;
full_name?: string | null;
email?: string | null;
}
export interface BbPostObjectLike {
post_id?: string | number | null;
id?: string | number | null;
title?: string | null;
topic_id?: string | number | null;
anonymous?: boolean | null;
notify?: boolean | null;
external_person_id?: string | null;
full_name?: string | null;
email?: string | null;
hide?: boolean | null;
priority?: boolean | null;
sort?: string | number | null;
group?: string | null;
enable?: boolean | null;
content?: string | null;
notes?: string | null;
}
export interface BbPostFormValues {
title: string;
topic_id: string | number | null;
anonymous: boolean | null;
notify: boolean | null;
external_person_id: string;
full_name: string;
email: string;
hide: boolean | null;
priority: boolean | null;
sort: string | number | null;
group: string;
enable: boolean | null;
}
export interface BbPostStaffNotificationSiteCfg {
novi_bb_base_url?: string | null;
noreply_email?: string | null;
noreply_name?: string | null;
admin_email?: string | null;
admin_name?: string | null;
}
export interface BbPostStaffNotificationEmail {
from_email: string;
from_name: string;
to_email: string;
to_name: string;
subject: string;
body_html: string;
}
const empty_identity_defaults: BbPostIdentityDefaults = {
external_person_id: null,
full_name: null,
email: null
};
function is_new_bb_post(source_obj: BbPostObjectLike = {}): boolean {
return !source_obj?.post_id && !source_obj?.id;
}
function normalize_identity_defaults(
identity_defaults: BbPostIdentityDefaults = empty_identity_defaults
): Required<BbPostIdentityDefaults> {
return {
external_person_id: identity_defaults.external_person_id ?? null,
full_name: identity_defaults.full_name ?? null,
email: identity_defaults.email ?? null
};
}
export function create_bb_post_form(
source_obj: BbPostObjectLike = {},
identity_defaults: BbPostIdentityDefaults = empty_identity_defaults
): BbPostFormValues {
const is_new_record = is_new_bb_post(source_obj);
const normalized_identity = normalize_identity_defaults(identity_defaults);
return {
title: source_obj.title ?? '',
topic_id: source_obj.topic_id != null ? Number(source_obj.topic_id) : '',
anonymous: source_obj.anonymous ?? null,
notify: source_obj.notify ?? null,
external_person_id:
source_obj.external_person_id ??
(is_new_record ? normalized_identity.external_person_id ?? '' : ''),
full_name:
source_obj.full_name ??
(is_new_record ? normalized_identity.full_name ?? '' : ''),
email:
source_obj.email ?? (is_new_record ? normalized_identity.email ?? '' : ''),
hide: source_obj.hide ?? null,
priority: source_obj.priority ?? null,
sort: source_obj.sort ?? null,
group: source_obj.group ?? '',
enable: source_obj.enable ?? null
};
}
interface BuildBbPostPayloadArgs {
post_di: key_val;
content_html: unknown;
notes_html: unknown;
original_post_obj?: BbPostObjectLike;
identity_defaults?: BbPostIdentityDefaults;
is_new_post: boolean;
account_id?: string | number | null;
}
export function build_bb_post_payload({
post_di,
content_html,
notes_html,
original_post_obj = {},
identity_defaults = empty_identity_defaults,
is_new_post,
account_id = null
}: BuildBbPostPayloadArgs): key_val {
const normalized_identity = normalize_identity_defaults(identity_defaults);
const post_do: key_val = {};
if (is_new_post) {
if (account_id !== null && account_id !== undefined) {
post_do.account_id = account_id;
}
post_do.enable = true;
}
post_do.title = post_di.title;
if (typeof content_html === 'string') {
post_do.content = content_html;
}
post_do.topic_id =
post_di.topic_id !== null && post_di.topic_id !== undefined && post_di.topic_id !== ''
? Number(post_di.topic_id)
: null;
post_do.anonymous = post_di.anonymous;
let identity_person_id: string | null = post_di.external_person_id || null;
let identity_full_name: string | null = post_di.full_name || null;
let identity_email: string | null = post_di.email || null;
if (is_new_post) {
identity_person_id =
identity_person_id || normalized_identity.external_person_id;
identity_full_name = identity_full_name || normalized_identity.full_name;
identity_email = identity_email || normalized_identity.email;
} else {
identity_person_id =
identity_person_id ?? original_post_obj.external_person_id ?? null;
identity_full_name = identity_full_name ?? original_post_obj.full_name ?? null;
identity_email = identity_email ?? original_post_obj.email ?? null;
}
post_do.external_person_id = identity_person_id;
post_do.full_name = identity_full_name;
post_do.email = identity_email;
post_do.notify = post_di.notify;
post_do.hide = post_di.hide;
post_do.priority = post_di.priority;
post_do.sort = post_di.sort ? Number(post_di.sort) : null;
post_do.group = post_di.group ? post_di.group : null;
if (typeof post_di.enable !== 'undefined') {
post_do.enable = post_di.enable;
}
if (typeof notes_html === 'string') {
post_do.notes = notes_html;
}
return post_do;
}
interface BuildBbPostStaffNotificationEmailArgs {
post_do: key_val;
url_origin: string;
site_cfg_json?: BbPostStaffNotificationSiteCfg | null;
}
export function build_bb_post_staff_notification_email({
post_do,
url_origin,
site_cfg_json
}: BuildBbPostStaffNotificationEmailArgs): BbPostStaffNotificationEmail {
const link_base_url =
site_cfg_json?.novi_bb_base_url ?? `${url_origin}/idaa/bb`;
const subject = `IDAA BB Post: ${post_do.title} (ID: ${post_do.post_id})`;
const body_html = `
<div>${post_do.full_name ?? '-- not set --'},
<p>A BB post has been created or updated named "${post_do.title}".</p>
</div>
<div>
Poster's Novi ID: ${post_do.external_person_id ?? '-- not set --'}<br>
Poster's Name: ${post_do.full_name ?? '-- not set --'}<br>
Poster's Email: ${post_do.email ?? '-- not set --'}
</div>
<br>
<div>
IDAA BB Post ID: ${post_do.post_id}<br>
<p>Use this link to view the post.<br>
Copy and paste link: <a href="${link_base_url}?post_id=${post_do.post_id}">${link_base_url}?post_id=${post_do.post_id}</a></p>
</div>`;
return {
from_email: site_cfg_json?.noreply_email ?? 'noreply+idaabb@oneskyit.com',
from_name: site_cfg_json?.noreply_name ?? 'IDAA BB NoReply',
to_email: site_cfg_json?.admin_email ?? 'admin+bbpost@oneskyit.com',
to_name: site_cfg_json?.admin_name ?? 'IDAA BB Admin',
subject,
body_html
};
}

View File

@@ -22,7 +22,7 @@ export interface Post {
external_person_id?: null | string; // For IDAA this is the Novi UUID
user_id?: null | string;
topic_id?: string;
topic_id?: number;
topic?: string; // or topic_name?
topic_name?: string;

View File

@@ -807,7 +807,7 @@ export interface ae_Archive extends ae_BaseObj {
account_id_random: string; // NO LONGER USE "_random"
archive_type?: string;
topic_id?: string;
topic_id?: number;
topic_name?: string;
content_html?: string;

View File

@@ -1,7 +1,12 @@
<script lang="ts">
interface AccountPageData {
slct?: {
post_id?: string | null;
};
}
interface Props {
/** @type {import('./$types').PageData} */
data: any;
data: { account_id: string } & Record<string, AccountPageData>;
}
let { data }: Props = $props();
@@ -17,17 +22,11 @@ import { untrack } from 'svelte';
import { liveQuery } from 'dexie';
// *** Import Aether specific variables and functions
// import type { key_val } from '$lib/ae_stores';
import { db_posts } from '$lib/ae_posts/db_posts';
import {
ae_snip,
ae_loc,
ae_sess,
ae_api,
ae_trig,
slct,
slct_trigger
slct
} from '$lib/stores/ae_stores';
import {
idaa_loc,
@@ -38,6 +37,7 @@ import {
} from '$lib/stores/ae_idaa_stores';
import { posts_func } from '$lib/ae_posts/ae_posts_functions';
import Comp__post_obj_id_edit from './ae_idaa_comp__post_obj_id_edit.svelte';
import Comp__post_obj_li from './ae_idaa_comp__post_obj_li.svelte';
// import Comp__post_obj_id_view from './ae_idaa_comp__post_obj_id_view.svelte';
import Comp__post_options from './ae_idaa_comp__post_options.svelte';
@@ -286,6 +286,11 @@ if (browser) {
<Comp__post_options />
{#if $idaa_sess.bb.edit__post_obj || $idaa_loc.bb.edit__post_obj}
<Comp__post_obj_id_edit
bind:obj_changed={$idaa_sess.bb.obj_changed} />
{/if}
<!-- <h1>Bulletin Board {$lq__post_obj_li?.length}</h1> -->
{#if $lq__post_obj_li && $lq__post_obj_li?.length}

View File

@@ -261,7 +261,6 @@ onDestroy(() => {
<!-- lq__post_comment_obj_li={lq__post_comment_obj_li} -->
<!-- lq__post_comment_obj={lq__post_comment_obj} -->
<Comp__post_obj_id_edit
{lq__post_obj}
bind:obj_changed={$idaa_sess.bb.obj_changed} />
{:else}
<Comp__post_obj_id_view

View File

@@ -1,14 +1,12 @@
<script lang="ts">
interface Props {
log_lvl?: number;
lq__post_obj: any;
// lq__post_comment_obj: any;
// lq__post_comment_obj_li?: any;
}
let {
log_lvl = $bindable(1),
lq__post_obj
// lq__post_comment_obj,
// lq__post_comment_obj_li
}: Props = $props();
@@ -23,15 +21,7 @@ import type { key_val } from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
// import { core_func } from '$lib/ae_core/ae_core_functions';
import { api } from '$lib/api/api';
import {
ae_snip,
ae_loc,
ae_sess,
ae_api,
ae_trig,
slct,
slct_trigger
} from '$lib/stores/ae_stores';
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
import { idaa_loc, idaa_sess, idaa_slct } from '$lib/stores/ae_idaa_stores';
import { posts_func } from '$lib/ae_posts/ae_posts_functions';
import AE_Comp_Editor_TipTap from '$lib/elements/element_editor_tiptap.svelte';
@@ -40,16 +30,20 @@ let prom_api__post_comment_obj: any = $state();
// SVELTE 5 STABILITY: Use local state for form bindings to prevent
// crashes when the global store object is briefly null.
const is_new_comment = !$idaa_slct.post_comment_id;
let post_comment_form = $state({
content: $idaa_slct.post_comment_obj?.content ?? '',
anonymous: $idaa_slct.post_comment_obj?.anonymous ?? false,
external_person_id:
$idaa_slct.post_comment_obj?.external_person_id ||
$idaa_loc.novi_uuid ||
'',
(is_new_comment ? $idaa_loc.novi_uuid || '' : ''),
full_name:
$idaa_slct.post_comment_obj?.full_name || $idaa_loc.novi_full_name || '',
email: $idaa_slct.post_comment_obj?.email || $idaa_loc.novi_email || '',
$idaa_slct.post_comment_obj?.full_name ||
(is_new_comment ? $idaa_loc.novi_full_name || '' : ''),
email:
$idaa_slct.post_comment_obj?.email ||
(is_new_comment ? $idaa_loc.novi_email || '' : ''),
enable: $idaa_slct.post_comment_obj?.enable ?? true,
hide: $idaa_slct.post_comment_obj?.hide ?? false,
priority: $idaa_slct.post_comment_obj?.priority ?? false,
@@ -99,23 +93,31 @@ async function handle_submit_form(event: any) {
post_comment_do['content'] = content_new_html;
post_comment_do['anonymous'] = post_comment_form.anonymous;
// Robust scavenging of identity from storage if form state is missing it
// Preserve the original creator UUID on edits. Only use the current Novi identity
// when creating a brand-new comment draft.
let novi_uuid = post_comment_form.external_person_id;
let novi_full_name = post_comment_form.full_name;
let novi_email = post_comment_form.email;
if (!novi_uuid && typeof localStorage !== 'undefined') {
try {
const ls_val = localStorage.getItem('ae_idaa_loc');
if (ls_val) {
const ls_json = JSON.parse(ls_val);
novi_uuid = ls_json.novi_uuid;
novi_full_name = ls_json.novi_full_name;
novi_email = ls_json.novi_email;
if (!$idaa_slct.post_comment_id) {
if (!novi_uuid && typeof localStorage !== 'undefined') {
try {
const ls_val = localStorage.getItem('ae_idaa_loc');
if (ls_val) {
const ls_json = JSON.parse(ls_val);
novi_uuid = ls_json.novi_uuid;
novi_full_name = ls_json.novi_full_name;
novi_email = ls_json.novi_email;
}
} catch {
/* ignore */
}
} catch (e) {
/* ignore */
}
} else {
novi_uuid = novi_uuid || $idaa_slct.post_comment_obj?.external_person_id || null;
novi_full_name =
novi_full_name || $idaa_slct.post_comment_obj?.full_name || null;
novi_email = novi_email || $idaa_slct.post_comment_obj?.email || null;
}
post_comment_do['external_person_id'] = novi_uuid;
@@ -595,9 +597,7 @@ Copy and paste link: <a href="${link_base_url}?post_id=${$idaa_slct.post_id}">${
type="text"
id="external_person_id"
name="external_person_id"
value={post_comment_form.external_person_id
? post_comment_form.external_person_id
: $idaa_loc.novi_uuid}
bind:value={post_comment_form.external_person_id}
readonly={true}
class="input form-control w-96" />
{:else}
@@ -613,9 +613,7 @@ Copy and paste link: <a href="${link_base_url}?post_id=${$idaa_slct.post_id}">${
type="text"
id="external_person_id"
name="external_person_id"
value={post_comment_form.external_person_id
? post_comment_form.external_person_id
: ''}
bind:value={post_comment_form.external_person_id}
readonly={false}
class="input form-control w-96" />
{/if}
@@ -640,9 +638,7 @@ Copy and paste link: <a href="${link_base_url}?post_id=${$idaa_slct.post_id}">${
type="text"
id="full_name"
name="full_name"
value={post_comment_form.full_name
? post_comment_form.full_name
: $idaa_loc.novi_full_name}
bind:value={post_comment_form.full_name}
readonly={!$ae_loc.trusted_access}
class="input form-control w-96" />
</label>
@@ -666,9 +662,7 @@ Copy and paste link: <a href="${link_base_url}?post_id=${$idaa_slct.post_id}">${
<input
type="text"
name="email"
value={post_comment_form.email
? post_comment_form.email
: $idaa_loc.novi_email}
bind:value={post_comment_form.email}
readonly={!$ae_loc.trusted_access}
class="input form-control w-96" />
<span
@@ -681,9 +675,7 @@ Copy and paste link: <a href="${link_base_url}?post_id=${$idaa_slct.post_id}">${
<input
type="hidden"
name="email"
value={post_comment_form.email
? post_comment_form.email
: $idaa_loc.novi_email} />
value={post_comment_form.email} />
{/if}
</div>
<!-- END: section post__general_information -->

View File

@@ -1,13 +1,11 @@
<script lang="ts">
interface Props {
log_lvl?: number;
lq__post_obj: any;
obj_changed: boolean;
}
let {
log_lvl = $bindable(1),
lq__post_obj,
obj_changed = $bindable(false)
}: Props = $props();
@@ -24,29 +22,72 @@ import type { key_val } from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
import { core_func } from '$lib/ae_core/ae_core_functions';
import { api } from '$lib/api/api';
import {
ae_snip,
ae_loc,
ae_sess,
ae_api,
ae_trig,
slct,
slct_trigger
} from '$lib/stores/ae_stores';
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
import { idaa_loc, idaa_sess, idaa_slct } from '$lib/stores/ae_idaa_stores';
import { posts_func } from '$lib/ae_posts/ae_posts_functions';
import {
build_bb_post_payload,
build_bb_post_staff_notification_email,
create_bb_post_form,
type BbPostIdentityDefaults,
type BbPostObjectLike
} from '$lib/ae_idaa/ae_idaa__bb_post_helpers';
import AE_Comp_Editor_TipTap from '$lib/elements/element_editor_tiptap.svelte';
import Comp_hosted_files_upload from '$lib/ae_core/ae_comp__hosted_files_upload.svelte';
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';
interface BbHostedFileObjLike {
hosted_file_id?: string | null;
id?: string | null;
filename?: string | null;
extension?: string | null;
file_extension?: string | null;
[key: string]: unknown;
}
// let obj_changed = $state(false);
// let orig_post_obj: any = $state(null);
// let orig_post_obj: any = $state({ ...$idaa_slct.post_obj }); // Create a copy of the post object
if (!$idaa_slct.post_obj) {
$idaa_slct.post_obj = {};
}
const initial_post_obj: BbPostObjectLike = $idaa_slct.post_obj ?? {};
function get_current_identity_defaults(): BbPostIdentityDefaults {
const identity_defaults: BbPostIdentityDefaults = {
external_person_id: $idaa_loc.novi_uuid ?? null,
full_name: $idaa_loc.novi_full_name ?? null,
email: $idaa_loc.novi_email ?? null
};
if (!identity_defaults.external_person_id && typeof localStorage !== 'undefined') {
try {
const ls_val = localStorage.getItem('ae_idaa_loc');
if (ls_val) {
const ls_json = JSON.parse(ls_val);
identity_defaults.external_person_id = ls_json.novi_uuid ?? null;
identity_defaults.full_name =
identity_defaults.full_name || ls_json.novi_full_name || null;
identity_defaults.email =
identity_defaults.email || ls_json.novi_email || null;
}
} catch {
/* ignore */
}
}
return identity_defaults;
}
const initial_identity_defaults = get_current_identity_defaults();
let post_form = $state(create_bb_post_form(initial_post_obj, initial_identity_defaults));
let orig_post_form = $state(create_bb_post_form(initial_post_obj, initial_identity_defaults));
let force_saveable_new_post = $state(false);
// Create a copy of the post object
let orig_post_obj: any = { ...$idaa_slct.post_obj };
let orig_post_obj: BbPostObjectLike = { ...initial_post_obj };
if (browser) {
// console.log(`$lq__post_obj = `, $lq__post_obj);
console.log(`$idaa_slct.post_obj = `, $idaa_slct.post_obj);
@@ -58,18 +99,19 @@ if (browser) {
if ($idaa_loc.bb.edit__post_obj) {
obj_changed = true; // This is an odd workaround to make new posts saveable.
force_saveable_new_post = true;
$idaa_sess.bb.edit__post_obj = $idaa_loc.bb.edit__post_obj;
$idaa_loc.bb.edit__post_obj = false;
}
let ae_promises: key_val = $state({});
let prom_api__post_obj: any = $state();
let prom_api__post_obj: Promise<unknown> | undefined = $state();
// let prom_api__post_obj__hosted_file: any;
let content_new_html = $state($idaa_slct.post_obj?.content ?? '');
let notes_new_html = $state($idaa_slct.post_obj?.notes ?? '');
let content_new_html = $state(initial_post_obj?.content ?? '');
let notes_new_html = $state(initial_post_obj?.notes ?? '');
let hosted_file_id_li = $state<string[]>([]); // Array of hosted file IDs
let hosted_file_obj_li = $state<any[]>([]); // Array of hosted file objects
let hosted_file_obj_li = $state<BbHostedFileObjLike[]>([]); // Array of hosted file objects
let upload_complete = $state(false);
let disable_submit_btn = $state(false);
@@ -80,152 +122,78 @@ function prevent_default<T extends Event>(fn: (event: T) => void) {
};
}
async function handle_submit_form(event: any) {
async function handle_submit_form(event: SubmitEvent) {
if (log_lvl > 1) {
console.log('*** handle_submit_form() ***', event.target);
}
const form = event.currentTarget;
if (!(form instanceof HTMLFormElement)) {
return false;
}
disable_submit_btn = true;
let form_data = new FormData(event.target);
const form_data = new FormData(form);
if (log_lvl) {
console.log(form_data);
}
// Form Post object data incoming
let post_di = ae_util.extract_prefixed_form_data({
const post_di = ae_util.extract_prefixed_form_data({
prefix: null,
form_data: form_data,
form_data,
trim_values: true,
bool_tf_str: true,
log_lvl: log_lvl
log_lvl
});
// console.log(post_di);
// Form Post object data outgoing
let post_do: key_val = {};
if (!$idaa_slct.post_id) {
// V3 Standard: Use clean ID field name for isolation
post_do['account_id'] = $ae_loc.account_id;
post_do['enable'] = true;
}
post_do['title'] = post_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_do['content'] = content_new_html;
} else {
console.log('New content is not a string. Do nothing.');
// post_do['content'] = event_meeting_fd.content;
}
// Change this to a string later? Or use the group field instead?
if (post_di.topic_id) {
post_do['topic_id'] = Number(post_di.topic_id);
} else {
post_do['topic_id'] = null;
}
post_do['anonymous'] = post_di.anonymous;
// Robust scavenging: the form input falls back to $idaa_loc.novi_uuid at render
// time, but if the store was null during that render (race condition on mount) the
// input would have captured an empty string. Re-check the store and then
// localStorage so legacy posts without an external_person_id never overwrite with ''.
let identity_person_id: string | null = post_di.external_person_id || null;
let identity_full_name: string | null = post_di.full_name || null;
let identity_email: string | null = post_di.email || null;
if (!identity_person_id) {
identity_person_id = $idaa_loc.novi_uuid ?? null;
identity_full_name = identity_full_name || $idaa_loc.novi_full_name || null;
identity_email = identity_email || $idaa_loc.novi_email || null;
}
if (!identity_person_id && typeof localStorage !== 'undefined') {
try {
const ls_val = localStorage.getItem('ae_idaa_loc');
if (ls_val) {
const ls_json = JSON.parse(ls_val);
identity_person_id = ls_json.novi_uuid ?? null;
identity_full_name = identity_full_name || ls_json.novi_full_name || null;
identity_email = identity_email || ls_json.novi_email || null;
}
} catch (e) { /* ignore */ }
}
post_do['external_person_id'] = identity_person_id;
post_do['full_name'] = identity_full_name;
post_do['email'] = identity_email;
post_do['notify'] = post_di.notify;
post_do['hide'] = post_di.hide;
post_do['priority'] = post_di.priority;
if (post_di.sort) {
post_do['sort'] = Number(post_di.sort);
} else {
post_do['sort'] = null;
}
if (post_di.group) {
post_do['group'] = post_di.group;
} else {
post_do['group'] = null;
}
// NOTE: We only want to set enable value if the input is defined.
if (typeof post_di.enable !== 'undefined') {
post_do.enable = post_di.enable;
}
// NOTE: We want to always default to false if the input is not defined.
// post_do['enable'] = !!post_di.enable;
console.log(`post_di.enable = ${post_di.enable}`);
console.log(`post_do.enable = ${post_do.enable}`);
// Check if the notes_new_html exists and is a string
if (typeof notes_new_html === 'string') {
console.log('New notes is a string');
post_do['notes'] = notes_new_html;
} else {
console.log('New notes is not a string. Do nothing.');
// post_do['notes'] = event_meeting_fd.notes;
}
const is_new_post = !$idaa_slct.post_id;
const post_do = build_bb_post_payload({
post_di,
content_html: content_new_html,
notes_html: notes_new_html,
original_post_obj: orig_post_obj,
identity_defaults: get_current_identity_defaults(),
is_new_post,
account_id: $ae_loc.account_id
});
if (log_lvl) {
console.log(post_do);
}
if (!$idaa_slct.post_id) {
if (is_new_post) {
prom_api__post_obj = posts_func
.create_ae_obj__post({
api_cfg: $ae_api,
account_id: $ae_loc.account_id,
data_kv: post_do,
log_lvl: log_lvl
log_lvl
})
.then(function (post_obj_create_result) {
if (!post_obj_create_result) {
console.log('The result was null or false.');
return false;
} else {
if (log_lvl) {
console.log(
'post_obj_create_result:',
post_obj_create_result
);
}
}
$idaa_slct.post_id = post_obj_create_result.post_id;
$idaa_slct.post_obj = post_obj_create_result;
if (log_lvl) {
console.log('post_obj_create_result:', post_obj_create_result);
}
if ($ae_loc.site_cfg_json?.bb_send_staff_new_email) {
send_staff_notification_email(prom_api__post_obj);
}
$idaa_slct.post_id = post_obj_create_result.post_id;
$idaa_slct.post_obj = post_obj_create_result;
if ($ae_loc.site_cfg_json?.bb_send_staff_new_email) {
send_staff_notification_email(post_obj_create_result);
}
if (post_obj_create_result.post_id) {
goto(`/idaa/bb/${post_obj_create_result.post_id}`);
}
return post_obj_create_result;
})
.catch(function (error: any) {
.catch(function (error: unknown) {
console.log('Something went wrong.');
console.log(error);
return false;
@@ -235,49 +203,47 @@ async function handle_submit_form(event: any) {
$idaa_sess.bb.edit__post_obj = false;
});
return prom_api__post_obj;
} else {
prom_api__post_obj = posts_func
.update_ae_obj__post({
api_cfg: $ae_api,
post_id: $idaa_slct.post_id,
data_kv: post_do,
log_lvl: log_lvl
})
.then(function (post_obj_update_result) {
if (!post_obj_update_result) {
console.log('The result was null or false.');
return false;
} else {
if (log_lvl) {
console.log(
'post_obj_update_result:',
post_obj_update_result
);
}
$idaa_slct.post_obj = post_obj_update_result;
if ($ae_loc.site_cfg_json?.bb_send_staff_update_email) {
send_staff_notification_email(post_obj_update_result);
}
}
return post_obj_update_result;
})
.catch(function (error: any) {
console.log('Something went wrong.');
console.log(error);
return false;
})
.finally(() => {
disable_submit_btn = false;
$idaa_sess.bb.edit__post_obj = false;
});
console.log(`prom_api__post_obj = `, prom_api__post_obj);
return prom_api__post_obj;
}
prom_api__post_obj = posts_func
.update_ae_obj__post({
api_cfg: $ae_api,
post_id: $idaa_slct.post_id,
data_kv: post_do,
log_lvl
})
.then(function (post_obj_update_result) {
if (!post_obj_update_result) {
console.log('The result was null or false.');
return false;
}
if (log_lvl) {
console.log('post_obj_update_result:', post_obj_update_result);
}
$idaa_slct.post_obj = post_obj_update_result;
if ($ae_loc.site_cfg_json?.bb_send_staff_update_email) {
send_staff_notification_email(post_obj_update_result);
}
return post_obj_update_result;
})
.catch(function (error: unknown) {
console.log('Something went wrong.');
console.log(error);
return false;
})
.finally(() => {
disable_submit_btn = false;
$idaa_sess.bb.edit__post_obj = false;
});
console.log(`prom_api__post_obj = `, prom_api__post_obj);
return prom_api__post_obj;
}
// Updated 2024-11-15
@@ -286,7 +252,7 @@ async function handle_delete_post_obj({
method = 'disable'
}: {
post_id: string;
method?: string;
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
}) {
if (log_lvl) {
console.log('*** handle_delete_post_obj() ***');
@@ -296,15 +262,15 @@ async function handle_delete_post_obj({
.delete_ae_obj_id__post({
api_cfg: $ae_api,
post_id: post_id,
method: method as any,
method,
log_lvl: log_lvl
})
.then(function (post_obj_delete_result) {
.then(function () {
// $idaa_sess.bb.show__modal_view__post_id = false;
$idaa_sess.bb.edit__post_obj = false;
// $idaa_sess.bb.show__inline_edit__post_comment_id = false;
})
.catch(function (error: any) {
.catch(function (error: unknown) {
console.log(
'The result was null or false when trying to delete.',
error
@@ -324,7 +290,7 @@ async function handle_delete_post_obj({
async function handle_hosted_files_uploaded(
hosted_file_id_li: string[],
hosted_file_obj_li: any[]
hosted_file_obj_li: BbHostedFileObjLike[]
) {
console.log(`*** handle_hosted_files_uploaded() *** ${hosted_file_id_li}`);
@@ -352,11 +318,11 @@ async function handle_hosted_files_uploaded(
},
log_lvl: log_lvl
})
.then(function (post_obj_update_result) {
.then(function () {
// $idaa_slct.post_obj = $lq__post_obj;
$idaa_slct.post_obj.linked_li_json = new_linked_li_json;
})
.catch(function (error: any) {
.catch(function (error: unknown) {
console.log('Something went wrong.');
console.log(error);
return false;
@@ -372,64 +338,45 @@ function send_staff_notification_email(post_do: key_val) {
);
}
let link_base_url =
$ae_loc.site_cfg_json.novi_bb_base_url ??
`${$ae_loc.url_origin}/idaa/bb`;
let subject = `IDAA BB Post: ${post_do.title} (ID: ${post_do.post_id})`;
let body_html = `
<div>${post_do.full_name ?? '-- not set --'},
<p>A BB post has been created or updated named "${post_do.title}".</p>
</div>
<div>
Poster's Novi ID: ${post_do.external_person_id ?? '-- not set --'}<br>
Poster's Name: ${post_do.full_name ?? '-- not set --'}<br>
Poster's Email: ${post_do.email ?? '-- not set --'}
</div>
<br>
<div>
IDAA BB Post ID: ${post_do.post_id}<br>
<p>Use this link to view the post.<br>
Copy and paste link: <a href="${link_base_url}?post_id=${post_do.post_id}">${link_base_url}?post_id=${post_do.post_id}</a></p>
</div>`;
const email_args = build_bb_post_staff_notification_email({
post_do,
url_origin: $ae_loc.url_origin,
site_cfg_json: $ae_loc.site_cfg_json
});
api.send_email({
api_cfg: $ae_api,
from_email:
$ae_loc.site_cfg_json?.noreply_email ??
'noreply+idaabb@oneskyit.com',
// from_email: 'noreply+idaabb@oneskyit.com',
from_name: $ae_loc.site_cfg_json?.noreply_name ?? 'IDAA BB NoReply',
to_email:
$ae_loc.site_cfg_json?.admin_email ?? 'admin+bbpost@oneskyit.com',
// to_email: 'test+idaabb@oneskyit.com', // 'scott+idaabb@oneskyit.com'
to_name: $ae_loc.site_cfg_json?.admin_name ?? 'IDAA BB Admin',
subject: subject,
body_html: body_html
...email_args
});
}
$effect(() => {
// Initialization Layer
if (!orig_post_obj?.id && $idaa_slct.post_obj?.id) {
const current_post_id =
$idaa_slct.post_obj?.post_id ?? $idaa_slct.post_obj?.id ?? null;
const original_post_id =
orig_post_obj?.post_id ?? orig_post_obj?.id ?? null;
if (current_post_id && current_post_id !== original_post_id) {
orig_post_obj = { ...$idaa_slct.post_obj };
post_form = create_bb_post_form(
$idaa_slct.post_obj,
get_current_identity_defaults()
);
orig_post_form = create_bb_post_form(
$idaa_slct.post_obj,
get_current_identity_defaults()
);
content_new_html = $idaa_slct.post_obj?.content ?? '';
notes_new_html = $idaa_slct.post_obj?.notes ?? '';
}
if (
orig_post_obj === null ||
orig_post_obj === undefined ||
orig_post_obj === 'undefined'
) {
obj_changed = false;
if (force_saveable_new_post) {
obj_changed = true;
} else if (
!obj_changed &&
orig_post_obj?.id &&
(JSON.stringify($idaa_slct.post_obj) !==
JSON.stringify(orig_post_obj) ||
(JSON.stringify(post_form) !== JSON.stringify(orig_post_form) ||
content_new_html !== (orig_post_obj.content ?? '') ||
notes_new_html !== (orig_post_obj.notes ?? ''))
) {
@@ -445,7 +392,7 @@ $effect(() => {
} else if (
obj_changed &&
orig_post_obj?.id &&
JSON.stringify($idaa_slct.post_obj) === JSON.stringify(orig_post_obj) &&
JSON.stringify(post_form) === JSON.stringify(orig_post_form) &&
content_new_html === (orig_post_obj.content ?? '') &&
notes_new_html === (orig_post_obj.notes ?? '')
) {
@@ -539,7 +486,7 @@ $effect(() => {
name="title"
required
max="200"
bind:value={$idaa_slct.post_obj.title}
bind:value={post_form.title}
autocomplete="off"
class="
input preset-tonal-surface
@@ -708,10 +655,7 @@ $effect(() => {
log_lvl: log_lvl
}
)
.then(
function (
delete_result
) {
.then(function () {
// Second - If deleted, then update the post_obj
console.log(
`File removed. Now update the post_obj`
@@ -721,7 +665,7 @@ $effect(() => {
let delete_result_li =
$idaa_slct.post_obj.linked_li_json.filter(
function (
hosted_file_obj: any
hosted_file_obj: BbHostedFileObjLike
) {
const current_id =
hosted_file_obj?.hosted_file_id ||
@@ -763,18 +707,14 @@ $effect(() => {
log_lvl
}
)
.then(
function (
post_obj_update_result
) {
.then(function () {
// We need to do all of this since the DB object has changed and the SLCT object does automatically update (yet...??? Svelte 5?).
// $idaa_slct.post_obj = $lq__post_obj;
}
)
.catch(
function (
error: any
) {
.catch(function (
error: unknown
) {
console.log(
'Something went wrong.'
);
@@ -787,7 +727,7 @@ $effect(() => {
}
)
.catch(function (
error: any
error: unknown
) {
console.log(
'Something went wrong.'
@@ -829,7 +769,7 @@ $effect(() => {
form-control col-sm-12
w-96 px-1
"
bind:value={$idaa_slct.post_obj.topic_id}>
bind:value={post_form.topic_id}>
<option value="">-- None --</option>
<option value={16}
>Licensing/ monitoring/ credentialing issues</option>
@@ -859,7 +799,7 @@ $effect(() => {
id="anonymous_no"
name="anonymous"
value={false}
bind:group={$idaa_slct.post_obj.anonymous}
bind:group={post_form.anonymous}
class="radio form-check-input" />
<label for="anonymous_no" class="form-check-label"
><strong>No</strong>, include my name and email address</label>
@@ -871,7 +811,7 @@ $effect(() => {
id="anonymous_yes"
name="anonymous"
value={true}
bind:group={$idaa_slct.post_obj.anonymous}
bind:group={post_form.anonymous}
class="radio form-check-input" />
<label for="anonymous_yes" class="form-check-label"
><strong>Yes</strong>, the post will be listed as
@@ -893,9 +833,7 @@ $effect(() => {
type="text"
id="external_person_id"
name="external_person_id"
value={$idaa_slct.post_obj.external_person_id
? $idaa_slct.post_obj.external_person_id
: $idaa_loc.novi_uuid}
bind:value={post_form.external_person_id}
readonly={true}
class="
input preset-tonal-surface hover:preset-filled-surface-100-900 form-control w-96
@@ -913,9 +851,7 @@ $effect(() => {
type="text"
id="external_person_id"
name="external_person_id"
value={$idaa_slct.post_obj?.external_person_id
? $idaa_slct.post_obj?.external_person_id
: ($idaa_loc.novi_uuid ?? '')}
bind:value={post_form.external_person_id}
readonly={!$ae_loc.administrator_access}
class="input preset-tonal-surface hover:preset-filled-surface-100-900 form-control w-96" />
{/if}
@@ -940,9 +876,7 @@ $effect(() => {
type="text"
id="full_name"
name="full_name"
value={$idaa_slct.post_obj?.full_name
? $idaa_slct?.post_obj.full_name
: ($idaa_loc.novi_full_name ?? '')}
bind:value={post_form.full_name}
readonly={!$ae_loc.trusted_access}
class="input preset-tonal-surface hover:preset-filled-surface-100-900 form-control w-96" />
</label>
@@ -967,9 +901,7 @@ $effect(() => {
<input
type="email"
name="email"
value={$idaa_slct.post_obj?.email
? $idaa_slct.post_obj?.email
: ($idaa_loc.novi_email ?? '')}
bind:value={post_form.email}
readonly={!$ae_loc.trusted_access}
class="input preset-tonal-surface hover:preset-filled-surface-100-900 form-control w-96" />
<span class="text-surface-600-400 text-xs italic">
@@ -999,7 +931,7 @@ $effect(() => {
id="notify_no"
name="notify"
value={false}
bind:group={$idaa_slct.post_obj.notify}
bind:group={post_form.notify}
class="radio form-check-input" />
<label for="notify_no"
><strong>No</strong>, do not notify me of comments</label>
@@ -1011,7 +943,7 @@ $effect(() => {
id="notify_yes"
name="notify"
value={true}
bind:group={$idaa_slct.post_obj.notify}
bind:group={post_form.notify}
class="radio form-check-input" />
<label for="notify_yes"
><strong>Yes</strong>, notify me of comments</label>
@@ -1060,7 +992,7 @@ $effect(() => {
id="hide_yes"
name="hide"
value={true}
bind:group={$idaa_slct.post_obj.hide}
bind:group={post_form.hide}
class="radio form-check-input" />
<label for="hide_yes">Yes</label>
</div>
@@ -1070,7 +1002,7 @@ $effect(() => {
id="hide_no"
name="hide"
value={false}
bind:group={$idaa_slct.post_obj.hide}
bind:group={post_form.hide}
class="radio form-check-input" />
<label for="hide_no">No</label>
</div>
@@ -1087,9 +1019,7 @@ $effect(() => {
id="priority_yes"
name="priority"
value={true}
bind:group={
$idaa_slct.post_obj.priority
}
bind:group={post_form.priority}
class="radio form-check-input" />
<label for="priority_yes">Yes</label>
</div>
@@ -1099,9 +1029,7 @@ $effect(() => {
id="priority_no"
name="priority"
value={false}
bind:group={
$idaa_slct.post_obj.priority
}
bind:group={post_form.priority}
class="radio form-check-input" />
<label for="priority_no">No</label>
</div>
@@ -1119,7 +1047,7 @@ $effect(() => {
>Sort <input
type="number"
name="sort"
bind:value={$idaa_slct.post_obj.sort}
bind:value={post_form.sort}
class="input preset-tonal-surface form-control w-24" /></label>
<label
@@ -1127,7 +1055,7 @@ $effect(() => {
>Group <input
type="text"
name="group"
bind:value={$idaa_slct.post_obj.group}
bind:value={post_form.group}
max="100"
class="input preset-tonal-surface form-control w-40" /></label>
</span>
@@ -1146,9 +1074,7 @@ $effect(() => {
id="enable_yes"
name="enable"
value={true}
bind:group={
$idaa_slct.post_obj.enable
}
bind:group={post_form.enable}
class="radio form-check-input" />
<label for="enable_yes">Yes</label>
</div>
@@ -1158,9 +1084,7 @@ $effect(() => {
id="enable_no"
name="enable"
value={false}
bind:group={
$idaa_slct.post_obj.enable
}
bind:group={post_form.enable}
class="radio form-check-input" />
<label for="enable_no">No</label>
</div>

View File

@@ -1,38 +1,9 @@
<script lang="ts">
interface Props {
log_lvl?: number;
}
let { log_lvl = 0 }: Props = $props();
// *** Import Svelte specific
import { goto } from '$app/navigation';
// *** Import other supporting libraries
// *** Import Aether specific variables and functions
// import type { key_val } from '$lib/ae_stores';
import {
ae_snip,
ae_loc,
ae_sess,
ae_api,
ae_trig,
slct,
slct_trigger
} from '$lib/stores/ae_stores';
import {
idaa_loc,
idaa_sess,
idaa_slct,
idaa_trig,
idaa_prom
} from '$lib/stores/ae_idaa_stores';
import { posts_func } from '$lib/ae_posts/ae_posts_functions';
import { ae_loc } from '$lib/stores/ae_stores';
import { idaa_loc, idaa_sess, idaa_slct, idaa_trig } from '$lib/stores/ae_idaa_stores';
import Help_tech from '$lib/app_components/e_app_help_tech.svelte';
let creating = $state(false); // true while create API call is in-flight
// let ae_promises: key_val = {};
// let ae_tmp: key_val = {};
// let ae_trigger: any = null;
@@ -174,84 +145,20 @@ let creating = $state(false); // true while create API call is in-flight
{#if ($ae_loc.trusted_access && $ae_loc.edit_mode) || $idaa_loc.novi_uuid}
<button
type="button"
disabled={creating || !$ae_loc.authenticated_access}
disabled={!$ae_loc.authenticated_access || $idaa_loc.bb.edit__post_obj || $idaa_sess.bb.edit__post_obj}
onclick={() => {
if (!confirm('Create new post?')) {
if (
!confirm(
'Open a new draft post? It will not be saved until you press Save Changes.'
)
) {
return false;
}
creating = true;
$idaa_slct.post_id = null;
$idaa_slct.post_obj = {};
// Robust scavenging of identity from storage if store is null
let novi_uuid = $idaa_loc.novi_uuid;
let novi_full_name = $idaa_loc.novi_full_name;
let novi_email = $idaa_loc.novi_email;
if (!novi_uuid && typeof localStorage !== 'undefined') {
try {
const ls_val = localStorage.getItem('ae_idaa_loc');
if (ls_val) {
const ls_json = JSON.parse(ls_val);
novi_uuid = ls_json.novi_uuid;
novi_full_name = ls_json.novi_full_name;
novi_email = ls_json.novi_email;
}
} catch (e) {
/* ignore */
}
}
let data_kv = {
external_person_id: novi_uuid,
title: '',
full_name: novi_full_name,
email: novi_email,
enable: true
};
if (log_lvl) {
console.log('Creating new post with data_kv:', data_kv);
}
posts_func
.create_ae_obj__post({
api_cfg: $ae_api,
account_id: $ae_loc.account_id,
data_kv: data_kv,
log_lvl: log_lvl
})
.then((results) => {
if (log_lvl) {
console.log('New post created:', results);
}
// 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 = clean_post_id;
$idaa_loc.bb.edit__post_obj = clean_post_id;
})
.catch((error) => {
console.error('Error creating post:', error);
creating = false; // Re-enable so user can retry
alert('Failed to create post.');
})
.finally(() => {
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 {
// .catch() already alerted the user — just reset the flag.
creating = false;
}
});
$idaa_sess.bb.obj_changed = true;
$idaa_loc.bb.edit__post_obj = true;
}}
class="
novi_btn novi_white btn-tertiary
@@ -260,12 +167,8 @@ let creating = $state(false); // true while create API call is in-flight
preset-outlined-warning-200-800 hover:preset-filled-warning-200-800 text-xs
transition
"
title="Create new post">
{#if creating}
<span class="fas fa-spinner fa-spin m-1"></span> Creating...
{:else}
<span class="fas fa-plus m-1"></span> Create New Post
{/if}
title="Open a new draft post">
<span class="fas fa-plus m-1"></span> Create New Post
</button>
{/if}
</div>