From 5056d5d8f0251a5fe93dca93360ab9ca0a6e1d17 Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Fri, 9 Jan 2026 15:14:36 -0500 Subject: [PATCH] Standardize Core UI forms and unify schemas for V3 API compatibility - Implement new Svelte 5 Person, Address, and Contact form components with surgical payload logic. - Refactor core routes (People, Addresses, Contacts) to support unified Create/Edit workflows. - Update ae_types.ts, db_core.ts, and db_journals.ts to align with V3 backend object models. - Fix type safety issues in Journal history views and refine metadata display. - Migrate person core functions to the newer ae_core__person module. --- TODO.md | 5 +- src/lib/ae_core/ae_core_functions.ts | 2 +- src/lib/ae_core/db_core.ts | 221 ++----- src/lib/ae_events/ae_events__event.ts | 25 +- src/lib/ae_journals/db_journals.ts | 557 +++--------------- src/lib/types/ae_types.ts | 51 ++ src/routes/core/addresses/+page.svelte | 48 +- .../core/addresses/[address_id]/+page.svelte | 184 +++--- .../addresses/ae_comp__address_form.svelte | 258 ++++++++ src/routes/core/contacts/+page.svelte | 48 +- .../core/contacts/[contact_id]/+page.svelte | 197 ++++--- .../contacts/ae_comp__contact_form.svelte | 240 ++++++++ src/routes/core/people/+page.svelte | 40 +- .../core/people/[person_id]/+page.svelte | 51 +- .../core/people/ae_comp__person_form.svelte | 262 ++++++++ .../ae_idaa_comp__event_obj_id_edit.svelte | 2 +- .../journals/JournalEntry_Metadata.svelte | 18 - .../journals/[journal_id]/+layout.svelte | 11 +- 18 files changed, 1276 insertions(+), 944 deletions(-) create mode 100644 src/routes/core/addresses/ae_comp__address_form.svelte create mode 100644 src/routes/core/contacts/ae_comp__contact_form.svelte create mode 100644 src/routes/core/people/ae_comp__person_form.svelte diff --git a/TODO.md b/TODO.md index 0d770e53..103b8834 100644 --- a/TODO.md +++ b/TODO.md @@ -16,10 +16,11 @@ This is a list of tasks to be completed before the next event/show/conference. *Goal: Transform Journals into the platform flagship with premium UI/UX and robust security.* - [ ] **Phase 1: Codebase Audit & Schema** - - [ ] Audit `src/lib/ae_journals` and `src/routes/journals` for Tailwind compliance and code quality. + - [/] Audit `src/lib/ae_journals` and `src/routes/journals` for Tailwind compliance and code quality. - [x] Refactor `ae_comp__journal_entry_obj_id_view.svelte` (Header, Editor, Settings extracted). + - [x] Fix type safety in `[journal_id]/+layout.svelte` (Recent entries history view). - [ ] Verify `db_journals.ts` schema for metadata and encryption support. - - [ ] Identify and replace non-standard CSS with Tailwind utility classes. + - [/] Identify and replace non-standard CSS with Tailwind utility classes. - [x] Update `db_journals.ts` types using the new `agents_sync` exported interfaces. - [ ] **Phase 2: UI/UX Excellence** - [ ] Implement "Quick Add" for high-velocity entry. diff --git a/src/lib/ae_core/ae_core_functions.ts b/src/lib/ae_core/ae_core_functions.ts index eb3763b3..63e0e70d 100644 --- a/src/lib/ae_core/ae_core_functions.ts +++ b/src/lib/ae_core/ae_core_functions.ts @@ -17,7 +17,7 @@ import { delete_ae_obj_id__person, update_ae_obj__person // db_save_ae_obj_li__person -} from '$lib/ae_core/core__person'; +} from '$lib/ae_core/ae_core__person'; import { auth_ae_obj__username_password, diff --git a/src/lib/ae_core/db_core.ts b/src/lib/ae_core/db_core.ts index ea18d732..0e395f2d 100644 --- a/src/lib/ae_core/db_core.ts +++ b/src/lib/ae_core/db_core.ts @@ -1,208 +1,91 @@ import Dexie, { type Table } from 'dexie'; +import type { + ae_HostedFile, + ae_Person, + ae_User, + ae_Account, + ae_Site, + ae_SiteDomain, + ae_Address, + ae_Contact +} from '$lib/types/ae_types'; // li = list // kv = key value list -// Updated 2025-01-07 -export interface File { - id: string; - id_random: string; - hosted_file_id: string; - hosted_file_id_random: string; - - hash_sha256: string; - - for_type?: string; - for_id?: string; - for_id_random?: string; - - account_id: string; - - filename: string; - extension: string; - content_type: string; - size: number; // In bytes - - enable: null | boolean; - hide?: null | boolean; - priority?: null | boolean; - sort?: null | number; - group?: null | string; - notes?: null | string; - created_on: Date; - updated_on?: null | Date; - - // Additional fields for convenience (database views) - filename_no_ext: string; - filename_w_ext: string; +// Updated 2026-01-09 - Unified Types +export interface File extends ae_HostedFile { + // Legacy mapping support + content_type?: string; + filename_no_ext?: string; + filename_w_ext?: string; } -// Updated 2024-07-17 -export interface Person { - id: string; - // id_random: string; - person_id: string; - person_id_random: string; - - external_id?: string; // This may be semi-random or unique only withing the account. - external_sys_id?: string; // Generated by an external system. Ideally this should be something like a UUID. It may be the same as the external_id if nothing given. - code?: string; // Not currently used. - - account_id?: string; // Technically this is not required for global users. - account_id_random?: string; // Technically this is not required for global users. - - person_profile_id?: null | string; - person_profile_id_random?: null | string; // The new table person_profile will be used soon... - - user_id?: string; - user_id_random?: string; - - pronouns?: null | string; - informal_name?: null | string; - title_names?: null | string; - given_name: string; - middle_name?: null | string; - family_name: null | string; - designations?: null | string; - - professional_title?: null | string; - - full_name?: string; - full_name_override?: null | string; // was called display_name - - affiliations?: null | string; - - primary_email?: string; - - biography?: null | string; - - agree?: null | boolean; - comments?: null | string; - - allow_auth_key?: null | boolean; // For sign in without password - auth_key?: null | string; // Should this be saved locally? - passcode?: null | string; - - data_json?: null | string; - - enable: null | boolean; - hide?: null | boolean; - priority?: null | boolean; - sort?: null | number; - group?: null | string; - notes?: null | string; - created_on: Date; - updated_on?: null | Date; - - // Generated fields for sorting locally only - tmp_sort_1?: null | string; - tmp_sort_2?: null | string; - tmp_sort_3?: null | string; - - // Additional fields for convenience (database views) - username?: string; // Same as user_username - // user_username?: null|string; // Same as username - user_name?: null | string; - user_email?: null | string; - user_allow_auth_key?: null | boolean; // For sign in without password +export interface Person extends ae_Person { + // Legacy mapping support and view fields + external_id?: string; + external_sys_id?: string; + person_profile_id?: string; + person_profile_id_random?: string; + + username?: string; + user_name?: string; + user_email?: string; user_super?: boolean; user_manager?: boolean; user_administrator?: boolean; user_public?: boolean; - organization_id?: null | string; // The organization this person belongs to, if any. - organization_id_random?: null | string; // The random ID of the organization this person belongs to, if any. - organization_name?: null | string; + organization_id?: string; + organization_id_random?: string; + organization_name?: string; - contact_id?: null | string; // The contact ID of the person, if any. - contact_id_random?: null | string; // The random ID of the contact, if any. - contact_name?: null | string; - contact_email?: null | string; - contact_cc_email?: null | string; - contact_phone_mobile?: null | string; - contact_phone_home?: null | string; - contact_phone_office?: null | string; - contact_phone_landline?: null | string; - contact_phone_fax?: null | string; - contact_phone_other?: null | string; + contact_id?: string; + contact_id_random?: string; + contact_name?: string; - address_id?: null | string; // The address ID of the person, if any. - address_id_random?: null | string; // The random ID of the address, if any. - address_city?: null | string; - address_country_alpha_2_code?: null | string; // ISO 3166-1 alpha-2 country code + address_id?: string; + address_id_random?: string; } -// Updated 2026-01-06 -export interface User { - id: string; - user_id: string; - user_id_random: string; - - username: string; - name: string; - email: string; - - allow_auth_key: boolean; - super: boolean; - manager: boolean; - administrator: boolean; - verified: boolean; - public: boolean; - - person_id?: string; - person_id_random?: string; - - enable: null | boolean; - hide?: null | boolean; - priority?: null | boolean; - sort?: null | number; - group?: null | string; - notes?: null | string; - created_on: Date; - updated_on?: null | Date; - - tmp_sort_1?: string; - tmp_sort_2?: string; +export interface User extends ae_User { + // Additional local fields if needed } -// Updated 2026-01-06 +// Updated 2026-01-09 export class MySubClassedDexie extends Dexie { file!: Table; person!: Table; user!: Table; - account!: Table; - site!: Table; - site_domain!: Table; - address!: Table; - contact!: Table; + account!: Table; + site!: Table; + site_domain!: Table; + address!: Table; + contact!: Table; constructor() { super('ae_core_db'); - this.version(1).stores({ + this.version(2).stores({ file: ` - id, id_random, hosted_file_id, hosted_file_id_random, + id, hosted_file_id, hosted_file_id_random, hash_sha256, - account_id, + account_id, account_id_random, for_type, for_id, for_id_random, filename, extension, - content_type, size, enable, hide, priority, sort, group, created_on, updated_on`, person: ` id, person_id, person_id_random, external_id, code, - account_id, user_id, - account_id_random, user_id_random, - person_profile_id, - person_profile_id_random, + account_id, account_id_random, + user_id, user_id_random, given_name, family_name, full_name, affiliations, email, - agree, enable, hide, priority, sort, group, created_on, updated_on`, user: ` id, user_id, user_id_random, - username, name, email, + username, email, super, manager, administrator, enable, hide, priority, sort, group, created_on, updated_on`, @@ -220,21 +103,21 @@ export class MySubClassedDexie extends Dexie { site_domain: ` id, site_domain_id, site_domain_id_random, site_id, site_id_random, - domain, + fqdn, enable, hide, priority, sort, group, created_on, updated_on`, address: ` id, address_id, address_id_random, account_id, account_id_random, - for_type, for_id, for_id_random, + for_type, for_id_random, city, state_province, country, enable, hide, priority, sort, group, created_on, updated_on`, contact: ` id, contact_id, contact_id_random, account_id, account_id_random, - for_type, for_id, for_id_random, - name, email, phone, + for_type, for_id_random, + title, email, phone_mobile, enable, hide, priority, sort, group, created_on, updated_on` }); } diff --git a/src/lib/ae_events/ae_events__event.ts b/src/lib/ae_events/ae_events__event.ts index 4363285a..c9aae486 100644 --- a/src/lib/ae_events/ae_events__event.ts +++ b/src/lib/ae_events/ae_events__event.ts @@ -316,7 +316,7 @@ export async function update_ae_obj__event({ return result; } -// Updated 2026-01-06 +// Updated 2026-01-09 export async function qry_ae_obj_li__event({ api_cfg, for_obj_type = 'account', @@ -347,11 +347,10 @@ export async function qry_ae_obj_li__event({ const search_query: any = { and: [] }; if (qry_str) search_query.q = qry_str; - if (qry_person_id) { - search_query.and.push({ field: 'external_person_id', op: 'eq', value: qry_person_id }); - } + // Note: V3 does not support searching by person_id directly on the event object yet. + // We will filter client-side instead. - return await api.search_ae_obj_v3({ + const result_li = await api.search_ae_obj_v3({ api_cfg, obj_type: 'event', for_obj_type, @@ -360,11 +359,25 @@ export async function qry_ae_obj_li__event({ enabled, hidden, view, - limit, + limit: qry_person_id ? 500 : limit, // Increase limit if filtering client-side offset, order_by_li, log_lvl }); + + if (qry_person_id && result_li) { + return result_li.filter((ev: any) => { + return ( + ev.external_person_id === qry_person_id || + ev.poc_person_id === qry_person_id || + ev.poc_person_id_random === qry_person_id || + ev.poc_event_person_id === qry_person_id || + ev.poc_event_person_id_random === qry_person_id + ); + }); + } + + return result_li; } // Updated 2025-05-09 diff --git a/src/lib/ae_journals/db_journals.ts b/src/lib/ae_journals/db_journals.ts index effbc213..33a00307 100644 --- a/src/lib/ae_journals/db_journals.ts +++ b/src/lib/ae_journals/db_journals.ts @@ -1,456 +1,50 @@ import Dexie, { type Table } from 'dexie'; import type { key_val } from '$lib/stores/ae_stores'; +import type { ae_Journal, ae_JournalEntry } from '$lib/types/ae_types'; // li = list // kv = key value list // json = JSON string // ux = user experience (mode) // LLM = Large Language Model (AI) -// Updated 2025-03-15 +// Updated 2026-01-09 - Unified Types -export interface Journal { - id: string; // actually "id_random" - journal_id: string; - - // Essentially this is a change log of journals - snapshot_id?: string; // This is the original journal ID. If deleted, then delete all children journals. - previous_id?: null | string; // This is the old or parent journal ID - next_id?: null | string; // This is the new or child journal ID - - external_id?: null | string; - import_id?: null | string; - code?: null | string; - - for_type?: null | string; - for_id?: null | string; - - // template?: null|boolean; // Is this a template journal? If true, it can be used to create new journals. - - type_code?: null | string; - - account_id?: null | string; // Owner account of the journal - person_id?: null | string; // Owner person of the journal - // event_id?: null|string; // Assign to an event??? - // location_id?: null|string; // Assign to a location??? - - name: string; // or the title - short_name?: null | string; // Short name for the journal, if any. Used for display purposes. - summary?: null | string; // LLM (AI) generated summary...??? - outline?: null | string; // LLM (AI) generated outline...??? - - description?: null | string; - description_md_html?: null | string; // Markdown converted to HTML based on description. Uses marked or similar library for conversion. - description_md_html_alt?: null | string; // Markdown converted to HTML based on description. Uses marked or similar library for conversion. - description_html?: null | string; - description_json?: null | string; - - start_datetime?: null | Date; - end_datetime?: null | Date; - timezone?: null | string; - - alert?: null | boolean; // LLM (AI) generated summary...??? - alert_msg?: null | string; // LLM (AI) generated summary...??? - - sort_by?: null | string; // This is the sort by field - sort_by_desc?: null | string; // This is the sort by field description - - cfg_json?: null | key_val; // This is the configuration JSON for the journal - - data_json?: null | key_val; // We always need to store something extra... - - ux_mode?: null | string; // 'mobile' or 'desktop' - - // This only allows for basic access to the data. - passcode_read?: null | string; // For LLM (AI) generated summary...??? - passcode_read_expire?: null | Date; - passcode_write?: null | string; - passcode_write_expire?: null | Date; - - passcode?: null | string; // For Journal Entry encryption password - passcode_timeout?: null | number; // Timeout in seconds - - private_passcode?: null | string; // Combine with the Journal passcode for Journal Entry encryption password - - auth_key?: null | string; // For Journal authorization without sign in - - enable: null | boolean; - hide?: null | boolean; - archive?: null | boolean; // Archive the journal - archive_on?: null | Date; - priority?: null | boolean; - sort?: null | number; - group?: null | string; - notes?: null | string; - created_on: Date; - updated_on?: null | Date; - - // Generated fields for sorting locally only - tmp_sort_1?: null | string; - tmp_sort_2?: null | string; - tmp_sort_3?: null | string; - - combined_passcode?: null | string; // For Journal Entry encryption password - - // Additional fields for convenience (database views) - file_count?: null | number; // Only files directly under a journal - journal_file_id_li_json?: null | string; - - // One person - person__given_name?: null | string; - person__family_name?: null | string; - person__full_name?: null | string; - person__primary_email?: null | string; - person__passcode?: null | string; - - // JSON formatted key value pairs for multiple people: {id: name, email, etc.} - person__kv_json?: null | string; - - journal_name?: null | string; - - journal_location_code?: null | string; - journal_location_name?: null | string; - - journal_entry_count?: null | number; - - // A key value list of the entries - journal_entry_kv?: null | key_val; - journal_entry_li?: null | []; - // A key value list of the files - journal_file_kv?: null | key_val; - journal_file_li?: null | []; - - // journal_collection_id?: null|string; // For a collection of journals? - - // Future standard fields!!! - obj_id?: null | string; - obj_ext_uid?: null | string; // Probably not needed for journals - obj_ext_id?: null | string; // Probably not needed for journals - obj_import_id?: null | string; // Probably not needed for journals - obj_code?: null | string; - obj_account_id?: null | string; - obj_passcode?: null | string; - obj_type?: null | string; // Should always be 'journal' in this case - obj_type_ver_id?: null | string; // The ID from the table for the object type - obj_name?: null | string; - obj_summary?: null | string; // LLM (AI) generated summary...??? - obj_outline?: null | string; // LLM (AI) generated outline...??? - obj_description?: null | string; // Probably not needed for journals - obj_enable?: null | boolean; - obj_enable_on?: null | Date; - obj_archive_on?: null | Date; - obj_hide?: null | boolean; - obj_priority?: null | number; - obj_sort?: null | number; - obj_group?: null | string; - obj_cfg_json?: null | string; - obj_notes?: null | string; - obj_created_on?: Date; - obj_updated_on?: null | Date; +export interface Journal extends ae_Journal { + // Add any Dexie-specific or legacy local-only fields here if not in ae_Journal + // Most fields are now in ae_Journal and ae_BaseObj + + // For backward compatibility with some views that expect these + combined_passcode?: string; + person__given_name?: string; + person__family_name?: string; + person__full_name?: string; + person__primary_email?: string; + person__passcode?: string; + person__kv_json?: string; + + journal_entry_kv?: key_val; + journal_entry_li?: any[]; + journal_file_kv?: key_val; + journal_file_li?: any[]; } export const journal_field_li = [ 'id', 'journal_id', - 'snapshot_id', - 'previous_id', - 'next_id', - 'external_id', - 'import_id', - 'code', - 'for_type', - 'for_id', - 'type_code', + 'journal_id_random', 'account_id', + 'account_id_random', 'person_id', + 'person_id_random', + 'code', 'name', 'short_name', 'summary', 'outline', 'description', - 'description_md_html', - 'description_md_html_alt', - 'description_html', - 'description_json', - 'start_datetime', - 'end_datetime', - 'timezone', - 'alert', - 'alert_msg', - 'sort_by', - 'sort_by_desc', - 'cfg_json', - 'data_json', - 'ux_mode', - 'passcode_read', - 'passcode_read_expire', - 'passcode_write', - 'passcode_write_expire', - 'passcode_timeout', - 'private_passcode', - 'auth_key', - 'enable', - 'hide', - 'archive', // Archive the journal - 'archive_on', // Archive date - 'priority', // Priority flag - 'sort', // Sort order - 'group', // Group name - 'notes', // Notes about the journal - 'created_on', // Creation date - 'updated_on', // Last updated date - - 'tmp_sort_1', // Temporary sort field 1 - 'tmp_sort_2', // Temporary sort field 2 - 'tmp_sort_3', // Temporary sort field 3 - - 'combined_passcode', // For Journal Entry encryption password - 'file_count', // Only files directly under a journal - 'journal_file_id_li_json', // JSON string of file IDs - 'person__given_name', // Person's given name - 'person__family_name', // Person's family name - 'person__full_name', // Person's full name - 'person__primary_email', // Person's primary email - 'person__passcode', // Person's passcode - 'person__kv_json', // JSON formatted key value pairs for multiple people - 'journal_name', // Journal name - 'journal_location_code', // Journal location code - 'journal_location_name', // Journal location name - 'journal_entry_count', // Count of journal entries - 'journal_entry_kv', // Key value list of the entries - 'journal_entry_li', // List of journal entries - 'journal_file_kv', // Key value list of the files - 'journal_file_li', // List of journal files - - 'obj_id', // Object ID - 'obj_ext_uid', // External UID - 'obj_ext_id', // External ID - 'obj_import_id', // Import ID - 'obj_code', // Object code - 'obj_account_id', // Object account ID - 'obj_passcode', // Object passcode - 'obj_type', // Object type - 'obj_type_ver_id', // Object type version ID - 'obj_name', // Object name - 'obj_summary', // Object summary - 'obj_outline', // Object outline - 'obj_description', // Object description - 'obj_enable', // Object enable flag - 'obj_enable_on', // Object enable date - 'obj_archive_on', // Object archive date - 'obj_hide', // Object hide flag - 'obj_priority', // Object priority - 'obj_sort', // Object sort order - 'obj_group', // Object group name - 'obj_cfg_json', // Object configuration JSON - 'obj_notes', // Object notes - 'obj_created_on', // Object creation date - 'obj_updated_on' // Object last updated date -]; - -// Updated 2025-04-02 -export interface Journal_Entry { - id: string; // actually "id_random" - journal_entry_id: string; - - journal_id: string; // This is the parent journal ID. If deleted, then delete all children journal entries. - - // Essentially this is a change log of journal entries - snapshot_id?: string; // This is the original journal ID. If deleted, then delete all children journal entries. - previous_id?: null | string; // This is the old or parent journal ID - next_id?: null | string; // This is the new or child journal ID - - external_id?: null | string; - import_id?: null | string; - code?: null | string; - - for_type?: null | string; - for_id?: null | string; - - template?: null | boolean; // Is this a template journal entry? If true, it can be used to create new journal entries. - - activity_code?: null | string; - category_code?: null | string; - topic_code?: null | string; - type_code?: null | string; - tags?: null | string; // Comma separated tags - - journal_entry_type?: null | string; // This is the type of journal entry - - account_id?: null | string; // Owner account of the journal - person_id?: null | string; // Owner person of the journal - // event_id?: null|string; // Assign to an event??? - // location_id?: null|string; // Assign to a location??? - - public?: null | boolean; - private?: null | boolean; - personal?: null | boolean; - professional?: null | boolean; - - name: string; // or the title - short_name?: null | string; // Short name for the journal entry, if any. Used for display purposes. Most likely for the templates list. - summary?: null | string; // LLM (AI) generated summary...??? - outline?: null | string; // LLM (AI) generated outline...??? - // description?: null|string; // This is the description of the journal entry - - content?: null | string; - content_md_html?: null | string; // Markdown converted to HTML based on content. Uses marked or similar library for conversion. - content_md_html_alt?: null | string; // Markdown converted to HTML based on content. Uses marked or similar library for conversion. - content_html?: null | string; - content_json?: null | string; - content_encrypted?: null | string; // This is the encrypted content of the journal entry - - history?: null | string; // This is the history of the journal entry; a log - history_encrypted?: null | string; // This is the encrypted history of the journal entry - - passcode_hash?: null | string; // This is the passcode hash for the journal entry to look up the passcode - - start_datetime?: null | Date; - end_datetime?: null | Date; - timezone?: null | string; - seconds?: null | number; // Duration in seconds - - location?: null | string; // Location of the journal entry - latitude?: null | number; // Latitude of the journal entry - longitude?: null | number; // Longitude of the journal entry - - billable?: null | boolean; // Is this billable? - bill_to?: null | string; // Who to bill for this journal entry - bill_rate?: null | number; // Rate to bill for this journal entry - billable_minutes?: null | number; // Billable minutes for this journal entry - - alert?: null | boolean; // LLM (AI) generated summary...??? - alert_msg?: null | string; // LLM (AI) generated summary...??? - - parent_id?: null | string; // This is the parent journal entry ID. If deleted, then delete all children journal entries. - related_entry_id_li?: null | key_val; // List of related journal entry IDs - - // cfg_json?: null|key_val; // This is the configuration JSON for the journal entry - data_json?: null | key_val; // We always need to store something extra... - - // This only allows for basic access to the content. - passcode_read?: null | string; // For LLM (AI) generated summary...??? - passcode_read_expire?: null | Date; - passcode_write?: null | string; - passcode_write_expire?: null | Date; - - enable: null | boolean; - hide?: null | boolean; - priority?: null | boolean; - sort?: null | number; - group?: null | string; - notes?: null | string; - created_on: Date; - updated_on?: null | Date; - - // Generated fields for sorting locally only - tmp_sort_1?: null | string; - tmp_sort_2?: null | string; - tmp_sort_3?: null | string; - - // Additional fields for convenience (database views) - file_count?: null | number; // Only files directly under a journal - journal_file_id_li_json?: null | string; - - journal_code?: null | string; // This is the code for the journal entry - journal_name?: null | string; // This is the name for the journal entry - - // One person - person__given_name?: null | string; - person__family_name?: null | string; - person__full_name?: null | string; - person__primary_email?: null | string; - person__passcode?: null | string; - - // JSON formatted key value pairs for multiple people: {id: name, email, etc.} - person__kv_json?: null | string; - - // A key value list of the files - journal_file_kv?: null | key_val; - journal_file_li?: null | []; - - // journal_collection_id?: null|string; // For a collection of journal entries? - - // Future standard fields!!! - obj_id?: null | string; - obj_ext_uid?: null | string; // Probably not needed for journal entries - obj_ext_id?: null | string; // Probably not needed for journal entries - obj_import_id?: null | string; // Probably not needed for journal entries - obj_code?: null | string; - obj_account_id?: null | string; - obj_passcode?: null | string; - obj_type?: null | string; // Should always be 'journal' in this case - obj_type_ver_id?: null | string; // The ID from the table for the object type - obj_name?: null | string; - obj_summary?: null | string; // LLM (AI) generated summary...??? - obj_outline?: null | string; // LLM (AI) generated outline...??? - obj_description?: null | string; // Probably not needed for journal entries - obj_enable?: null | boolean; - obj_enable_on?: null | Date; - obj_archive_on?: null | Date; - obj_hide?: null | boolean; - obj_priority?: null | number; - obj_sort?: null | number; - obj_group?: null | string; - obj_cfg_json?: null | string; - obj_notes?: null | string; - obj_created_on?: Date; - obj_updated_on?: null | Date; -} - -export const journal_entry_field_li = [ - 'id', - 'journal_entry_id', - 'journal_id', - 'code', - 'for_type', - 'for_id', - 'template', - 'activity_code', - 'category_code', - 'topic_code', 'type_code', 'tags', - 'journal_entry_type', - 'account_id', - 'person_id', - 'public', - 'private', - 'personal', - 'professional', - 'name', - 'short_name', - 'summary', - 'outline', - 'content', - 'content_md_html', - 'content_md_html_alt', - 'content_html', - 'content_json', - 'content_encrypted', - 'history', - 'history_encrypted', - 'passcode_hash', - 'start_datetime', - 'end_datetime', - 'timezone', - 'seconds', - 'location', - 'latitude', - 'longitude', - 'billable', - 'bill_to', - 'bill_rate', - 'billable_minutes', - 'alert', - 'alert_msg', - 'parent_id', - 'related_entry_id_li', - 'data_json', - 'passcode_read', - 'passcode_read_expire', - 'passcode_write', - 'passcode_write_expire', 'enable', 'hide', 'priority', @@ -459,80 +53,73 @@ export const journal_entry_field_li = [ 'notes', 'created_on', 'updated_on', - 'tmp_sort_1', 'tmp_sort_2', - 'tmp_sort_3', - - 'file_count', - 'journal_file_id_li_json', - 'journal_code', - 'journal_name', - 'person__given_name', - 'person__family_name', - 'person__full_name', - 'person__primary_email', - 'person__passcode', - 'person__kv_json', - 'journal_file_kv', - 'journal_file_li', - - 'obj_id', - 'obj_ext_uid', - 'obj_ext_id', - 'obj_import_id', - 'obj_code', - 'obj_account_id', - 'obj_passcode', - 'obj_type', - 'obj_type_ver_id', - 'obj_name', - 'obj_summary', - 'obj_outline', - 'obj_description', - 'obj_enable', - 'obj_enable_on', - 'obj_archive_on', - 'obj_hide', - 'obj_priority', - 'obj_sort', - 'obj_group', - 'obj_cfg_json', - 'obj_notes', - 'obj_created_on', - 'obj_updated_on' + 'tmp_sort_3' +]; + +export interface Journal_Entry extends ae_JournalEntry { + // Add any Dexie-specific or legacy local-only fields here + person__given_name?: string; + person__family_name?: string; + person__full_name?: string; + person__primary_email?: string; + person__passcode?: string; + person__kv_json?: string; + + journal_file_kv?: key_val; + journal_file_li?: any[]; +} + +export const journal_entry_field_li = [ + 'id', + 'journal_entry_id', + 'journal_entry_id_random', + 'journal_id', + 'journal_id_random', + 'person_id', + 'person_id_random', + 'code', + 'name', + 'short_name', + 'summary', + 'outline', + 'content', + 'content_md_html', + 'content_encrypted', + 'enable', + 'hide', + 'priority', + 'sort', + 'group', + 'notes', + 'created_on', + 'updated_on', + 'tmp_sort_1', + 'tmp_sort_2', + 'tmp_sort_3' ]; -// Updated 2024-06-10 export class MySubClassedDexie extends Dexie { - // We just tell the typing system this is the case journal!: Table; journal_entry!: Table; constructor() { super('ae_journals_db'); - this.version(4).stores({ + this.version(5).stores({ journal: ` - id, journal_id, + id, journal_id, journal_id_random, code, - account_id, - person_id, - conference, type, + account_id, account_id_random, + person_id, person_id_random, name, - start_datetime, end_datetime, - timezone, - tmp_sort_1, tmp_sort_2, tmp_sort_3, enable, hide, priority, sort, group, created_on, updated_on`, journal_entry: ` - id, journal_entry_id, - journal_id, + id, journal_entry_id, journal_entry_id_random, + journal_id, journal_id_random, code, - account_id, template, name, - start_datetime, end_datetime, - timezone, - tmp_sort_1, tmp_sort_2, tmp_sort_3, enable, hide, priority, sort, group, created_on, updated_on` }); } diff --git a/src/lib/types/ae_types.ts b/src/lib/types/ae_types.ts index 11738629..c8c356e9 100644 --- a/src/lib/types/ae_types.ts +++ b/src/lib/types/ae_types.ts @@ -106,12 +106,40 @@ export interface ae_SiteDomain extends ae_BaseObj { /** * Journal - A collection of entries */ +/** + * Journal - A container for journal entries + */ export interface ae_Journal extends ae_BaseObj { journal_id: string; journal_id_random: string; account_id: string; account_id_random: string; + person_id?: string; + person_id_random?: string; + for_type?: string; + for_id?: string; + for_id_random?: string; + + type_code?: string; + tags?: string; + + summary?: string; + outline?: string; + + start_datetime?: string | Date; + end_datetime?: string | Date; + timezone?: string; + + passcode?: string; + passcode_timeout?: number; + private_passcode?: string; + + cfg_json?: any; + data_json?: any; + meta_json?: any; + + // SQL View fields journal_entry_count?: number; file_count?: number; } @@ -128,6 +156,8 @@ export interface ae_JournalEntry extends ae_BaseObj { person_id?: string; person_id_random?: string; + template?: boolean; + journal_entry_type?: string; activity_code?: string; category_code?: string; @@ -155,14 +185,30 @@ export interface ae_JournalEntry extends ae_BaseObj { passcode_hash?: string; + start_datetime?: string | Date; + end_datetime?: string | Date; + timezone?: string; + seconds?: number; + + location?: string; + latitude?: number; + longitude?: number; + + billable?: boolean; + bill_to?: string; + bill_rate?: number; + billable_minutes?: number; + alert?: boolean; alert_msg?: string; data_json?: any; + meta_json?: any; // SQL View fields journal_code?: string; journal_name?: string; + file_count?: number; } /** @@ -178,10 +224,12 @@ export interface ae_Person extends ae_BaseObj { user_id_random?: string; prefix?: string; + title_names?: string; given_name?: string; middle_name?: string; family_name?: string; suffix?: string; + designations?: string; full_name?: string; informal_name?: string; @@ -199,6 +247,9 @@ export interface ae_Person extends ae_BaseObj { tagline?: string; + allow_auth_key?: boolean; + passcode?: string; + data_json?: any; } diff --git a/src/routes/core/addresses/+page.svelte b/src/routes/core/addresses/+page.svelte index 8625c230..c5212fb5 100644 --- a/src/routes/core/addresses/+page.svelte +++ b/src/routes/core/addresses/+page.svelte @@ -2,11 +2,13 @@ import { onMount } from 'svelte'; import { ae_loc, ae_api, slct } from '$lib/stores/ae_stores'; import { goto } from '$app/navigation'; - import { MapPin, Plus, Search, ExternalLink } from 'lucide-svelte'; + import { MapPin, Plus, Search, ExternalLink, X } from 'lucide-svelte'; import { load_ae_obj_li__address, create_ae_obj__address } from '$lib/ae_core/ae_core__address'; + import Address_form from './ae_comp__address_form.svelte'; let address_li: any[] = $state([]); let loading = $state(true); + let show_add_form = $state(false); async function load_addresses() { if (!$ae_loc.account_id) return; @@ -20,27 +22,6 @@ loading = false; } - async function handle_add() { - const city = prompt('Enter city:'); - if (!city) return; - const state_province = prompt('Enter state/province:'); - const country = prompt('Enter country:', 'USA'); - - const result = await create_ae_obj__address({ - api_cfg: $ae_api, - account_id: $ae_loc.account_id, - data_kv: { city, state_province, country, enable: true }, - log_lvl: 1 - }); - - if (result) { - load_addresses(); - if (result.address_id_random) { - goto(`/core/addresses/${result.address_id_random}`); - } - } - } - onMount(() => { if (!$ae_loc.manager_access) { goto('/core'); @@ -56,11 +37,30 @@

Address Management

- + {#if show_add_form} +
+ { + show_add_form = false; + load_addresses(); + if (new_addr.address_id_random) { + goto(`/core/addresses/${new_addr.address_id_random}`); + } + }} + onCancel={() => show_add_form = false} + /> +
+ {/if} + {#if loading}
{:else if address_li.length === 0} diff --git a/src/routes/core/addresses/[address_id]/+page.svelte b/src/routes/core/addresses/[address_id]/+page.svelte index a7575fd0..b3fa0e80 100644 --- a/src/routes/core/addresses/[address_id]/+page.svelte +++ b/src/routes/core/addresses/[address_id]/+page.svelte @@ -9,12 +9,13 @@ import { editable_fields__address } from '$lib/ae_core/ae_core__address.editable_fields'; import { ae_api, ae_loc } from '$lib/stores/ae_stores'; import { goto } from '$app/navigation'; - import { Save, Trash2, ArrowLeft, MapPin } from 'lucide-svelte'; + import { Save, Trash2, ArrowLeft, MapPin, Edit, Eye } from 'lucide-svelte'; + import Address_form from '../ae_comp__address_form.svelte'; let address_id = $page.params.address_id; let address: any = $state(null); let loading = $state(true); - let saving = $state(false); + let is_editing = $state(false); async function load_data() { loading = true; @@ -34,24 +35,6 @@ load_data(); }); - async function handle_save() { - saving = true; - const data_kv: any = {}; - editable_fields__address.forEach(field => { - if (address[field] !== undefined) { - data_kv[field] = address[field]; - } - }); - - await update_ae_obj__address({ - api_cfg: $ae_api, - address_id, - data_kv, - log_lvl: 1 - }); - saving = false; - } - async function handle_delete() { if (!confirm('Permanently delete this address?')) return; await delete_ae_obj_id__address({ @@ -76,11 +59,15 @@
- -
@@ -88,72 +75,99 @@ {#if loading}
{:else if address} -
-
-
-

Address Details

-
- - - + {#if is_editing} + { + address = updated; + is_editing = false; + }} + onCancel={() => is_editing = false} + /> + {:else} +
+
+
+

Address Details

+
+
+

Attention To

+

{address.attention_to || '--'}

+
+
+

Organization

+

{address.organization_name || '--'}

+
+
+

Address Lines

+

{address.line_1}

+ {#if address.line_2}

{address.line_2}

{/if} + {#if address.line_3}

{address.line_3}

{/if} +
+
+

City, State/Province

+

{address.city}, {address.state_province || '--'}

+
+
+

Postal Code / Country

+

{address.postal_code || '--'} / {address.country_name || address.country || '--'}

+
+
+
+ +
+

Technical Details

+
+
+

Timezone

+

{address.timezone || '--'}

+
+
+

Coordinates

+

{address.latitude || '--'}, {address.longitude || '--'}

+
+
+

Internal Notes

+

{address.notes || '--'}

+
+
-
-

Internal Metadata

-
- - - +
+
+

Status

+
+
+ Enabled + + {address.enable ? 'Yes' : 'No'} + +
+
+ Hidden + + {address.hide ? 'Yes' : 'No'} + +
+
+ Priority + + {address.priority ? 'Yes' : 'No'} + +
+
+
+ +
+

ID: {address.address_id_random}

+

Created: {new Date(address.created_on).toLocaleString()}

+ {#if address.updated_on} +

Updated: {new Date(address.updated_on).toLocaleString()}

+ {/if}
- -
-
-

Status & Visibility

-
- - - -
-
- -
-

ID: {address.address_id_random}

-

Created: {new Date(address.created_on).toLocaleString()}

- {#if address.updated_on} -

Updated: {new Date(address.updated_on).toLocaleString()}

- {/if} -
-
-
+ {/if} {/if}
diff --git a/src/routes/core/addresses/ae_comp__address_form.svelte b/src/routes/core/addresses/ae_comp__address_form.svelte new file mode 100644 index 00000000..2828baaf --- /dev/null +++ b/src/routes/core/addresses/ae_comp__address_form.svelte @@ -0,0 +1,258 @@ + + +
+
+

+ + {address ? 'Edit Address' : 'Create New Address'} +

+
+ {#if onCancel} + + {/if} + +
+
+ + {#if error_msg} + + {/if} + +
+ +
+ Location Details + + + + + + + +
+ + +
+
+ + +
+ Region & Code + +
+ + +
+ +
+ + +
+ + + +
+ + +
+ Technical & GIS + + + +
+ + +
+
+ + +
+ Status + +
+ + + +
+ + +
+
+ +
+ +
+
diff --git a/src/routes/core/contacts/+page.svelte b/src/routes/core/contacts/+page.svelte index 0031b9e8..ca31f4b7 100644 --- a/src/routes/core/contacts/+page.svelte +++ b/src/routes/core/contacts/+page.svelte @@ -2,11 +2,13 @@ import { onMount } from 'svelte'; import { ae_loc, ae_api, slct } from '$lib/stores/ae_stores'; import { goto } from '$app/navigation'; - import { Phone, Plus, Search, Mail, User, ExternalLink } from 'lucide-svelte'; + import { Phone, Plus, Search, Mail, User, ExternalLink, X } from 'lucide-svelte'; import { load_ae_obj_li__contact, create_ae_obj__contact } from '$lib/ae_core/ae_core__contact'; + import Contact_form from './ae_comp__contact_form.svelte'; let contact_li: any[] = $state([]); let loading = $state(true); + let show_add_form = $state(false); async function load_contacts() { if (!$ae_loc.account_id) return; @@ -20,27 +22,6 @@ loading = false; } - async function handle_add() { - const name = prompt('Enter contact name:'); - if (!name) return; - const email = prompt('Enter email address:'); - const phone = prompt('Enter phone number:'); - - const result = await create_ae_obj__contact({ - api_cfg: $ae_api, - account_id: $ae_loc.account_id, - data_kv: { name, email, phone, enable: true }, - log_lvl: 1 - }); - - if (result) { - load_contacts(); - if (result.contact_id_random) { - goto(`/core/contacts/${result.contact_id_random}`); - } - } - } - onMount(() => { if (!$ae_loc.manager_access) { goto('/core'); @@ -56,11 +37,30 @@

Contact Management

- + {#if show_add_form} +
+ { + show_add_form = false; + load_contacts(); + if (new_con.contact_id_random) { + goto(`/core/contacts/${new_con.contact_id_random}`); + } + }} + onCancel={() => show_add_form = false} + /> +
+ {/if} + {#if loading}
{:else if contact_li.length === 0} diff --git a/src/routes/core/contacts/[contact_id]/+page.svelte b/src/routes/core/contacts/[contact_id]/+page.svelte index 2c0e710f..553015f7 100644 --- a/src/routes/core/contacts/[contact_id]/+page.svelte +++ b/src/routes/core/contacts/[contact_id]/+page.svelte @@ -9,12 +9,13 @@ import { editable_fields__contact } from '$lib/ae_core/ae_core__contact.editable_fields'; import { ae_api, ae_loc } from '$lib/stores/ae_stores'; import { goto } from '$app/navigation'; - import { Save, Trash2, ArrowLeft, UserRound } from 'lucide-svelte'; + import { Save, Trash2, ArrowLeft, UserRound, Edit, Eye } from 'lucide-svelte'; + import Contact_form from '../ae_comp__contact_form.svelte'; let contact_id = $page.params.contact_id; let contact: any = $state(null); let loading = $state(true); - let saving = $state(false); + let is_editing = $state(false); async function load_data() { loading = true; @@ -34,24 +35,6 @@ load_data(); }); - async function handle_save() { - saving = true; - const data_kv: any = {}; - editable_fields__contact.forEach(field => { - if (contact[field] !== undefined) { - data_kv[field] = contact[field]; - } - }); - - await update_ae_obj__contact({ - api_cfg: $ae_api, - contact_id, - data_kv, - log_lvl: 1 - }); - saving = false; - } - async function handle_delete() { if (!confirm('Permanently delete this contact?')) return; await delete_ae_obj_id__contact({ @@ -72,15 +55,19 @@
-

{contact?.name ?? 'Loading Contact...'}

+

{contact?.name || contact?.title || 'Loading Contact...'}

- -
@@ -88,72 +75,110 @@ {#if loading}
{:else if contact} -
-
-
-

Contact Information

-
- - - + {#if is_editing} + { + contact = updated; + is_editing = false; + }} + onCancel={() => is_editing = false} + /> + {:else} +
+
+
+

Contact Details

+
+
+

Title / Name

+

{contact.title || contact.name || '--'}

+
+
+

Tagline

+

{contact.tagline || '--'}

+
+
+

Email

+

{contact.email || '--'}

+
+
+

Website

+

+ {#if contact.website_url} + {contact.website_url} + {:else} + -- + {/if} +

+
+
+
+ +
+

Communication & Social

+
+
+

Mobile Phone

+

{contact.phone_mobile || contact.phone || '--'}

+
+
+

Office Phone

+

{contact.phone_office || '--'}

+
+
+

LinkedIn

+

{contact.linkedin_url || '--'}

+
+
+

Socials

+
+ {#if contact.facebook_url}FB{/if} + {#if contact.instagram_url}IG{/if} +
+
+
+

Internal Notes

+

{contact.notes || '--'}

+
+
-
-

Internal Metadata

-
- - - +
+
+

Status

+
+
+ Enabled + + {contact.enable ? 'Yes' : 'No'} + +
+
+ Hidden + + {contact.hide ? 'Yes' : 'No'} + +
+
+ Priority + + {contact.priority ? 'Yes' : 'No'} + +
+
+
+ +
+

ID: {contact.contact_id_random}

+

Created: {new Date(contact.created_on).toLocaleString()}

+ {#if contact.updated_on} +

Updated: {new Date(contact.updated_on).toLocaleString()}

+ {/if}
- -
-
-

Status & Visibility

-
- - - -
-
- -
-

ID: {contact.contact_id_random}

-

Created: {new Date(contact.created_on).toLocaleString()}

- {#if contact.updated_on} -

Updated: {new Date(contact.updated_on).toLocaleString()}

- {/if} -
-
-
+ {/if} {/if}
diff --git a/src/routes/core/contacts/ae_comp__contact_form.svelte b/src/routes/core/contacts/ae_comp__contact_form.svelte new file mode 100644 index 00000000..d4a430bf --- /dev/null +++ b/src/routes/core/contacts/ae_comp__contact_form.svelte @@ -0,0 +1,240 @@ + + +
+
+

+ + {contact ? 'Edit Contact' : 'Create New Contact'} +

+
+ {#if onCancel} + + {/if} + +
+
+ + {#if error_msg} + + {/if} + +
+ +
+ Identity & Branding + + + + + + +
+ + +
+ Phone & Web + +
+ + +
+ + +
+ + +
+ Social Media + + + +
+ + +
+
+ + +
+ Status + +
+ + + +
+ + +
+
+ +
+ +
+
diff --git a/src/routes/core/people/+page.svelte b/src/routes/core/people/+page.svelte index 8d2914f9..254308de 100644 --- a/src/routes/core/people/+page.svelte +++ b/src/routes/core/people/+page.svelte @@ -1,13 +1,16 @@ - -