Compare commits
64 Commits
7f17b3b9a1
...
929f08b656
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
929f08b656 | ||
|
|
48a39b16d5 | ||
|
|
ab294c2a0b | ||
|
|
1de563203d | ||
|
|
0091fe3ff6 | ||
|
|
0ad36a74b2 | ||
|
|
fd244720a7 | ||
|
|
362136e677 | ||
|
|
a5a806e256 | ||
|
|
613e43114c | ||
|
|
1c818e648b | ||
|
|
5cd1d3b7ad | ||
|
|
66f0efb507 | ||
|
|
a637343544 | ||
|
|
a8f3c29b9f | ||
|
|
23d25bf65a | ||
|
|
12a9472064 | ||
|
|
b74c6d0e9c | ||
|
|
e1338b1a72 | ||
|
|
6018a94499 | ||
|
|
6e67534454 | ||
|
|
693486bac9 | ||
|
|
6d1d1e2658 | ||
|
|
7f6e286b73 | ||
|
|
a3ed379b17 | ||
|
|
e9379be5a1 | ||
|
|
9a75243d9c | ||
|
|
94849137f0 | ||
|
|
512e5ef87c | ||
|
|
d27ec58fe9 | ||
|
|
42358efe7d | ||
|
|
8e61bd0ba1 | ||
|
|
0bc71391fc | ||
|
|
6e22639e6e | ||
|
|
a6f8ff709e | ||
|
|
dafe79b3c6 | ||
|
|
a4927d37bd | ||
|
|
f3ab1c1050 | ||
|
|
5bed167829 | ||
|
|
a14320d9ed | ||
|
|
de8a016bda | ||
|
|
1c8997bd4f | ||
|
|
fe23899479 | ||
|
|
6662e82f40 | ||
|
|
4586e809d7 | ||
|
|
334c3a21bc | ||
|
|
14c2635df4 | ||
|
|
9673cbefe3 | ||
|
|
9a43879535 | ||
|
|
2c570c82dc | ||
|
|
bf9aa9710c | ||
|
|
942b3ddf5f | ||
|
|
bb0365f1e8 | ||
|
|
2b747de9bd | ||
|
|
519f5b949c | ||
|
|
bf834aa165 | ||
|
|
0d960435f8 | ||
|
|
60461de9d9 | ||
|
|
da3b8dcf46 | ||
|
|
f628e7e3fc | ||
|
|
fdd8691e2e | ||
|
|
621a637b85 | ||
|
|
639e436854 | ||
|
|
81741919a8 |
@@ -1,4 +1,28 @@
|
||||
node_modules/
|
||||
.svelte-kit
|
||||
.vite
|
||||
node_modules
|
||||
dist
|
||||
build
|
||||
.cache
|
||||
.DS_Store
|
||||
.vscode
|
||||
.idea
|
||||
.git
|
||||
.gitignore
|
||||
coverage
|
||||
tests
|
||||
documentation
|
||||
backups
|
||||
*.log
|
||||
*.bak
|
||||
*.tgz
|
||||
.env
|
||||
.env.*
|
||||
npm-debug.log
|
||||
package-lock.json.bak
|
||||
yarn-error.log
|
||||
/.cache
|
||||
/.parcel-cachenode_modules/
|
||||
build/
|
||||
.svelte-kit/
|
||||
.git/
|
||||
|
||||
10
.prettierrc
10
.prettierrc
@@ -4,6 +4,14 @@
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 80,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"bracketSameLine": true,
|
||||
|
||||
"svelteSortOrder": "options-scripts-markup-styles",
|
||||
"svelteIndentScriptAndStyle": false,
|
||||
"svelteAllowShorthand": true,
|
||||
"plugins": [
|
||||
"prettier-plugin-svelte",
|
||||
"prettier-plugin-tailwindcss"
|
||||
],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
}
|
||||
|
||||
@@ -100,10 +100,9 @@ src/routes/
|
||||
|
||||
## Active Issues (check TODO__Agents.md for current state)
|
||||
|
||||
- Session/Presentation page cold-start bug (Events Pres Mgmt) — unresolved
|
||||
- Sev-1: `PUBLIC_AE_API_SECRET_KEY` audit
|
||||
- CRUD v2 retirement → V3 editor
|
||||
- 403/401 error boundaries ("Session Expired" UI)
|
||||
- Sev-1: `PUBLIC_AE_API_SECRET_KEY` audit — see TODO__Agents.md (assessed acceptable, 2026-03-11)
|
||||
- V3 CRUD migration — remaining legacy API wrappers in events/sponsorships/core (see `PROJECT__Use_AE_API_V3_CRUD_upgrade.md`)
|
||||
- Style Review Phase 3 — IDAA + Pres Mgmt card polish deferred post-April 2026 conference
|
||||
|
||||
---
|
||||
|
||||
@@ -117,4 +116,4 @@ src/routes/
|
||||
| `documentation/GUIDE__SvelteKit2_Svelte5_DexieJS.md` | Dexie + liveQuery patterns |
|
||||
| `documentation/GEMINI__Svelte_and_Me.md` | Svelte 5 runes patterns |
|
||||
| `documentation/PROJECT__AE_Events_Launcher_Native_integration.md` | Electron/Launcher |
|
||||
| `documentation/PROJECT__AE_Pres_Mgmt_Session_view_refactor_2026-02.md` | Session bug plan |
|
||||
| `documentation/PROJECT__AE_Events_Badges_Review_Print.md` | Badges — kiosk editing (Task 4.0 open) |
|
||||
|
||||
@@ -116,8 +116,8 @@ Built on the Events object.
|
||||
|
||||
Developer sandbox pages — not for production use.
|
||||
|
||||
- `/testing/ae_obj_field_editor_v3/` — V3 field editor playground
|
||||
- `/testing/data_store_v3/` — Data store V3 playground
|
||||
- `/testing/ae_obj_field_editor/` — V3 field editor playground
|
||||
- `/testing/data_store/` — Data store V3 playground
|
||||
- `/testing/editor_test/` — CodeMirror / TipTap editor tests
|
||||
- `/testing/hosted_files/` — File upload tests
|
||||
|
||||
|
||||
27
aether_container_env/Dockerfile.buildkit.example
Normal file
27
aether_container_env/Dockerfile.buildkit.example
Normal file
@@ -0,0 +1,27 @@
|
||||
## BuildKit-friendly multi-stage Dockerfile example for Aether frontend
|
||||
|
||||
# Stage 1: dependencies
|
||||
FROM node:20-alpine AS deps
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci --no-audit --prefer-offline
|
||||
|
||||
# Stage 2: build
|
||||
FROM node:20-alpine AS build
|
||||
WORKDIR /app
|
||||
# optionally reuse deps from previous stage
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
# If you want to use BuildKit cache mounts during local development, uncomment the next line
|
||||
# RUN --mount=type=cache,target=/root/.npm npm ci
|
||||
RUN npm run build
|
||||
|
||||
# Stage 3: runtime (static site served by nginx)
|
||||
FROM nginx:stable-alpine AS runtime
|
||||
COPY --from=build /app/build /usr/share/nginx/html
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
# Notes:
|
||||
# - Keep dependency installation separate from copying source to maximize cache hits when only application code changes.
|
||||
# - For backend images, follow the same pattern: install deps early, copy source later, and keep a small final runtime image.
|
||||
33
aether_container_env/ci_buildx_example.sh
Normal file
33
aether_container_env/ci_buildx_example.sh
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Example CI script to build and push an image with buildx using registry cache.
|
||||
# This script is provider-agnostic and intended to be run inside CI where
|
||||
# Docker and buildx are available and authenticated against the registry.
|
||||
|
||||
REGISTRY=${REGISTRY:-ghcr.io/ORG/REPO}
|
||||
IMAGE_TAG=${IMAGE_TAG:-staging}
|
||||
CACHE_REF=${CACHE_REF:-${REGISTRY}:cache}
|
||||
|
||||
echo "Building ${REGISTRY}:${IMAGE_TAG} using registry cache ${CACHE_REF}"
|
||||
|
||||
docker buildx build \
|
||||
--push \
|
||||
--tag ${REGISTRY}:${IMAGE_TAG} \
|
||||
--cache-from type=registry,ref=${CACHE_REF} \
|
||||
--cache-to type=registry,ref=${CACHE_REF},mode=max \
|
||||
.
|
||||
|
||||
echo "Build complete. Image: ${REGISTRY}:${IMAGE_TAG}"
|
||||
|
||||
# Optional: instruct devs how to run locally with a local cache
|
||||
cat <<'EOF'
|
||||
Local test with BuildKit and local cache:
|
||||
DOCKER_BUILDKIT=1 docker build \
|
||||
--tag myapp:staging \
|
||||
--cache-to=type=local,dest=/tmp/docker-cache \
|
||||
--cache-from=type=local,src=/tmp/docker-cache .
|
||||
|
||||
Prune local builder cache older than 72 hours:
|
||||
docker builder prune --filter "until=72h" --force
|
||||
EOF
|
||||
30
documentation/AE_Docker_CI_cache_policy.md
Normal file
30
documentation/AE_Docker_CI_cache_policy.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# AE Docker CI Cache Policy (recommendation)
|
||||
|
||||
Purpose
|
||||
- Provide a straightforward policy to keep build caches useful but bounded.
|
||||
|
||||
Recommendations
|
||||
- Primary CI cache: **registry-based buildx cache** (preferred). Use a single cache ref (e.g. `ghcr.io/ORG/REPO:cache`) reused by CI builds.
|
||||
- Local dev cache: use `--cache-to type=local` for fast iteration but prune periodically.
|
||||
- Retention: keep registry cache for 30 days by default. Implement registry GC or lifecycle rule to delete older cache blobs.
|
||||
|
||||
Rotation strategy
|
||||
- Option A (simple): CI always writes to the same cache ref `:cache`. Periodically (monthly) run a job to `docker pull` and `docker image rm` older tags if you use date-based tagging.
|
||||
- Option B (date-tag): CI writes cache to `cache-YYYYMMDD` and a small scheduled job deletes tags older than 30 days.
|
||||
|
||||
Pruning commands (developer)
|
||||
- Remove local build cache older than 72 hours:
|
||||
```bash
|
||||
docker builder prune --filter "until=72h" --force
|
||||
```
|
||||
- Remove all builder cache (aggressive):
|
||||
```bash
|
||||
docker builder prune --all --force
|
||||
```
|
||||
|
||||
CI runner requirements
|
||||
- `docker` and `docker buildx` available in runner environment.
|
||||
- Registry credentials provided via CI secrets with permission to push/pull images.
|
||||
|
||||
Security & Secrets
|
||||
- Do not store registry credentials in repo. Use CI secret storage.
|
||||
@@ -575,7 +575,6 @@ This document provides a reference for the data structures of the core Aether AP
|
||||
- `orders_info`: `Optional[dict]`
|
||||
- `order_list`: `Optional[list]`
|
||||
- `order_cart`: `Optional[dict]`
|
||||
- `order_cart_v3`: `Optional[dict]`
|
||||
- `organization`: `Optional[Union[Organization_Base, None]]`
|
||||
- `post_list`: `Optional[list]`
|
||||
- `user`: `Optional[Union[User_Base, None]]`
|
||||
|
||||
@@ -16,9 +16,8 @@ The Aether project is a Svelte and SvelteKit based application, utilizing Tailwi
|
||||
- Flowbite (Tailwind Components)
|
||||
- Custom Components (a growing library of `ae_comp__*` and `element_*` components)
|
||||
- **Text/Code Editors:**
|
||||
- CodeMirror 6.x (primary text and code editor, planned for rich text editing)
|
||||
- Edra (TipTap based Rich Text Editor)
|
||||
- **Note:** ShadEditor TipTap is present but marked for removal, with CodeMirror being the preferred solution for all text editing needs.
|
||||
- CodeMirror 6.x (`element_editor_codemirror.svelte`) — source/code editing, markdown
|
||||
- TipTap (`element_editor_tiptap.svelte`) — WYSIWYG rich-text for content fields (IDAA, Journals, Leads notes)
|
||||
- **Icons:** Lucide Icons (SVG Icons)
|
||||
- **Markdown Parsing:** `marked` library
|
||||
- **State Management:** Svelte stores, potentially with `liveQuery` from Dexie for reactive IndexedDB interactions.
|
||||
|
||||
@@ -40,9 +40,9 @@ These are reusable components that provide common functionalities across differe
|
||||
- **`copy_btn`**: A button to copy content to the clipboard.
|
||||
- Properties: `clipboard`, `bind:value`, `btn_text`, `btn_html`.
|
||||
- **`txt_editor`**: A basic text area editor.
|
||||
- **`md_editor`**: Markdown editor.
|
||||
- Uses CodeMirror (planned for rich text editing).
|
||||
- **Note:** ShadEditor TipTap is present but marked for removal. ShadCN components are also being phased out in favor of CodeMirror for text editing.
|
||||
- **`md_editor`**: Markdown/rich-text editing handled by two active components:
|
||||
- `element_editor_codemirror.svelte` — CodeMirror 6, used for source/code editing
|
||||
- `element_editor_tiptap.svelte` — TipTap (WYSIWYG), used for rich-text content fields
|
||||
- **`html_editor`**: HTML editor.
|
||||
- **`media_player`**: Component for playing media files.
|
||||
- Properties: `hosted_file`, `archive_content`, `media_player`.
|
||||
@@ -62,13 +62,11 @@ These are reusable components that provide common functionalities across differe
|
||||
- Bindings: `bind:trigger`, `bind:show_spinner`, `bind:show_percent`.
|
||||
- Status: `started`, `downloading`, `finished`.
|
||||
- **`data_store`**: Component for interacting with data stores.
|
||||
- **`ae_crud`**: Generic CRUD (Create, Read, Update, Delete) component.
|
||||
- **Note:** Needs simplification.
|
||||
- Properties: `obj`, `prop`, `current_value`.
|
||||
- Bindings: `bind:value`, `bind:trigger`, `inner fragment`.
|
||||
- **`ae_obj_prop_val`**: A wrapper for a function to manage object property values.
|
||||
- Bindings: `bind:obj_type`, `bind:obj_id`, `bind:obj_prop`, `bind:obj_value`, `bind:obj_new_value`, `bind:trigger`, `bind:show_spinner`, `bind:show_percent`.
|
||||
- Status: `status`, `result`.
|
||||
- **`element_ae_obj_field_editor`**: Standard single-field inline editor. Replaces retired `ae_crud` v1/v2 components.
|
||||
- Props: `object_type`, `object_id`, `field_name`, `field_type`, `current_value`
|
||||
- Field types: `text`, `textarea`, `select`, `tiptap`, `checkbox`, `date`, `datetime`, `number`
|
||||
- Callbacks: `on_success`, `on_error`
|
||||
- Respects `$ae_loc.edit_mode` — edit trigger hidden when edit mode is off.
|
||||
- **`sql_qry`**: Component for executing SQL queries.
|
||||
- **`obj_tbl`**: Object SQL results table or similar.
|
||||
- **`qr_scanner`**: Component for scanning QR codes.
|
||||
|
||||
@@ -68,39 +68,145 @@ Modify data in the system.
|
||||
* **Header:** `x-ae-ignore-extra-fields: true`
|
||||
* **Behavior:** When set to `true`, the backend will automatically strip any fields from the payload that are not defined in the object's model before attempting to save to the database.
|
||||
|
||||
### D. ID Fields in Responses (Vision ID Convention)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **V3 responses always use random string IDs — never database integers.**
|
||||
|
||||
After a successful `POST` create or any `GET`, the response contains:
|
||||
|
||||
| Field | Type | Use |
|
||||
| :--- | :--- | :--- |
|
||||
| `{obj_type}_id` | `string` | **Primary public ID.** Use this for subsequent `PATCH` calls and UI routing. |
|
||||
| `{obj_type}_id_random` | `string` | Legacy alias. Same value as `{obj_type}_id`. Present for backward compat only. |
|
||||
|
||||
**Example — create then immediately PATCH:**
|
||||
```ts
|
||||
const created = await postArchiveContent(archiveId, payload);
|
||||
const newId = created.data.archive_content_id; // random string e.g. "xK9mP3qRtL2"
|
||||
|
||||
// Use it directly in the PATCH URL — no lookup needed
|
||||
await patchArchiveContent(newId, { name: 'Updated Name' });
|
||||
// PATCH /v3/crud/archive/{archive_id}/archive_content/{newId}
|
||||
```
|
||||
|
||||
> **Note on `_id_random` suffix:** The `{obj_type}_id_random` field is a legacy artifact from the pre-Vision model. Once you confirm `{obj_type}_id` is a random string (length 11–22), you do not need `_id_random` as a fallback. New code should only read `{obj_type}_id`.
|
||||
|
||||
---
|
||||
|
||||
## 4. V3 Uniform Lookup System
|
||||
|
||||
The V3 Lookup system provides a hierarchical, deduplicated interface for standardized tables (Countries, Timezones, etc.). It supports global defaults, account overrides, and site-specific whitelisting.
|
||||
The V3 Lookup system provides a hierarchical, deduplicated interface for standardized reference tables (Countries, Timezones, etc.). It supports global defaults, account-level overrides, and object-level overrides, with optional site-specific whitelisting.
|
||||
|
||||
### How the hierarchy works
|
||||
|
||||
Each lookup table (`lu_v3_country`, `lu_v3_time_zone`, etc.) can hold multiple rows for the same logical item at different scopes:
|
||||
|
||||
| Scope | `account_id` | `for_type` / `for_id` | Wins over |
|
||||
|---|---|---|---|
|
||||
| Global default | `NULL` | `NULL` / `NULL` | nothing |
|
||||
| Account override | set | `NULL` / `NULL` | Global default |
|
||||
| Object override | set | set | Account override + Global default |
|
||||
|
||||
The API uses `ROW_NUMBER() PARTITION BY group` to collapse all rows for the same item down to the single highest-priority winner before returning results. **`group` is the identity key** — it is what makes two rows "the same item competing for priority."
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **The `group` field is not a display label.** It is the deduplication key. Each lookup type uses a different natural key for `group`:
|
||||
>
|
||||
> | Lookup type | `group` value | Example |
|
||||
> |---|---|---|
|
||||
> | `country` | ISO alpha-2 code | `"US"`, `"CA"`, `"GB"` |
|
||||
> | `country_subdivision` | subdivision code | `"US-NY"`, `"CA-ON"` |
|
||||
> | `time_zone` | IANA timezone name | `"America/New_York"`, `"US/Eastern"` |
|
||||
>
|
||||
> For `time_zone`, `group` and `name` must always be identical — there is no concept of "override all US timezones as a group." Each timezone is its own identity.
|
||||
|
||||
### A. List Lookups
|
||||
Retrieve a ranked and filtered list of lookup items.
|
||||
|
||||
Retrieve the deduplicated, ranked list for a lookup type.
|
||||
|
||||
* **Endpoint:** `GET /v3/lookup/{lu_type}/list`
|
||||
* **Available Types:** `country`, `country_subdivision`, `time_zone`
|
||||
* **Parameters:**
|
||||
* `site_id` (Optional): Random ID of the site to apply a **Whitelist Policy**.
|
||||
* `only_priority` (Optional): Set to `true` to return only high-priority items (e.g., common time zones).
|
||||
* `for_type` / `for_id` (Optional): Context for object-specific overrides.
|
||||
* `include_disabled` (Optional): Set to `true` to see shadowed/disabled records.
|
||||
* `site_id` (Optional): Random ID of the site — applies a **Whitelist Policy** (see §C).
|
||||
* `only_priority` (Optional): `true` returns only `priority=1` items (e.g., common time zones).
|
||||
* `for_type` / `for_id` (Optional): Object context — activates object-level override matching.
|
||||
* `include_disabled` (Optional): `true` includes shadowed/disabled records (useful for admin views).
|
||||
|
||||
**Frontend keying:** Always key Svelte `{#each}` blocks on `group`, not `id` or `name`. `group` is guaranteed unique in the response. Keying on `id` will break if an account override wins (different `id`, same logical item).
|
||||
|
||||
### B. Resolve Identity
|
||||
Resolves a string (code, group, or name) to a single record.
|
||||
|
||||
Resolves a string to a single lookup record.
|
||||
|
||||
* **Endpoint:** `GET /v3/lookup/{lu_type}/resolve?q=VALUE`
|
||||
* **Usage:** Use this when you have an external code (e.g., ISO "US") and need the full Aether record.
|
||||
* **Usage:** Use when you have an external code (e.g., ISO `"US"`) and need the full Aether record. Scans `name`, `group`, and other identity fields.
|
||||
|
||||
### C. Site Whitelist Policy
|
||||
To limit lookups for a specific site, add a `lookup_policy` to the `site.cfg_json` field.
|
||||
**Schema:**
|
||||
|
||||
To restrict which lookup items appear for a specific site, add a `lookup_policy` to `site.cfg_json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"lookup_policy": {
|
||||
"country": ["US", "CA", "GB"],
|
||||
"time_zone": ["America/New_York"]
|
||||
"time_zone": ["America/New_York", "US/Eastern"]
|
||||
}
|
||||
}
|
||||
```
|
||||
*Note: Whitelist values must match the `group` field in the database.*
|
||||
|
||||
> **Whitelist values must match the `group` field** — i.e., the natural key for that type (ISO code for country, IANA name for time zone). Using a display name will silently return no results for that item.
|
||||
|
||||
### D. Adding and managing client overrides
|
||||
|
||||
When a client needs a customized label or wants to hide/reorder lookup items, create override records rather than modifying global defaults.
|
||||
|
||||
**Rules:**
|
||||
1. **Never modify global default rows** (`account_id = NULL`). Those are shared across all accounts. Any change there affects every client.
|
||||
2. **Set `group` to the exact same value as the global default row** for the item you are overriding. If `group` doesn't match, the override creates a new item instead of replacing the existing one.
|
||||
3. **Set `account_id`** to the client's account ID. Leave `for_type` / `for_id` null unless the override is specific to a single object (e.g., one site).
|
||||
|
||||
**Example — rename "US/Eastern" for one account:**
|
||||
|
||||
```sql
|
||||
INSERT INTO lu_v3_time_zone
|
||||
(account_id, name, name_override, `group`, enable, priority, sort)
|
||||
VALUES
|
||||
(42, 'US/Eastern', 'Eastern Time (Client Label)', 'US/Eastern', 1, 1, 50);
|
||||
```
|
||||
|
||||
The `name_override` field is the display label the frontend should prefer when set. `group = 'US/Eastern'` ensures this row competes with — and wins over — the global default in the `PARTITION BY group` deduplication.
|
||||
|
||||
**To disable an item for one account** (hide it from their dropdowns):
|
||||
|
||||
```sql
|
||||
INSERT INTO lu_v3_time_zone
|
||||
(account_id, name, `group`, enable)
|
||||
VALUES
|
||||
(42, 'US/Samoa', 'US/Samoa', 0);
|
||||
```
|
||||
|
||||
Setting `enable = 0` on an account-scoped row shadows the global default for that account only.
|
||||
|
||||
**To remove a client override** (revert to global default):
|
||||
|
||||
Simply delete the row where `account_id = <client>` and `group = '<item>'`. The global default row is unaffected and immediately resumes winning.
|
||||
|
||||
### E. Adding new global lookup items
|
||||
|
||||
When seeding new lookup data (e.g., adding timezones in bulk):
|
||||
|
||||
1. Set `group = name` for every row (for `time_zone`). This is a hard invariant — if `group` is set to a regional label like `"United States"` instead of the timezone name, the entire group collapses to a single winner and all but one entry disappear from the API response.
|
||||
2. Set `account_id = NULL` and `for_type = NULL` / `for_id = NULL` for global defaults.
|
||||
3. After seeding, verify with:
|
||||
```sql
|
||||
-- Should return 0 rows; any result means multiple items will collapse into one
|
||||
SELECT `group`, COUNT(*) AS cnt
|
||||
FROM lu_v3_time_zone
|
||||
WHERE account_id IS NULL
|
||||
GROUP BY `group`
|
||||
HAVING cnt > 1;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Aether UI — Design System Style Guidelines
|
||||
> **Version:** 1.1 (2026-03-17)
|
||||
> **Version:** 1.2 (2026-03-20)
|
||||
> **Author:** One Sky IT / Scott Idem
|
||||
> **Scope:** All Aether SvelteKit frontend components
|
||||
> **Related:** `AE__UI_Component_Patterns.md`, `ae-firefly.css`, `documentation/AE__Components.md`
|
||||
@@ -34,6 +34,7 @@ To maintain codebase health and performance, all new development must adhere to
|
||||
- **Mandatory**: Use `preset-*` classes for interactive elements (e.g., `preset-tonal-primary`).
|
||||
- **Forbidden**: Legacy Skeleton v3 `variant-*` classes.
|
||||
- **Customization**: Use Tailwind 4 `@theme` blocks for project-wide overrides.
|
||||
- **URLs**: Skeleton for Svelte for LLMs docs: https://www.skeleton.dev/llms-svelte.txt
|
||||
|
||||
### 🔣 Lucide Icons
|
||||
- **Mandatory**: Use `@lucide/svelte` components (e.g., `<Calendar size="1em" />`).
|
||||
@@ -192,6 +193,7 @@ Always wrap in `{#if $lq__obj}{...}{:else}...skeleton...{/if}` — **never** sho
|
||||
| Form inputs | Visible `<label>` linked via `for` / `id`, or explicit `aria-label` |
|
||||
| Color-only information | Always pair color coding with icon or text — never color alone |
|
||||
| Minimum touch target | 44×44px effective hit area for all tap targets |
|
||||
| Button label + icon | All buttons should include **both a Lucide icon and text label**. Icon-only is acceptable for space-constrained toolbar/header actions (with `title` attribute); text-only is acceptable when layout is extremely tight. The icon+text combination aids non-English-native users who may not read the label fluently. |
|
||||
|
||||
---
|
||||
|
||||
@@ -241,3 +243,34 @@ $events_sess.pres_mgmt.session_qr_url[$lq__obj.id] = result; // ← URL string
|
||||
- **`text-sm leading-relaxed`**: Standard for body-level descriptive text in cards.
|
||||
- **`tracking-wide uppercase`**: Use for section label/eyebrow text with `opacity-40`.
|
||||
- **`whitespace-pre-wrap`**: Required for any `<pre>` or `<p>` displaying user-entered multi-line text (preserves breaks without horizontal overflow).
|
||||
|
||||
---
|
||||
|
||||
## 12. Known Issues & Workarounds
|
||||
|
||||
### `btn` + `preset-filled-*` resolves to transparent inside `card` components
|
||||
|
||||
**Symptom:** A button using `btn preset-filled-primary` (or any `preset-filled-*`) inside a `card` div renders with `background-color: transparent`, making it invisible against the card surface.
|
||||
|
||||
**Root cause:** The Skeleton v4 `btn` class sets a transparent background via a CSS variable chain. When nested inside a `card` element, the `preset-filled-*` class fails to win the specificity battle and the button appears invisible. This affects both light and dark mode.
|
||||
|
||||
**Workaround:** Skip `btn` and `preset-filled-*` entirely for buttons inside `card` elements. Use direct Tailwind token classes instead:
|
||||
|
||||
```svelte
|
||||
<!-- ✅ Correct — works reliably inside cards -->
|
||||
<button class="w-full rounded-xl py-5 font-bold flex items-center justify-center gap-2
|
||||
bg-primary-500 text-white hover:brightness-110 transition-all cursor-pointer">
|
||||
...
|
||||
</button>
|
||||
|
||||
<!-- Secondary / cancel button inside a card -->
|
||||
<button class="w-full rounded-lg py-3 text-sm font-medium flex items-center justify-center gap-2
|
||||
border border-surface-500/40 hover:bg-surface-200-800 transition-colors cursor-pointer opacity-70">
|
||||
...
|
||||
</button>
|
||||
|
||||
<!-- ❌ Broken inside card — do not use -->
|
||||
<button class="btn btn-xl preset-filled-primary">...</button>
|
||||
```
|
||||
|
||||
**Scope:** `btn` + `preset-*` classes work correctly on standalone buttons (e.g. page headers, nav bars). The issue is specific to the `card` component context. If we migrate away from Skeleton `card`/`btn`, this issue goes away.
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
2. **Type Safety:** Ensure interfaces in `src/lib/types/ae_types.ts` match backend schemas.
|
||||
3. **Reactivity Check:** Verify Svelte 5 runes (`$state`, `$derived`) are not creating race conditions with Dexie `liveQuery`.
|
||||
4. **Build Check:** For major changes, run `npm run build:staging` to ensure no SSR or build-time failures.
|
||||
5. **Integration Tests:** For changes to badge print, event layouts, or auth/store logic, run the relevant Playwright test file(s):
|
||||
```bash
|
||||
npx playwright test tests/event_badge_render.test.ts tests/event_badge_attendee_workflow.test.ts
|
||||
```
|
||||
Run the full suite with `npm run test:integration`. The badge tests (`event_badge_*.test.ts`) are the canonical integration test template.
|
||||
|
||||
## 2. Commit Policy
|
||||
- **Atomic Commits:** One component or one logic fix per commit. Do not batch unrelated changes.
|
||||
@@ -46,8 +51,81 @@ You are not working in a vacuum. Coordinate with the Backend Agent via MCP tools
|
||||
| `documentation/AE__Architecture.md` | System architecture overview |
|
||||
| `documentation/AE__Naming_Conventions.md` | Naming rules |
|
||||
| `documentation/PROJECT__AE_Events_Launcher_Native_integration.md` | Electron/Launcher reference |
|
||||
| `tests/README.md` | Playwright test guide — shared helpers, hard-won lessons, demo IDs |
|
||||
|
||||
## 6. URL Parameters
|
||||
## 6. Inline Field Editing — `element_ae_obj_field_editor`
|
||||
|
||||
The standard component for single-field inline editing throughout the platform. Wraps a `PATCH /v3/crud/{obj_type}/{obj_id}` call behind a click-to-edit UI that respects `$ae_loc.edit_mode`.
|
||||
|
||||
```svelte
|
||||
import Element_ae_obj_field_editor from '$lib/elements/element_ae_obj_field_editor.svelte';
|
||||
```
|
||||
|
||||
### Basic usage — text field with custom display
|
||||
|
||||
Wrap the display content in the default snippet. The component renders it in view mode and swaps in the input on edit.
|
||||
|
||||
```svelte
|
||||
<Element_ae_obj_field_editor
|
||||
object_type={'event_session'}
|
||||
object_id={session.id}
|
||||
field_name={'name'}
|
||||
field_type={'text'}
|
||||
current_value={session.name}
|
||||
on_success={() => events_func.load_ae_obj_id__event_session({ api_cfg: $ae_api, event_session_id: session.id })}
|
||||
>
|
||||
<h1 class="text-2xl font-bold">{session.name}</h1>
|
||||
</Element_ae_obj_field_editor>
|
||||
```
|
||||
|
||||
### Field types
|
||||
|
||||
| `field_type` | Input rendered |
|
||||
| --- | --- |
|
||||
| `text` (default) | `<input type="text">` — Enter key saves |
|
||||
| `textarea` | `<textarea>` — use `textarea_rows` prop |
|
||||
| `select` | `<select>` — pass `select_options={{ value: 'Label' }}` |
|
||||
| `checkbox` | `<input type="checkbox">` — shows Enabled/Disabled |
|
||||
| `tiptap` | TipTap rich-text editor |
|
||||
| `date` | `<input type="date">` |
|
||||
| `datetime` | `<input type="datetime-local">` |
|
||||
| `number` | `<input type="number">` — Enter key saves |
|
||||
|
||||
### Select with nullable FK
|
||||
|
||||
```svelte
|
||||
<Element_ae_obj_field_editor
|
||||
object_type={'event_presenter'}
|
||||
object_id={presenter.event_presenter_id}
|
||||
field_name={'person_id'}
|
||||
field_type={'select'}
|
||||
current_value={presenter.person_id}
|
||||
select_options={$slct.person_obj_kv}
|
||||
allow_null={$ae_loc.administrator_access}
|
||||
on_success={() => events_func.load_ae_obj_id__event_presenter({ api_cfg: $ae_api, event_presenter_id: presenter.event_presenter_id })}
|
||||
>
|
||||
{presenter.person_id ?? 'Not linked'}
|
||||
</Element_ae_obj_field_editor>
|
||||
```
|
||||
|
||||
### Key props
|
||||
|
||||
| Prop | Default | Notes |
|
||||
| --- | --- | --- |
|
||||
| `current_value` | — | Required. Bound with `$bindable` — liveQuery updates flow through automatically |
|
||||
| `allow_null` | `false` | Shows a "Set Null" button in edit mode |
|
||||
| `display_block` | `false` | Makes the wrapper `display: block` instead of `inline-block` |
|
||||
| `on_success` | — | Callback after successful PATCH — use to trigger SWR cache refresh |
|
||||
| `object_reload` | `true` | Triggers internal SWR reload after patch (in addition to `on_success`) |
|
||||
|
||||
### Behavior notes
|
||||
- The edit trigger button is `visibility: hidden` (not `display: none`) when `$ae_loc.edit_mode` is off — this preserves layout so the page doesn't shift when edit mode toggles.
|
||||
- Optimistic display: draft value is shown immediately after save; cleared once liveQuery confirms the update came back from the DB.
|
||||
- `on_success` should always call the relevant `load_ae_obj_id__*` function to keep Dexie in sync.
|
||||
|
||||
---
|
||||
|
||||
## 7. URL Parameters
|
||||
|
||||
URL params consumed by the app. Params are read by layouts and applied on mount.
|
||||
|
||||
|
||||
@@ -280,6 +280,66 @@ If you must use non-blocking loads, you must pass the initial data to the compon
|
||||
{/if}
|
||||
```
|
||||
|
||||
## The `untrack()` Reactive-Tracking Trap
|
||||
|
||||
`untrack()` is used inside `$effect` to read reactive values without registering them as tracked dependencies of that effect. This is correct for most "read-once" values (params, IDs) where you don't want the effect re-running on every change. But it has a silent failure mode: if a value you *need* to re-read is consumed inside `untrack()`, the effect becomes a one-shot and never retries when that value changes.
|
||||
|
||||
### Symptom
|
||||
|
||||
An effect runs once, reads a store value inside `untrack()`, takes an early-exit path (e.g. "no API key → skip"), and never retries — even after the store value is updated by a background process.
|
||||
|
||||
### Real Example (IDAA Novi Verification Bug — 2026-03-25)
|
||||
|
||||
The IDAA layout verifies Novi UUIDs. `site_cfg_json` (which contains the Novi API key) was read **inside** `untrack()`:
|
||||
|
||||
```typescript
|
||||
// BUG: site_cfg_json read inside untrack → one-shot, never retries
|
||||
$effect(() => {
|
||||
if (!browser) return;
|
||||
const uuid = data.url.searchParams.get('uuid'); // tracked ✓
|
||||
|
||||
untrack(() => {
|
||||
const site_cfg_json = $ae_loc.site_cfg_json; // ← NOT tracked ✗
|
||||
const api_key = site_cfg_json?.novi_idaa_api_key ?? null;
|
||||
if (!api_key) return; // exits silently on first load with stale cache
|
||||
verify_novi_uuid(uuid, api_key, ...);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
On first load, the Dexie cache returned a stale `site_cfg_json` missing the API key. The effect exited early. The background refresh later updated `$ae_loc.site_cfg_json`, but because `site_cfg_json` was consumed inside `untrack()`, the effect never re-ran.
|
||||
|
||||
**Fix:** Move the dependency read **outside** `untrack()`:
|
||||
|
||||
```typescript
|
||||
// FIX: site_cfg_json tracked outside untrack → effect re-runs when it changes
|
||||
$effect(() => {
|
||||
if (!browser) return;
|
||||
const uuid = data.url.searchParams.get('uuid'); // tracked ✓
|
||||
const site_cfg_json = $ae_loc.site_cfg_json; // tracked ✓ — effect re-runs on change
|
||||
|
||||
untrack(() => {
|
||||
// Guard: already verified for this UUID — don't repeat the round-trip
|
||||
if ($idaa_loc.novi_verified && $idaa_loc.novi_uuid === uuid) return;
|
||||
|
||||
const api_key = site_cfg_json?.novi_idaa_api_key ?? null;
|
||||
if (!api_key) return;
|
||||
verify_novi_uuid(uuid, api_key, ...);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
The guard inside `untrack()` is important: without it, every unrelated change to `$ae_loc` would re-trigger verification.
|
||||
|
||||
### Rule of Thumb
|
||||
|
||||
Before wrapping a store read in `untrack()`, ask: **"Do I need this effect to re-run if this value changes?"**
|
||||
|
||||
- If yes → read it **outside** `untrack()`, and add a guard inside to prevent redundant work.
|
||||
- If no → `untrack()` is correct.
|
||||
|
||||
---
|
||||
|
||||
## Svelte 5 Binding Pitfalls
|
||||
|
||||
### 1. `props_invalid_value` (The "Expression Binding" Error)
|
||||
|
||||
@@ -358,7 +358,7 @@ Firefox users can use "Save to PDF" directly — it just works.
|
||||
|
||||
- [x] Wire `style_href` via `<svelte:head>` in print page — done in `print/+page.svelte`; also in `properties_to_save`. (2026-03-18 verified)
|
||||
- [x] Add `duplex` to `properties_to_save` — done. (2026-03-18 verified)
|
||||
- [x] Add `duplex`-driven suppression to `badge_back` section — done in `ae_comp__badge_obj_view_v2.svelte`; `show_badge_back` derived from `duplex` field. Note: v1 (`ae_comp__badge_obj_view.svelte`) was archived to `~/tmp/gemini_trash/`; v2 is canonical. (2026-03-18 verified)
|
||||
- [x] Add `duplex`-driven suppression to `badge_back` section — done in `ae_comp__badge_obj_view.svelte`; `show_badge_back` derived from `duplex` field.
|
||||
- [ ] Make `layout` field drive actual card dimensions in the badge component — currently the Zebra ZC10L layout CSS (`badge_layout_zebra_zc10l_pvc.css`) sets dimensions correctly via `[data-layout="..."]` scoping, but fanfold layouts still use Tailwind defaults. Needs proper CSS for each layout code.
|
||||
- [ ] Remove dead `exhibitor_info` / `presenter_info` / `staff_info` / `vip_info` / `vote_info` `{#if}` blocks from `ae_comp__badge_obj_view_v2.svelte` (if they were carried over from v1)
|
||||
- [ ] Remove dead `exhibitor_info` / `presenter_info` / `staff_info` / `vip_info` / `vote_info` `{#if}` blocks from `ae_comp__badge_obj_view.svelte` (if they were carried over from v1)
|
||||
- [ ] Improve `ae_comp__badge_template_form.svelte` to edit all relevant fields (currently minimal)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## Overview
|
||||
|
||||
The Badges module manages event attendee badges with support for:
|
||||
- **External system imports** (iMIS, Zoom, Novi, Impexium, Confex, Cvent, and others)
|
||||
- **External system imports as needed** (CSV/Excel, iMIS, Zoom, Novi, Impexium, Confex, Cvent, and others)
|
||||
- **Field override protection** to prevent staff/attendee edits from being overwritten by automated syncs
|
||||
- **Multi-tier access control** for field editing
|
||||
- **QR code generation** for badge scanning
|
||||
@@ -535,8 +535,8 @@ Button has `data-testid="badge-print-btn"` and shows loading/done/error states w
|
||||
**Indexed Fields:**
|
||||
```typescript
|
||||
badge: `
|
||||
event_badge_id_random, event_badge_id, id,
|
||||
event_id, event_id_random,
|
||||
event_badge_id, id,
|
||||
event_id,
|
||||
full_name, full_name_override, email, email_override,
|
||||
affiliations, affiliations_override,
|
||||
badge_type, badge_type_code, badge_type_code_override, badge_type_override,
|
||||
@@ -704,7 +704,7 @@ delete_ae_obj_id__event_badge({ event_badge_id, event_id, method })
|
||||
|
||||
### Key Test Lessons Learned
|
||||
|
||||
**Search API path is FLAT, not nested.** `search_ae_obj_v3` builds `/v3/crud/{obj_type}/search` — always flat regardless of the parent relationship. Mocks must match this:
|
||||
**Search API path is FLAT, not nested.** `search_ae_obj` builds `/v3/crud/{obj_type}/search` — always flat regardless of the parent relationship. Mocks must match this:
|
||||
```typescript
|
||||
// CORRECT — flat path
|
||||
url.includes('/v3/crud/event_badge/search') && method === 'POST'
|
||||
@@ -712,7 +712,7 @@ url.includes('/v3/crud/event_badge/search') && method === 'POST'
|
||||
url.includes(`/v3/crud/event/${event_id}/event_badge/search`) && method === 'POST'
|
||||
```
|
||||
|
||||
**List API (GET) is also FLAT with query params.** `get_ae_obj_li_v3` builds `/v3/crud/{obj_type}/?for_obj_id=...` — always flat. Mocks must check `url.includes('/v3/crud/event_badge_template/') && url.includes('for_obj_id')`.
|
||||
**List API (GET) is also FLAT with query params.** `get_ae_obj_li` builds `/v3/crud/{obj_type}/?for_obj_id=...` — always flat. Mocks must check `url.includes('/v3/crud/event_badge_template/') && url.includes('for_obj_id')`.
|
||||
|
||||
**CSS `input[value*=...]` selectors don't work with Svelte bind:value.** The CSS selector checks the HTML *attribute*; Svelte's `bind:value` sets the DOM *property* only. In Playwright tests, use `page.getByLabel()` or `locator.inputValue()` instead.
|
||||
|
||||
@@ -726,9 +726,9 @@ All API mock responses in tests need these fields.
|
||||
|
||||
**Badge view requires both badge AND template.** `ae_comp__badge_obj_view.svelte` wraps everything in `{#if $lq__event_badge_obj && $lq__event_badge_template_obj}` — if the template isn't loaded, edit/print buttons and the badge itself don't render. Tests must mock the badge template endpoint.
|
||||
|
||||
**Badge GET endpoint (single object):** `/v3/crud/event_badge/{id}` (NOT nested under event). Matches `api.get_ae_obj_v3()` which uses the flat path.
|
||||
**Badge GET endpoint (single object):** `/v3/crud/event_badge/{id}` (NOT nested under event). Matches `api.get_ae_obj()` which uses the flat path.
|
||||
|
||||
**Badge PATCH endpoint (update):** `/v3/crud/event/${event_id}/event_badge/${badge_id}` (nested under event). Matches `api.patch_ae_obj_v3()` which uses the nested path.
|
||||
**Badge PATCH endpoint (update):** `/v3/crud/event/${event_id}/event_badge/${badge_id}` (nested under event). Matches `api.patch_ae_obj()` which uses the nested path.
|
||||
|
||||
**Use `data-testid` for test selectors.** Key buttons have targets: `badge-edit-btn`, `badge-save-btn`, `badge-cancel-btn`, `badge-print-btn`, `badge-professional-title-input`.
|
||||
|
||||
|
||||
207
documentation/MODULE__AE_Events_Badges_Onsite.md
Normal file
207
documentation/MODULE__AE_Events_Badges_Onsite.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# Aether Events — Onsite Badge Printing
|
||||
|
||||
Notes on setup, process, hardware, and browser behavior for onsite badge printing at events.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Aether badge printing uses the browser's native `window.print()` — no special software or print
|
||||
server needed. The badge render page (`/events/[event_id]/badges/print/[badge_id]`) outputs
|
||||
print-ready HTML/CSS, and the browser sends it directly to the connected printer via CUPS (Linux)
|
||||
or the OS print system (macOS/Windows).
|
||||
|
||||
Chrome (Chromium) is the recommended browser for onsite kiosk stations.
|
||||
Firefox is a solid alternative, especially for Save-to-PDF workflows.
|
||||
|
||||
---
|
||||
|
||||
## Recommended Workflow — Onsite Kiosk
|
||||
|
||||
1. Open the event's badge printing page: `/events/[event_id]/badges`
|
||||
2. Search for the attendee (name, badge ID, or QR scan)
|
||||
3. Open the badge print page — review the rendered badge
|
||||
4. Click **Print Badge** in the controls panel (or use keyboard shortcut)
|
||||
5. In the browser print dialog:
|
||||
- Set Margins to **None** (Chrome) or leave defaults (Firefox)
|
||||
- Confirm paper/card size matches the stock loaded in the printer
|
||||
- Print
|
||||
6. `print_count` increments automatically on each print via the Print Badge button
|
||||
|
||||
For high-volume events, consider the **rapid QR scan** mode in the Leads module or using a
|
||||
dedicated kiosk session where the operator only handles physical card handoff.
|
||||
|
||||
---
|
||||
|
||||
## Browser Settings
|
||||
|
||||
### Chrome / Chromium (Recommended for kiosk use)
|
||||
|
||||
Chrome is recommended for onsite badge printing stations. Key print dialog settings:
|
||||
|
||||
| Setting | Correct value | Notes |
|
||||
|---|---|---|
|
||||
| Margins | **None** or **Minimum** | Default margins add URL/date headers — breaks badge centering |
|
||||
| Paper size | Match card stock (e.g. 3.5" × 5.5") | Zebra driver may override this automatically |
|
||||
| Background graphics | **On** | Required for colored header/footer stripe to print |
|
||||
| Pages | 1 | PVC single-sided — only front should print |
|
||||
|
||||
**Important:** Chrome ignores CSS `@page { size }` for Save to PDF — it defaults to letter/A4.
|
||||
For physical printer output, the printer driver controls paper size. This is expected behavior.
|
||||
|
||||
To lock Chrome settings for a kiosk, set Margins to "None" once and Chrome remembers per-printer.
|
||||
|
||||
### Firefox
|
||||
|
||||
Firefox honors CSS `@page { size }` which makes it ideal for PDF generation.
|
||||
For physical printing, Firefox generally "just works" without margin adjustments.
|
||||
|
||||
| Setting | Notes |
|
||||
|---|---|
|
||||
| Paper size | Can be set in dialog, but CSS `@page { size }` is honored |
|
||||
| Margins | Default is usually fine; remove headers/footers if they appear |
|
||||
| Background graphics | Enable for colored stripes and header images to print |
|
||||
|
||||
### General Notes
|
||||
|
||||
- **Background graphics must be enabled** in any browser — otherwise header images, footer
|
||||
color stripes, and tonal backgrounds will not print.
|
||||
- Private/incognito mode blocks PWA install prompts — use normal browser sessions for kiosk.
|
||||
- For highest reliability, set the kiosk machine to auto-login and open Chrome to the event URL.
|
||||
|
||||
---
|
||||
|
||||
## Linux / CUPS Setup
|
||||
|
||||
For Linux workstations and dedicated kiosk machines running Linux:
|
||||
|
||||
1. Install CUPS if not already present: `sudo pacman -S cups` (Arch) or equivalent
|
||||
2. Start the CUPS service: `sudo systemctl enable --now cups`
|
||||
3. Open the CUPS web UI: `http://localhost:631`
|
||||
4. Add the printer and install the appropriate driver (see per-printer sections below)
|
||||
5. Print a test page from CUPS to confirm card feed and quality
|
||||
6. In Chrome: select the CUPS printer name under Destination in the print dialog
|
||||
|
||||
On macOS and Windows, use the vendor-provided driver installer.
|
||||
|
||||
---
|
||||
|
||||
## Printers
|
||||
|
||||
---
|
||||
|
||||
### Zebra ZC10L — PVC Card Printer
|
||||
|
||||
**Card stock:** 3.5" × 5.5" PVC cards (CR80 extended)
|
||||
**Tested:** 2026-03-17 (rental test day, Arch Linux)
|
||||
**Status:** Working. Confirmed suitable for Axonius NYC (mid-April 2026).
|
||||
|
||||
#### Physical Setup
|
||||
|
||||
- Connect via USB (the ZC10L supports USB and Ethernet)
|
||||
- Load PVC card stock per the Zebra loading instructions — cards face-up, landscape
|
||||
- The ZC10L prints one side (single-sided dye-sub thermal); do not attempt duplex on PVC stock
|
||||
|
||||
#### Linux Driver
|
||||
|
||||
- Download the Zebra ZC10L CUPS driver from zebra.com (ZC Series Linux support)
|
||||
- Install the `.deb` or extract the PPD file and add to CUPS manually
|
||||
- In CUPS (`http://localhost:631`), add the printer and select the ZC10L PPD
|
||||
- Set default paper size to **3.5" × 5.5"** (or CR80 Extended if listed)
|
||||
- Print a blank test page from CUPS before using Chrome
|
||||
|
||||
> **Note:** Driver version tested: *(update here after confirming)*
|
||||
> CUPS printer name used: *(update here after setup)*
|
||||
|
||||
#### Chrome Print Settings (ZC10L)
|
||||
|
||||
| Setting | Value |
|
||||
|---|---|
|
||||
| Destination | Zebra ZC10L (CUPS name) |
|
||||
| Paper size | 3.5 × 5.5 in (or as set in CUPS) |
|
||||
| Margins | **None** |
|
||||
| Background graphics | On |
|
||||
| Pages | 1 (front only) |
|
||||
|
||||
#### CSS Layout
|
||||
|
||||
The ZC10L uses the `badge_3.5x5.5_pvc` layout. The PVC layout CSS is at:
|
||||
`src/routes/events/[event_id]/(badges)/badges/print/badge_layout_zebra_zc10l_pvc.css`
|
||||
|
||||
This layout hides `.badge_back` in `@media print` — only the front face prints.
|
||||
`@page { size: 3.5in 5.5in; margin: 0; }` is set in the CSS.
|
||||
|
||||
#### Known Behaviors / Watch-outs
|
||||
|
||||
- Chrome with **Default** margins: inserts URL/date headers, offsets badge — use **None**
|
||||
- Chrome with **None** or **Minimum** margins: correct output
|
||||
- Firefox: works correctly out of the box with this layout
|
||||
- Physical card alignment: if the badge appears offset on the card, a CSS margin tweak may
|
||||
be needed in the PVC layout file — note the offset and adjust `print_margin_cfg` once that
|
||||
field is wired to the UI
|
||||
- Font sizes: if name/affiliation text appears too small at physical scale, adjust via the
|
||||
font size controls (+ / −) in the print controls panel; note the preferred values for this
|
||||
event's template
|
||||
|
||||
#### Test Results (2026-03-19)
|
||||
|
||||
- Card feeds and prints without jam: ✅
|
||||
- Single-sided PVC confirmed (back does not print): ✅
|
||||
- Chrome margins None/Minimum: correct output ✅
|
||||
- Firefox: correct output ✅
|
||||
- QR code scannable from printed card: ✅
|
||||
- Print tracking (`print_count` increment): ✅
|
||||
- Font sizes / visual quality: tested; specific calibration pending client design direction
|
||||
- Driver version: *(record here)*
|
||||
- Physical offset needed: *(record if any)*
|
||||
|
||||
---
|
||||
|
||||
### Epson — Fan-Fold / Label Printer
|
||||
|
||||
**Status:** Not yet tested. Section to be filled in after testing.
|
||||
|
||||
Common Epson models used for fan-fold name badge stock: TM-T88 series, C3500, LX series.
|
||||
Fan-fold stock is typically 4" × 3" or 4" × 6" paper labels.
|
||||
|
||||
#### CSS Layout
|
||||
|
||||
Fan-fold badges would use a layout sized to the specific label stock.
|
||||
A new CSS layout file will need to be created per stock size if not already present.
|
||||
Naming convention: `badge_layout_epson_[model]_[size].css`
|
||||
|
||||
#### Setup Notes
|
||||
|
||||
*(To be filled in after testing — cover: driver source, CUPS setup, paper size, Chrome settings)*
|
||||
|
||||
#### Known Behaviors
|
||||
|
||||
*(To be filled in after testing)*
|
||||
|
||||
---
|
||||
|
||||
## Print Tracking
|
||||
|
||||
The badge print page tracks print counts per badge:
|
||||
|
||||
- `print_count` — increments on each **Print Badge** button click
|
||||
- `print_first_datetime` — timestamp of first print
|
||||
- An amber "Printed N×" chip appears in the print page header after the first print
|
||||
|
||||
The reprint shortcut (trusted access + edit mode) does **not** increment the count.
|
||||
Only the **Print Badge** button path increments the count. This is intentional — reprints
|
||||
for alignment or quality checks should not inflate the print count.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Cause | Fix |
|
||||
|---|---|---|
|
||||
| White border around printed badge | Chrome Default margins | Change to None or Minimum |
|
||||
| URL / date printed at top or bottom | Chrome Default margins | Change to None |
|
||||
| Header image / stripe not printing | Background graphics disabled | Enable in print dialog |
|
||||
| Badge appears on wrong-size output | Paper size mismatch | Set correct size in CUPS and/or print dialog |
|
||||
| Card jams | Card stock misloaded | Re-seat cards per printer manual; check stock orientation |
|
||||
| Badge content clipped | Layout overflow | Check font size — use − control to reduce if needed |
|
||||
| Second blank card ejected | Duplex triggered on PVC | Confirm `.badge_back { display: none }` in print CSS for this layout |
|
||||
262
documentation/MODULE__AE_Events_Exhibitor_Leads.md
Normal file
262
documentation/MODULE__AE_Events_Exhibitor_Leads.md
Normal file
@@ -0,0 +1,262 @@
|
||||
# Aether Events — Exhibitor Leads Module (v3)
|
||||
|
||||
**Status:** Implemented and ready for demo. Core lead capture flow works end-to-end.
|
||||
**Platform:** PWA only — mobile-first, offline-capable.
|
||||
**Target users:** Conference exhibitors scanning attendee badges at their booths.
|
||||
|
||||
---
|
||||
|
||||
## What It Does
|
||||
|
||||
The Exhibitor Leads module lets conference exhibitors capture and manage attendee leads directly
|
||||
from their booth. Exhibitors scan or search attendee badges and build a list of contacts they met.
|
||||
All data is cached locally (IndexedDB / Dexie.js) for spotty or offline venue Wi-Fi, with
|
||||
background SWR revalidation against the API when the network is available.
|
||||
|
||||
Key capabilities:
|
||||
|
||||
- **Badge scanning** — QR scan or text search (name, email, affiliations, badge ID)
|
||||
- **Lead list** — filterable/sortable, per-exhibitor or per-staff-member view
|
||||
- **Lead detail** — custom question responses, notes (rich text), priority flag, hide/unhide
|
||||
- **Export** — CSV/XLSX download of all leads for an exhibit
|
||||
- **License management** — assign staff accounts (email + passcode) per max license count
|
||||
- **Custom questions** — configurable per-exhibit follow-up questions (ratings, dropdowns, text)
|
||||
- **Offline-first** — IndexedDB cache survives network drops; syncs on reconnect
|
||||
- **PWA install** — Chrome/Android native install prompt; iOS Safari "Add to Home Screen" nudge
|
||||
|
||||
---
|
||||
|
||||
## Access Levels
|
||||
|
||||
Three sign-in levels are supported within this module:
|
||||
|
||||
| Level | How to sign in | What they can do |
|
||||
|---|---|---|
|
||||
| **Aether Platform Auth** | Standard Aether login (manager/trusted access) | Full admin bypass; all exhibit data |
|
||||
| **Shared Exhibit Passcode** | Enter booth's `staff_passcode` | Manage licenses, view/add leads |
|
||||
| **Licensed User** | Email + individual passcode from `license_li_json` | Add and manage leads for this booth |
|
||||
|
||||
Auth state is persisted in `$events_loc.leads.auth_exhibit_kv[exhibit_id]` (localStorage-backed).
|
||||
|
||||
A booth only shows in the landing page search to non-admins if it is marked `priority = true` (i.e. paid).
|
||||
|
||||
### `allow_tracking` Opt-In
|
||||
|
||||
Attendees must have `allow_tracking = true` on their badge record to be added as a lead.
|
||||
Attendees without this flag are blocked at both the QR scanner and the manual search:
|
||||
- QR scan shows a "Tracking Blocked" warning card (`ShieldOff` icon)
|
||||
- Manual search shows an "Opt-Out" badge per result row; the "Add as Lead" button is suppressed
|
||||
|
||||
---
|
||||
|
||||
## Route Structure
|
||||
|
||||
```
|
||||
/events/[event_id]/leads/
|
||||
→ Exhibit search / landing page — find your booth
|
||||
|
||||
/events/[event_id]/leads/exhibit/[exhibit_id]/
|
||||
→ Main exhibitor view — all 4 tabs
|
||||
|
||||
/events/[event_id]/leads/exhibit/[exhibit_id]/lead/[exhibit_tracking_id]/
|
||||
→ Lead detail view — edit notes, custom responses, flags
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Module Tabs
|
||||
|
||||
### Tab 1 — Start / Sign In
|
||||
|
||||
The only tab visible when not signed in as a licensed leads user.
|
||||
|
||||
- **Sign in with shared passcode** — grants booth management access (license management, passcode change)
|
||||
- **Sign in as licensed user** — grants lead capture access (email + passcode)
|
||||
- **PWA install prompt** — Chrome/Android native install button; iOS "Share → Add to Home Screen" instructions
|
||||
- **License list** — shown when signed in via shared passcode or Aether admin; add/edit/remove staff slots
|
||||
|
||||
### Tab 2 — Add Leads
|
||||
|
||||
Visible only when signed in (licensed user or Aether auth).
|
||||
|
||||
- **Text search** — search by name, email, affiliations, badge ID
|
||||
- **QR scan** — three modes (persisted per exhibit in `tab_scan_qualify`):
|
||||
- **Confirm** (`rapid`) — scan, then choose per badge: **Add & Scan Next** (resets after 2s) or **Add & View Lead** (navigates to detail)
|
||||
- **Auto** — no confirmation tap; adds immediately and auto-resets (high-throughput)
|
||||
- **Multi** — BarcodeDetector batch scan; up to 4 badges in one frame as a confirm grid
|
||||
- Previously-removed leads detected on scan — shown a "Previously Removed" card with **Restore & Scan Next** / **Restore & View Lead** buttons
|
||||
- Results show "Add as Lead" or "View Lead" depending on whether already captured
|
||||
- `external_person_id` and `group` resolved by auth type — see [Capture Identity](#capture-identity) below
|
||||
|
||||
### Tab 3 — Leads List
|
||||
|
||||
The main lead management view.
|
||||
|
||||
- **Search** — full-text across name, email, notes (local IDB fast path + API revalidation)
|
||||
- **Sort** — Newest first, Oldest first, Name A→Z, Name Z→A
|
||||
- **Filter by staff member** — "All Leads" or filter by individual licensed user
|
||||
- **Show/hide hidden records** — toggles `hide` filter on IDB and API results
|
||||
- **Export** — downloads CSV/XLSX for the exhibit (`leads_api_access` required)
|
||||
|
||||
### Tab 4 — Manage / Config
|
||||
|
||||
Exhibit configuration and app settings.
|
||||
|
||||
**Admin Tools** (manager_access only):
|
||||
- Payment status toggle (`priority` field)
|
||||
- Max licenses, small/large device counts
|
||||
|
||||
**Booth Profile** (all signed-in users):
|
||||
- Exhibitor name, booth description (rich text)
|
||||
|
||||
**Access & Security**:
|
||||
- View/change shared staff passcode
|
||||
- Sign out button
|
||||
|
||||
**Lead Retrieval Config**:
|
||||
- Exhibit Leads Licensees — manage staff accounts (`administrator_access` only; gap: should also allow shared-passcode users — see Known Gaps)
|
||||
- Qualifiers & Questions — custom question config
|
||||
- Licenses & Billing — stub (Stripe not yet implemented)
|
||||
|
||||
**App Settings**:
|
||||
- Auto-hide header/footer toggle
|
||||
- Show Payment Tab toggle
|
||||
- Show Extra Details toggle
|
||||
- Refresh interval (1–120 seconds, default 25s), countdown timer, last-refresh timestamp
|
||||
- Reload App, Clear IDB, Hard Reset (clears localStorage)
|
||||
|
||||
---
|
||||
|
||||
## Data Model
|
||||
|
||||
### `event_exhibit`
|
||||
One exhibitor's presence at an event.
|
||||
|
||||
| Field | Purpose |
|
||||
|---|---|
|
||||
| `event_exhibit_id` | Primary / URL-safe ID |
|
||||
| `name` | Exhibitor display name |
|
||||
| `code` | Booth number |
|
||||
| `staff_passcode` | Shared sign-in code |
|
||||
| `priority` | `1` = paid/active |
|
||||
| `license_max` | Max licensed staff slots |
|
||||
| `license_li_json` | Array of `{ full_name, email, passcode }` |
|
||||
| `leads_custom_questions_json` | Array of question definitions |
|
||||
| `leads_device_sm_qty` / `leads_device_lg_qty` | Device count tracking |
|
||||
|
||||
### `event_exhibit_tracking`
|
||||
One captured lead — links an exhibit to a badge.
|
||||
|
||||
| Field | Purpose |
|
||||
|---|---|
|
||||
| `event_exhibit_tracking_id` | Primary key |
|
||||
| `event_exhibit_id` | Parent exhibit |
|
||||
| `event_badge_id` | Captured attendee's badge |
|
||||
| `external_person_id` | Capturing staff's email (from license) |
|
||||
| `exhibitor_notes` | Rich text notes (HTML via TipTap) |
|
||||
| `responses_json` | `{ [question_code]: { response: value } }` |
|
||||
| `priority` | Star/flag for high-priority leads |
|
||||
| `hide` | Soft-delete / hide from list |
|
||||
| Denormalized badge fields | `event_badge_full_name`, `event_badge_email`, `event_badge_affiliations`, `event_badge_professional_title` |
|
||||
|
||||
---
|
||||
|
||||
## Key Files
|
||||
|
||||
### Routes
|
||||
|
||||
| File | Role |
|
||||
|---|---|
|
||||
| `leads/+page.svelte` | Exhibit search/landing |
|
||||
| `leads/exhibit/[exhibit_id]/+page.svelte` | Main exhibitor view — orchestrates all tabs |
|
||||
| `leads/exhibit/[exhibit_id]/+layout.svelte` / `+layout.ts` | Layout / data load |
|
||||
| `leads/exhibit/[exhibit_id]/lead/[exhibit_tracking_id]/+page.svelte` | Lead detail |
|
||||
|
||||
### Components
|
||||
|
||||
| File | Role |
|
||||
|---|---|
|
||||
| `ae_tab__start.svelte` | Tab 1 — welcome, sign-in, license list |
|
||||
| `ae_tab__add.svelte` | Tab 2 — QR scan + text search toggle |
|
||||
| `ae_tab__manage.svelte` | Tab 4 — admin tools, booth config, app settings |
|
||||
| `ae_comp__exhibit_signin.svelte` | Sign-in UI (shared passcode + licensed user) |
|
||||
| `ae_comp__lead_qr_scanner.svelte` | QR scanner (rapid / qualify mode) |
|
||||
| `ae_comp__lead_manual_search.svelte` | Manual badge search + add |
|
||||
| `ae_comp__exhibit_tracking_search.svelte` | Lead list search/filter/sort bar |
|
||||
| `ae_comp__exhibit_tracking_obj_li.svelte` | Lead list item renderer |
|
||||
| `ae_comp__exhibit_license_list.svelte` | License slot manager |
|
||||
| `ae_comp__exhibit_custom_questions.svelte` | Custom question config editor |
|
||||
| `ae_comp__exhibit_payment.svelte` | **STUB** — Stripe placeholder |
|
||||
| `ae_comp__exhibit_search.svelte` | Exhibit search on the landing page |
|
||||
| `lead/ae_comp__lead_detail_form.svelte` | Custom question response editor |
|
||||
|
||||
### Lib Functions
|
||||
|
||||
| File | Role |
|
||||
|---|---|
|
||||
| `src/lib/ae_events/ae_events__exhibit.ts` | Exhibit load, search, create, update |
|
||||
| `src/lib/ae_events/ae_events__exhibit_tracking.ts` | Tracking load, search, create, update, export |
|
||||
|
||||
Both aggregated into `events_func` via `src/lib/ae_events/ae_events_functions.ts`.
|
||||
|
||||
---
|
||||
|
||||
## Offline / PWA Notes
|
||||
|
||||
- All data is stored in `db_events` (Dexie.js) — `exhibit` and `exhibit_tracking` tables
|
||||
- SWR pattern: IDB cache returned immediately; background API fetch updates IDB and triggers UI refresh
|
||||
- Search: local IDB first pass (fast), then API revalidation via `search__exhibit_tracking`
|
||||
- `beforeinstallprompt` event captured at module load time (`src/lib/pwa/pwa_install.svelte.ts`)
|
||||
— fires within ~1 second of page load, before any Svelte `$effect` runs
|
||||
- iOS Safari: no native install prompt; shows "Share → Add to Home Screen" instructions instead
|
||||
|
||||
---
|
||||
|
||||
## Capture Identity
|
||||
|
||||
`external_person_id` and `group` on every `event_exhibit_tracking` record record who captured the lead. Resolved at capture time in all three lead capture components (single scanner, multi scanner, manual search):
|
||||
|
||||
| Auth type | `kv.type` | Value stored |
|
||||
| --- | --- | --- |
|
||||
| Licensed exhibit user | `'licensed'` | Their email address (`kv.key`) |
|
||||
| Shared exhibit passcode | `'shared'` | `'shared_passcode'` (label — raw passcode is NOT stored) |
|
||||
| Aether user (admin bypass, no kv) | `undefined` | `$ae_loc.access_type` — e.g. `'trusted'`, `'manager'`, `'super'` |
|
||||
|
||||
`kv` = `$events_loc.leads.auth_exhibit_kv[exhibit_id]` (localStorage-persisted exhibit sign-in state).
|
||||
|
||||
---
|
||||
|
||||
## Lead Soft-Delete / Re-enable
|
||||
|
||||
Leads are never hard-deleted. "Remove Lead" sets `enable = false`. Key behaviors:
|
||||
|
||||
- **Leads list** always filters out `enable = false` records (both IDB fast-path and API results) — no flash of removed records
|
||||
- **QR scanner**: if a previously-removed badge is scanned, the scanner detects it via `existing_leads_map` (IDB) or API fallback search (`search__exhibit_tracking` with `qry_badge_id` + `enabled: 'not_enabled'`) and shows the reenable card instead of an error
|
||||
- **Lead detail page**: "Remove Lead" button (two-click confirm in header) sets `enable = false` and navigates back. "Restore Lead" card appears at the bottom of the right sidebar when `enable` is falsy.
|
||||
- `search__exhibit_tracking` supports `qry_badge_id` param (added) and `enabled: 'not_enabled'` to find disabled records for a specific badge + exhibit combination
|
||||
|
||||
---
|
||||
|
||||
## Known Gaps
|
||||
|
||||
### Payment / Stripe
|
||||
`ae_comp__exhibit_payment.svelte` is a stub. The Stripe integration is not implemented.
|
||||
The payment tab can be hidden via "Show Payment Tab" in App Settings.
|
||||
|
||||
### License Management — Shared Passcode Access
|
||||
Implemented. The license section in the Manage tab is visible to Aether admins and to anyone
|
||||
signed in via the shared exhibit passcode (`auth_exhibit_kv[exhibit_id].type === 'shared'`).
|
||||
Guard in [ae_tab__manage.svelte](src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/ae_tab__manage.svelte):
|
||||
```svelte
|
||||
{#if $ae_loc.administrator_access || $events_loc.leads.auth_exhibit_kv?.[exhibit_id]?.type === 'shared'}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## OSIT Admin Notes
|
||||
|
||||
- Mark `priority = 1` on an exhibit to make it visible in public search and to enable lead capture
|
||||
- `license_max` controls how many licensed staff slots an exhibit can have
|
||||
- Export endpoint: `GET /v3/action/event_exhibit/{id}/tracking_export` — requires `leads_api_access`
|
||||
- Custom questions are stored per-exhibit in `leads_custom_questions_json` (not global)
|
||||
- The exhibitor landing page link format: `/events/[event_id]/leads/exhibit/[exhibit_exhibit_id]/`
|
||||
53
documentation/PROJECT__AE_Docker_CI_BuildKit_implement.md
Normal file
53
documentation/PROJECT__AE_Docker_CI_BuildKit_implement.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Project: AE Docker + CI BuildKit Implementation
|
||||
|
||||
**Status:** Proposed
|
||||
|
||||
**Goal:** Make Docker image builds for Aether cache-friendly using BuildKit/buildx and CI registry caching, while keeping local developer caches small and manageable.
|
||||
|
||||
Summary
|
||||
- Implement a BuildKit-friendly multi-stage `Dockerfile` pattern for frontend and API images.
|
||||
- Add CI `buildx` examples that push/read registry-based cache to avoid local disk bloat.
|
||||
- Provide cache retention/rotation guidance and developer commands for safe pruning.
|
||||
|
||||
Scope
|
||||
- Repository areas: `aether_container_env/`, root `Dockerfile` (if present), and CI pipeline definitions (Gitea/Drone or other).
|
||||
- Non-goal: full CI pipeline migration to a new provider. This work provides CI snippets and a PR-ready set of files for your CI team.
|
||||
|
||||
Deliverables (this PR)
|
||||
- `documentation/PROJECT__AE_Docker_CI_BuildKit_implement.md` (this file)
|
||||
- `aether_container_env/Dockerfile.buildkit.example` — BuildKit-friendly multi-stage Dockerfile example.
|
||||
- `aether_container_env/ci_buildx_example.sh` — standalone CI script examples (registry cache + local cache usage).
|
||||
- `documentation/AE_Docker_CI_cache_policy.md` — cache rotation and prune guidance.
|
||||
|
||||
Tasks (implementation checklist)
|
||||
- [ ] Review existing `Dockerfile`(s) under `aether_container_env/` and repository root.
|
||||
- [ ] Replace/extend Dockerfile with multi-stage BuildKit-friendly layout (use example as guide).
|
||||
- [ ] Ensure `.dockerignore` (already added) excludes large build artifacts.
|
||||
- [ ] Add CI step using `docker buildx build` with `--cache-from` and `--cache-to` pointed at a registry cache.
|
||||
- [ ] Add a scheduled job or registry lifecycle rule to delete old cache images (30 days default).
|
||||
- [ ] Document required CI secrets and permissions (registry write/read) for the operations team.
|
||||
- [ ] Run verification builds (dev local with BuildKit; CI runs with cache) and record timings.
|
||||
|
||||
Verification
|
||||
- Local dev: `DOCKER_BUILDKIT=1` build with `--cache-to`/`--cache-from` shows cache hits on second run and faster build time.
|
||||
- CI: subsequent CI runs log `cache hit` from `buildx` and total build time reduced vs baseline.
|
||||
- Confirm registry contains `cache` image tags and that rotation job/prune removes old entries.
|
||||
|
||||
Notes about Gitea/CI
|
||||
- Gitea does not include native Actions like GitHub; teams typically use Drone CI, Tekton, or a self-hosted runner that can execute the `docker`/`buildx` CLI.
|
||||
- The provided `ci_buildx_example.sh` is intentionally provider-agnostic — pasteable into Drone, Jenkins, GitLab CI, or any shell-capable runner.
|
||||
|
||||
Risks & Mitigations
|
||||
- Risk: Unbounded registry cache growth. Mitigation: enforce retention policy and rotation job; prefer a single `cache` tag reused by CI.
|
||||
- Risk: Developers unfamiliar with BuildKit. Mitigation: examples show simple `DOCKER_BUILDKIT=1` usage and local cache prune commands.
|
||||
|
||||
Next steps for the container team
|
||||
1. Review examples in `aether_container_env/` and adapt the Dockerfile to your runtime constraints (ssl certs, env injection, secrets).
|
||||
2. Add a CI job using the `ci_buildx_example.sh` snippet; configure registry credentials as secrets.
|
||||
3. Add a scheduled job to rotate/delete old cache images or configure registry lifecycle rules.
|
||||
4. Run a before/after benchmark of `time npm run build:prod` inside the build stage to quantify improvement.
|
||||
|
||||
Files included in this PR for reference:
|
||||
- `aether_container_env/Dockerfile.buildkit.example`
|
||||
- `aether_container_env/ci_buildx_example.sh`
|
||||
- `documentation/AE_Docker_CI_cache_policy.md`
|
||||
@@ -31,7 +31,7 @@
|
||||
- Once satisfied, staff prints the badge.
|
||||
- The key differentiator vs the review form: **the live badge render** shows exactly how
|
||||
the badge will print. Attendees and staff can see changes immediately.
|
||||
- Component: `ae_comp__badge_obj_view.svelte` / `ae_comp__badge_obj_view_v2.svelte`
|
||||
- Component: `ae_comp__badge_obj_view.svelte`
|
||||
- Route: `/events/[event_id]/badges/[badge_id]/print/`
|
||||
|
||||
### Permission Model — Same Logic, Both Flows
|
||||
@@ -69,8 +69,8 @@ Work needed:
|
||||
or whether it should share/reuse the review form component alongside the badge render.
|
||||
- **Do NOT use `email_override` as the send-to address** — always use `event_badge.email`.
|
||||
|
||||
### 1. Auto-Scaling Badge Text (v2) — In Progress
|
||||
`ae_comp__badge_obj_view_v2.svelte` using `element_fit_text.svelte` (binary search auto-scale).
|
||||
### 1. Auto-Scaling Badge Text — In Progress
|
||||
`ae_comp__badge_obj_view.svelte` using `element_fit_text.svelte` (binary search auto-scale).
|
||||
Toggle between v1 (heuristic) and v2 (auto-scale) on the print page via the `v1`/`v2` header button.
|
||||
Heights tuned per layout in `fit_heights` derived object. Still needs visual tuning with real badges.
|
||||
|
||||
@@ -118,7 +118,7 @@ the MODULE doc TODO list was stale. `duplex` is in `properties_to_save`; v2 badg
|
||||
**Files created/updated:**
|
||||
- `src/lib/elements/action_fit_text.ts` — Svelte action
|
||||
- `src/lib/elements/element_fit_text.svelte` — Component wrapper
|
||||
- `src/routes/events/.../ae_comp__badge_obj_view_v2.svelte` — V2 badge render (canonical)
|
||||
- `src/routes/events/.../ae_comp__badge_obj_view.svelte` — V2 badge render (canonical)
|
||||
Debug blocks gated behind `$ae_loc.edit_mode` (hidden in production).
|
||||
- `print/+page.svelte` — Always uses v2 now. v1/v2 toggle removed. Header redesigned for kiosk UX.
|
||||
- `ae_comp__badge_print_controls.svelte` — Identity card at top, pronouns moved to attendee section,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Project Plan: Aether AE Obj Field Editor v3 (Consolidated)
|
||||
|
||||
> **Status:** 🔵 Active / Testing & Stabilization
|
||||
> **Date:** February 13, 2026
|
||||
> **Target Component:** `src/lib/elements/element_ae_obj_field_editor_v3.svelte`
|
||||
> **Status:** 🟡 Mostly Complete — Phase 3 items + GUIDE update remaining
|
||||
> **Date:** February 13, 2026 (last updated: 2026-03-20)
|
||||
> **Target Component:** `src/lib/elements/element_ae_obj_field_editor.svelte`
|
||||
> **Replaces:** `element_ae_crud.svelte` and `element_ae_crud_v2.svelte`
|
||||
|
||||
## 1. Overview
|
||||
@@ -32,12 +32,12 @@ Consolidate the legacy CRUD components into a single, high-performance "Aether O
|
||||
|
||||
### Phase 3: Field Type Parity (IN PROGRESS)
|
||||
- [x] Support `text`, `textarea`, `select`, `tiptap`, and `checkbox`.
|
||||
- [ ] Add `datetime` support using native browser pickers.
|
||||
- [x] Add `datetime` support using native browser pickers — `date` and `datetime-local` inputs implemented.
|
||||
- [ ] Implement searchable dropdowns for the `select` type.
|
||||
|
||||
### Phase 4: Migration & Cleanup
|
||||
- [x] Create a playground route for V3 verification (`/testing/ae_obj_field_editor_v3`).
|
||||
- [ ] Deprecate and eventually remove `v1` and `v2` files.
|
||||
- [x] Create a playground route for V3 verification (`/testing/ae_obj_field_editor`).
|
||||
- [x] Deprecate and remove `v1` and `v2` files — `element_ae_crud.svelte` and `element_ae_crud_v2.svelte` removed 2026-03-20.
|
||||
- [ ] Update `GUIDE__Development.md` with the new usage patterns.
|
||||
|
||||
## ⚠️ Security & Reliability Stabilization (NEW)
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
## 1. Project Overview
|
||||
This document outlines the modernization of the Journals module UI in the SvelteKit frontend (`aether_app_sveltekit`). The primary goals are to fully leverage the generic V3 API architecture and introduce high-velocity productivity features for journal management.
|
||||
|
||||
**Context:** The backend transition to the generic `api_crud_v3` router is complete. Custom legacy routers have been removed. The frontend must now fully align with this pattern and provide a frictionless user experience.
|
||||
**Context:** The backend transition to the generic `api_crud` router is complete. Custom legacy routers have been removed. The frontend must now fully align with this pattern and provide a frictionless user experience.
|
||||
|
||||
---
|
||||
|
||||
## 2. Core Objectives
|
||||
|
||||
### 🎯 Primary Goals
|
||||
1. **V3 API Verification:** Ensure all CRUD operations utilize the generic `api_crud_v3` endpoints (Verified).
|
||||
1. **V3 API Verification:** Ensure all CRUD operations utilize the generic `api_crud` endpoints (Verified).
|
||||
2. **Quick Add UI:** Implement a specialized interface for rapid, friction-free entry creation.
|
||||
3. **Append/Prepend UI:** Allow users to quickly add text to the beginning or end of existing entries without full edit mode.
|
||||
4. **Interop & Portability:** Robust import/export logic for Markdown/HTML (Nextcloud Notes compatibility).
|
||||
@@ -25,14 +25,14 @@ This document outlines the modernization of the Journals module UI in the Svelte
|
||||
## 3. Technical Architecture
|
||||
|
||||
### Backend (Completed)
|
||||
* **Router:** `api_crud_v3` (Generic)
|
||||
* **Router:** `api_crud` (Generic)
|
||||
* **Definitions:** `app/ae_obj_types_def.py` -> `app/object_definitions/journals.py`
|
||||
* **Endpoints:** `/v3/crud/journal/...` and `/v3/crud/journal_entry/...`
|
||||
|
||||
### Frontend (In Progress)
|
||||
* **State Management:** `src/lib/ae_journals/ae_journals_stores.ts`
|
||||
* **Local Storage:** Dexie.js (`db_journals`)
|
||||
* **API Client:** `src/lib/api/api.ts` -> `get_ae_obj_v3`
|
||||
* **API Client:** `src/lib/api/api.ts` -> `get_ae_obj`
|
||||
* **Export Engine:** Centralized templates in `src/lib/ae_journals/ae_journals_export_templates.ts`.
|
||||
|
||||
---
|
||||
|
||||
@@ -60,26 +60,26 @@ For each file listed above, follow this standard refactoring pattern:
|
||||
|
||||
1. **Imports:**
|
||||
* Remove imports of `create_ae_obj_crud`, `update_ae_obj_id_crud`, etc.
|
||||
* Import V3 helpers: `get_ae_obj_v3`, `create_ae_obj_v3`, `update_ae_obj_v3`, `delete_ae_obj_v3`, `search_ae_obj_v3`.
|
||||
* Import V3 helpers: `get_ae_obj`, `create_ae_obj`, `update_ae_obj`, `delete_ae_obj`, `search_ae_obj`.
|
||||
|
||||
2. **Pattern Replacement:**
|
||||
|
||||
* **Get (Single):**
|
||||
* *Old:* `get_ae_obj_id_crud({ api_cfg, obj_type: 'event_session', obj_id: '...' })`
|
||||
* *New:* `get_ae_obj_v3({ api_cfg, obj_type: 'event_session', obj_id: '...' })`
|
||||
* *New:* `get_ae_obj({ api_cfg, obj_type: 'event_session', obj_id: '...' })`
|
||||
|
||||
* **Get (List):**
|
||||
* *Old:* `get_ae_obj_li_for_obj_id_crud_v2(...)`
|
||||
* *New:* `get_ae_obj_li_v3(...)` or `search_ae_obj_v3(...)` if complex filtering is needed.
|
||||
* *New:* `get_ae_obj_li(...)` or `search_ae_obj(...)` if complex filtering is needed.
|
||||
|
||||
* **Update:**
|
||||
* *Old:* `update_ae_obj_id_crud({ ..., fields: { name: 'New Name' } })`
|
||||
* *New:* `update_ae_obj_v3({ ..., data: { name: 'New Name' } })`
|
||||
* *New:* `update_ae_obj({ ..., data: { name: 'New Name' } })`
|
||||
* *Note:* Ensure payload whitelisting is applied! V3 will 400 Error on unknown columns.
|
||||
|
||||
* **Create:**
|
||||
* *Old:* `create_ae_obj_crud({ ..., fields: { ... } })`
|
||||
* *New:* `create_ae_obj_v3({ ..., data: { ... } })`
|
||||
* *New:* `create_ae_obj({ ..., data: { ... } })`
|
||||
|
||||
3. **Verification:**
|
||||
* Verify the module still loads data (check Network tab for `/v3/` requests).
|
||||
@@ -106,7 +106,7 @@ V3 returns detailed error metadata in the `meta.details` object.
|
||||
**Symptom:** Providing a string ID in a search body that the backend maps to an integer can result in **Zero Results** if the underlying view expects a string.
|
||||
|
||||
**Final Solution (Body + Header Injection):**
|
||||
1. **Body:** Inject the raw field name (e.g. `account_id_random`) into the `search_query.and` array to bypass automatic backend mapping.
|
||||
1. **Body:** Inject the raw field name (e.g. `account_id`) into the `search_query.and` array to bypass automatic backend mapping.
|
||||
2. **Headers:** Pass `headers: { 'x-account-id': ... }` manually to provide context for Auth validation.
|
||||
3. **Isolation (IDAA):** Due to specific bugs in the IDAA module, it has been temporarily isolated to a legacy V2 search function (`qry_ae_obj_li__event_v2`) using `default_qry_str` for text searching, while the main module continues to use the V3 implementation.
|
||||
|
||||
@@ -114,7 +114,7 @@ V3 returns detailed error metadata in the `meta.details` object.
|
||||
|
||||
## 6. Final Cleanup
|
||||
Once all checkboxes above are completed:
|
||||
1. [ ] Remove legacy exports from `src/lib/api/api.ts`.
|
||||
2. [ ] Delete `src/lib/ae_api/api_get__crud_obj_li_v1.ts`.
|
||||
3. [ ] Delete `src/lib/ae_api/api_get__crud_obj_li_v2.ts`.
|
||||
4. [ ] Delete `src/lib/ae_api/api_get__crud_obj_id.ts` (Legacy version).
|
||||
1. [x] Remove legacy exports from `src/lib/api/api.ts`.
|
||||
2. [x] Delete `src/lib/ae_api/api_get__crud_obj_li_v1.ts`.
|
||||
3. [x] Delete `src/lib/ae_api/api_get__crud_obj_li_v2.ts`.
|
||||
4. [x] Delete `src/lib/ae_api/api_get__crud_obj_id.ts` (Legacy version).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Frontend Agent Task List
|
||||
> Use this file to track steps for complex features or bug fixes.
|
||||
> **Status:** <20> Stable — ongoing development.
|
||||
> **Status:** Stable — ongoing development.
|
||||
|
||||
|
||||
## 🚧 Upcoming High Priority
|
||||
|
||||
@@ -40,3 +40,6 @@
|
||||
|
||||
---
|
||||
*Prepared by: Gemini CLI (March 17, 2026)*
|
||||
|
||||
---
|
||||
*Archival note (2026-03-20): `element_modal_v1.svelte` (referenced in §2 as "new standard modal") was subsequently retired — it had zero active importers. Modal usage in the codebase relies on Flowbite `<Modal>` component. See `AE__UI_Component_Patterns.md` §11.*
|
||||
@@ -32,7 +32,9 @@ export default tseslint.config(
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': 'warn'
|
||||
'@typescript-eslint/no-unused-vars': 'warn',
|
||||
// No base path configured — this rule is not applicable to this project
|
||||
'svelte/no-navigation-without-resolve': 'off'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
106
package-lock.json
generated
106
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "osit-aether-app-svelte",
|
||||
"version": "3.12.08",
|
||||
"version": "3.00.05",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "osit-aether-app-svelte",
|
||||
"version": "3.12.08",
|
||||
"version": "3.00.05",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.20.0",
|
||||
"@codemirror/commands": "^6.10.0",
|
||||
@@ -34,6 +34,7 @@
|
||||
"lucide-svelte": "^0.*.0",
|
||||
"marked": "^17.0.0",
|
||||
"openai": "^6.10.0",
|
||||
"prettier-plugin-tailwindcss": "^0.7.2",
|
||||
"qrcode": "^1.5.4",
|
||||
"shadcn-svelte": "^1.0.11",
|
||||
"svelte-persisted-store": "^0.12.0",
|
||||
@@ -6161,6 +6162,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-load-config/node_modules/yaml": {
|
||||
"version": "1.10.3",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
|
||||
"integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-safe-parser": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz",
|
||||
@@ -6242,7 +6253,6 @@
|
||||
"version": "3.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
|
||||
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
@@ -6258,13 +6268,91 @@
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.5.1.tgz",
|
||||
"integrity": "sha512-65+fr5+cgIKWKiqM1Doum4uX6bY8iFCdztvvp2RcF+AJoieaw9kJOFMNcJo/bkmKYsxFaM9OsVZK/gWauG/5mg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"prettier": "^3.0.0",
|
||||
"svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-plugin-tailwindcss": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.7.2.tgz",
|
||||
"integrity": "sha512-LkphyK3Fw+q2HdMOoiEHWf93fNtYJwfamoKPl7UwtjFQdei/iIBoX11G6j706FzN3ymX9mPVi97qIY8328vdnA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.19"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ianvs/prettier-plugin-sort-imports": "*",
|
||||
"@prettier/plugin-hermes": "*",
|
||||
"@prettier/plugin-oxc": "*",
|
||||
"@prettier/plugin-pug": "*",
|
||||
"@shopify/prettier-plugin-liquid": "*",
|
||||
"@trivago/prettier-plugin-sort-imports": "*",
|
||||
"@zackad/prettier-plugin-twig": "*",
|
||||
"prettier": "^3.0",
|
||||
"prettier-plugin-astro": "*",
|
||||
"prettier-plugin-css-order": "*",
|
||||
"prettier-plugin-jsdoc": "*",
|
||||
"prettier-plugin-marko": "*",
|
||||
"prettier-plugin-multiline-arrays": "*",
|
||||
"prettier-plugin-organize-attributes": "*",
|
||||
"prettier-plugin-organize-imports": "*",
|
||||
"prettier-plugin-sort-imports": "*",
|
||||
"prettier-plugin-svelte": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@ianvs/prettier-plugin-sort-imports": {
|
||||
"optional": true
|
||||
},
|
||||
"@prettier/plugin-hermes": {
|
||||
"optional": true
|
||||
},
|
||||
"@prettier/plugin-oxc": {
|
||||
"optional": true
|
||||
},
|
||||
"@prettier/plugin-pug": {
|
||||
"optional": true
|
||||
},
|
||||
"@shopify/prettier-plugin-liquid": {
|
||||
"optional": true
|
||||
},
|
||||
"@trivago/prettier-plugin-sort-imports": {
|
||||
"optional": true
|
||||
},
|
||||
"@zackad/prettier-plugin-twig": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-astro": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-css-order": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-jsdoc": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-marko": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-multiline-arrays": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-organize-attributes": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-organize-imports": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-sort-imports": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-svelte": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-compare": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-3.0.1.tgz",
|
||||
@@ -7737,16 +7825,6 @@
|
||||
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
|
||||
"devOptional": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "15.4.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "osit-aether-app-svelte",
|
||||
"version": "3.00.04",
|
||||
"version": "3.00.07",
|
||||
"description": "One Sky IT's Aether App created with Svelte, SvelteKit, Tailwind CSS, Lucide, Font Awesome, and Skeleton UI. -Scott Idem",
|
||||
"homepage": "https://oneskyit.com/",
|
||||
"private": true,
|
||||
@@ -112,6 +112,7 @@
|
||||
"lucide-svelte": "^0.*.0",
|
||||
"marked": "^17.0.0",
|
||||
"openai": "^6.10.0",
|
||||
"prettier-plugin-tailwindcss": "^0.7.2",
|
||||
"qrcode": "^1.5.4",
|
||||
"shadcn-svelte": "^1.0.11",
|
||||
"svelte-persisted-store": "^0.12.0",
|
||||
|
||||
@@ -63,50 +63,50 @@ html[data-theme='AE_Firefly_Indigo'] {
|
||||
|
||||
/* --- Color ramps (light mode) copied from dark block so both modes have full ramps --- */
|
||||
html[data-theme='AE_Firefly_Indigo'] {
|
||||
--color-primary-50: oklch(95.5% 0.040 270deg);
|
||||
--color-primary-50: oklch(95.5% 0.04 270deg);
|
||||
--color-primary-100: oklch(89.5% 0.072 270deg);
|
||||
--color-primary-200: oklch(82.5% 0.108 269deg);
|
||||
--color-primary-300: oklch(74.5% 0.135 268deg);
|
||||
--color-primary-400: oklch(65.0% 0.155 267deg);
|
||||
--color-primary-500: oklch(50.5% 0.160 266deg);
|
||||
--color-primary-400: oklch(65% 0.155 267deg);
|
||||
--color-primary-500: oklch(50.5% 0.16 266deg);
|
||||
--color-primary-600: oklch(43.5% 0.152 265deg);
|
||||
--color-primary-700: oklch(37.0% 0.138 264deg);
|
||||
--color-primary-800: oklch(30.0% 0.120 263deg);
|
||||
--color-primary-900: oklch(23.0% 0.100 262deg);
|
||||
--color-primary-950: oklch(15.5% 0.080 261deg);
|
||||
--color-primary-contrast-dark: var(--color-primary-950);
|
||||
--color-primary-700: oklch(37% 0.138 264deg);
|
||||
--color-primary-800: oklch(30% 0.12 263deg);
|
||||
--color-primary-900: oklch(23% 0.1 262deg);
|
||||
--color-primary-950: oklch(15.5% 0.08 261deg);
|
||||
--color-primary-contrast-dark: var(--color-primary-950);
|
||||
--color-primary-contrast-light: var(--color-primary-50);
|
||||
|
||||
--color-secondary-50: oklch(96.5% 0.032 297deg);
|
||||
--color-secondary-50: oklch(96.5% 0.032 297deg);
|
||||
--color-secondary-100: oklch(91.5% 0.058 295deg);
|
||||
--color-secondary-200: oklch(85.5% 0.090 293deg);
|
||||
--color-secondary-200: oklch(85.5% 0.09 293deg);
|
||||
--color-secondary-300: oklch(78.5% 0.115 292deg);
|
||||
--color-secondary-400: oklch(70.0% 0.132 291deg);
|
||||
--color-secondary-500: oklch(60.0% 0.140 290deg);
|
||||
--color-secondary-400: oklch(70% 0.132 291deg);
|
||||
--color-secondary-500: oklch(60% 0.14 290deg);
|
||||
--color-secondary-600: oklch(52.5% 0.135 289deg);
|
||||
--color-secondary-700: oklch(45.0% 0.126 288deg);
|
||||
--color-secondary-700: oklch(45% 0.126 288deg);
|
||||
--color-secondary-800: oklch(37.5% 0.112 286deg);
|
||||
--color-secondary-900: oklch(30.0% 0.094 284deg);
|
||||
--color-secondary-950: oklch(22.0% 0.076 282deg);
|
||||
--color-secondary-contrast-dark: var(--color-secondary-950);
|
||||
--color-secondary-900: oklch(30% 0.094 284deg);
|
||||
--color-secondary-950: oklch(22% 0.076 282deg);
|
||||
--color-secondary-contrast-dark: var(--color-secondary-950);
|
||||
--color-secondary-contrast-light: var(--color-secondary-50);
|
||||
|
||||
--color-tertiary-50: oklch(96.5% 0.022 348deg);
|
||||
--color-tertiary-100: oklch(91.0% 0.042 346deg);
|
||||
--color-tertiary-50: oklch(96.5% 0.022 348deg);
|
||||
--color-tertiary-100: oklch(91% 0.042 346deg);
|
||||
--color-tertiary-200: oklch(84.5% 0.068 344deg);
|
||||
--color-tertiary-300: oklch(76.5% 0.095 343deg);
|
||||
--color-tertiary-400: oklch(68.0% 0.118 342deg);
|
||||
--color-tertiary-400: oklch(68% 0.118 342deg);
|
||||
--color-tertiary-500: oklch(57.5% 0.128 341deg);
|
||||
--color-tertiary-600: oklch(50.0% 0.122 340deg);
|
||||
--color-tertiary-700: oklch(43.0% 0.112 339deg);
|
||||
--color-tertiary-600: oklch(50% 0.122 340deg);
|
||||
--color-tertiary-700: oklch(43% 0.112 339deg);
|
||||
--color-tertiary-800: oklch(35.5% 0.098 338deg);
|
||||
--color-tertiary-900: oklch(28.0% 0.080 337deg);
|
||||
--color-tertiary-900: oklch(28% 0.08 337deg);
|
||||
--color-tertiary-950: oklch(20.5% 0.062 336deg);
|
||||
--color-tertiary-contrast-dark: var(--color-tertiary-950);
|
||||
--color-tertiary-contrast-dark: var(--color-tertiary-950);
|
||||
--color-tertiary-contrast-light: var(--color-tertiary-50);
|
||||
|
||||
--color-success-50: oklch(95.77% 0.05 152.69deg);
|
||||
--color-success-100: oklch(91.59% 0.06 152.00deg);
|
||||
--color-success-50: oklch(95.77% 0.05 152.69deg);
|
||||
--color-success-100: oklch(91.59% 0.06 152deg);
|
||||
--color-success-200: oklch(87.45% 0.08 152.08deg);
|
||||
--color-success-300: oklch(83.57% 0.09 150.85deg);
|
||||
--color-success-400: oklch(79.47% 0.11 150.71deg);
|
||||
@@ -114,51 +114,51 @@ html[data-theme='AE_Firefly_Indigo'] {
|
||||
--color-success-600: oklch(67.65% 0.11 149.94deg);
|
||||
--color-success-700: oklch(59.71% 0.09 150.42deg);
|
||||
--color-success-800: oklch(51.74% 0.08 150.24deg);
|
||||
--color-success-900: oklch(43.20% 0.06 151.12deg);
|
||||
--color-success-950: oklch(34.20% 0.04 151.44deg);
|
||||
--color-success-contrast-dark: var(--color-success-950);
|
||||
--color-success-900: oklch(43.2% 0.06 151.12deg);
|
||||
--color-success-950: oklch(34.2% 0.04 151.44deg);
|
||||
--color-success-contrast-dark: var(--color-success-950);
|
||||
--color-success-contrast-light: var(--color-success-50);
|
||||
|
||||
--color-warning-50: oklch(97.5% 0.065 78deg);
|
||||
--color-warning-100: oklch(93.5% 0.090 75deg);
|
||||
--color-warning-200: oklch(89.5% 0.120 73deg);
|
||||
--color-warning-50: oklch(97.5% 0.065 78deg);
|
||||
--color-warning-100: oklch(93.5% 0.09 75deg);
|
||||
--color-warning-200: oklch(89.5% 0.12 73deg);
|
||||
--color-warning-300: oklch(85.5% 0.145 70deg);
|
||||
--color-warning-400: oklch(81.5% 0.160 67deg);
|
||||
--color-warning-500: oklch(77.0% 0.165 65deg);
|
||||
--color-warning-400: oklch(81.5% 0.16 67deg);
|
||||
--color-warning-500: oklch(77% 0.165 65deg);
|
||||
--color-warning-600: oklch(69.5% 0.155 64deg);
|
||||
--color-warning-700: oklch(61.5% 0.140 63deg);
|
||||
--color-warning-700: oklch(61.5% 0.14 63deg);
|
||||
--color-warning-800: oklch(53.5% 0.125 62deg);
|
||||
--color-warning-900: oklch(45.0% 0.105 61deg);
|
||||
--color-warning-950: oklch(37.0% 0.088 60deg);
|
||||
--color-warning-contrast-dark: var(--color-warning-950);
|
||||
--color-warning-900: oklch(45% 0.105 61deg);
|
||||
--color-warning-950: oklch(37% 0.088 60deg);
|
||||
--color-warning-contrast-dark: var(--color-warning-950);
|
||||
--color-warning-contrast-light: var(--color-warning-50);
|
||||
|
||||
--color-error-50: oklch(95.0% 0.040 18deg);
|
||||
--color-error-100: oklch(88.0% 0.070 20deg);
|
||||
--color-error-200: oklch(80.0% 0.105 21deg);
|
||||
--color-error-300: oklch(72.0% 0.140 22deg);
|
||||
--color-error-400: oklch(64.5% 0.170 23deg);
|
||||
--color-error-50: oklch(95% 0.04 18deg);
|
||||
--color-error-100: oklch(88% 0.07 20deg);
|
||||
--color-error-200: oklch(80% 0.105 21deg);
|
||||
--color-error-300: oklch(72% 0.14 22deg);
|
||||
--color-error-400: oklch(64.5% 0.17 23deg);
|
||||
--color-error-500: oklch(57.5% 0.195 24deg);
|
||||
--color-error-600: oklch(51.5% 0.182 25deg);
|
||||
--color-error-700: oklch(45.5% 0.165 26deg);
|
||||
--color-error-800: oklch(39.5% 0.148 27deg);
|
||||
--color-error-900: oklch(33.0% 0.128 28deg);
|
||||
--color-error-900: oklch(33% 0.128 28deg);
|
||||
--color-error-950: oklch(26.5% 0.108 29deg);
|
||||
--color-error-contrast-dark: var(--color-error-950);
|
||||
--color-error-contrast-dark: var(--color-error-950);
|
||||
--color-error-contrast-light: var(--color-error-50);
|
||||
|
||||
--color-surface-50: oklch(99.0% 0.003 270deg);
|
||||
--color-surface-50: oklch(99% 0.003 270deg);
|
||||
--color-surface-100: oklch(96.5% 0.006 268deg);
|
||||
--color-surface-200: oklch(92.5% 0.010 266deg);
|
||||
--color-surface-300: oklch(87.0% 0.014 265deg);
|
||||
--color-surface-200: oklch(92.5% 0.01 266deg);
|
||||
--color-surface-300: oklch(87% 0.014 265deg);
|
||||
--color-surface-400: oklch(78.5% 0.018 265deg);
|
||||
--color-surface-500: oklch(66.5% 0.020 267deg);
|
||||
--color-surface-500: oklch(66.5% 0.02 267deg);
|
||||
--color-surface-600: oklch(54.5% 0.022 269deg);
|
||||
--color-surface-700: oklch(42.5% 0.024 270deg);
|
||||
--color-surface-800: oklch(31.0% 0.026 272deg);
|
||||
--color-surface-900: oklch(20.5% 0.030 274deg);
|
||||
--color-surface-950: oklch(13.0% 0.034 276deg);
|
||||
--color-surface-contrast-dark: var(--color-surface-950);
|
||||
--color-surface-800: oklch(31% 0.026 272deg);
|
||||
--color-surface-900: oklch(20.5% 0.03 274deg);
|
||||
--color-surface-950: oklch(13% 0.034 276deg);
|
||||
--color-surface-contrast-dark: var(--color-surface-950);
|
||||
--color-surface-contrast-light: var(--color-surface-50);
|
||||
}
|
||||
|
||||
@@ -182,20 +182,20 @@ html.dark[data-theme='AE_Firefly_Indigo'] {
|
||||
* maintaining sufficient contrast at mid-range shades.
|
||||
* At 500 (L≈50%): sufficient contrast with primary-50 text (≥4:1).
|
||||
* =================================================================== */
|
||||
--color-primary-50: oklch(95.5% 0.040 270deg);
|
||||
--color-primary-50: oklch(95.5% 0.04 270deg);
|
||||
--color-primary-100: oklch(89.5% 0.072 270deg);
|
||||
--color-primary-200: oklch(82.5% 0.108 269deg);
|
||||
--color-primary-300: oklch(74.5% 0.135 268deg);
|
||||
--color-primary-400: oklch(65.0% 0.155 267deg);
|
||||
--color-primary-500: oklch(50.5% 0.160 266deg);
|
||||
--color-primary-400: oklch(65% 0.155 267deg);
|
||||
--color-primary-500: oklch(50.5% 0.16 266deg);
|
||||
--color-primary-600: oklch(43.5% 0.152 265deg);
|
||||
--color-primary-700: oklch(37.0% 0.138 264deg);
|
||||
--color-primary-800: oklch(30.0% 0.120 263deg);
|
||||
--color-primary-900: oklch(23.0% 0.100 262deg);
|
||||
--color-primary-950: oklch(15.5% 0.080 261deg);
|
||||
--color-primary-contrast-dark: var(--color-primary-950);
|
||||
--color-primary-700: oklch(37% 0.138 264deg);
|
||||
--color-primary-800: oklch(30% 0.12 263deg);
|
||||
--color-primary-900: oklch(23% 0.1 262deg);
|
||||
--color-primary-950: oklch(15.5% 0.08 261deg);
|
||||
--color-primary-contrast-dark: var(--color-primary-950);
|
||||
--color-primary-contrast-light: var(--color-primary-50);
|
||||
--color-primary-contrast-50: var(--color-primary-contrast-dark);
|
||||
--color-primary-contrast-50: var(--color-primary-contrast-dark);
|
||||
--color-primary-contrast-100: var(--color-primary-contrast-dark);
|
||||
--color-primary-contrast-200: var(--color-primary-contrast-dark);
|
||||
--color-primary-contrast-300: var(--color-primary-contrast-dark);
|
||||
@@ -214,20 +214,20 @@ html.dark[data-theme='AE_Firefly_Indigo'] {
|
||||
* remaining clearly distinct from the primary.
|
||||
* Used for secondary actions, badges, and soft highlights.
|
||||
* =================================================================== */
|
||||
--color-secondary-50: oklch(96.5% 0.032 297deg);
|
||||
--color-secondary-50: oklch(96.5% 0.032 297deg);
|
||||
--color-secondary-100: oklch(91.5% 0.058 295deg);
|
||||
--color-secondary-200: oklch(85.5% 0.090 293deg);
|
||||
--color-secondary-200: oklch(85.5% 0.09 293deg);
|
||||
--color-secondary-300: oklch(78.5% 0.115 292deg);
|
||||
--color-secondary-400: oklch(70.0% 0.132 291deg);
|
||||
--color-secondary-500: oklch(60.0% 0.140 290deg);
|
||||
--color-secondary-400: oklch(70% 0.132 291deg);
|
||||
--color-secondary-500: oklch(60% 0.14 290deg);
|
||||
--color-secondary-600: oklch(52.5% 0.135 289deg);
|
||||
--color-secondary-700: oklch(45.0% 0.126 288deg);
|
||||
--color-secondary-700: oklch(45% 0.126 288deg);
|
||||
--color-secondary-800: oklch(37.5% 0.112 286deg);
|
||||
--color-secondary-900: oklch(30.0% 0.094 284deg);
|
||||
--color-secondary-950: oklch(22.0% 0.076 282deg);
|
||||
--color-secondary-contrast-dark: var(--color-secondary-950);
|
||||
--color-secondary-900: oklch(30% 0.094 284deg);
|
||||
--color-secondary-950: oklch(22% 0.076 282deg);
|
||||
--color-secondary-contrast-dark: var(--color-secondary-950);
|
||||
--color-secondary-contrast-light: var(--color-secondary-50);
|
||||
--color-secondary-contrast-50: var(--color-secondary-contrast-dark);
|
||||
--color-secondary-contrast-50: var(--color-secondary-contrast-dark);
|
||||
--color-secondary-contrast-100: var(--color-secondary-contrast-dark);
|
||||
--color-secondary-contrast-200: var(--color-secondary-contrast-dark);
|
||||
--color-secondary-contrast-300: var(--color-secondary-contrast-dark);
|
||||
@@ -247,20 +247,20 @@ html.dark[data-theme='AE_Firefly_Indigo'] {
|
||||
* breaking against a deep indigo sky.
|
||||
* Used for location chips, warm accents, tertiary elements.
|
||||
* =================================================================== */
|
||||
--color-tertiary-50: oklch(96.5% 0.022 348deg);
|
||||
--color-tertiary-100: oklch(91.0% 0.042 346deg);
|
||||
--color-tertiary-50: oklch(96.5% 0.022 348deg);
|
||||
--color-tertiary-100: oklch(91% 0.042 346deg);
|
||||
--color-tertiary-200: oklch(84.5% 0.068 344deg);
|
||||
--color-tertiary-300: oklch(76.5% 0.095 343deg);
|
||||
--color-tertiary-400: oklch(68.0% 0.118 342deg);
|
||||
--color-tertiary-400: oklch(68% 0.118 342deg);
|
||||
--color-tertiary-500: oklch(57.5% 0.128 341deg);
|
||||
--color-tertiary-600: oklch(50.0% 0.122 340deg);
|
||||
--color-tertiary-700: oklch(43.0% 0.112 339deg);
|
||||
--color-tertiary-600: oklch(50% 0.122 340deg);
|
||||
--color-tertiary-700: oklch(43% 0.112 339deg);
|
||||
--color-tertiary-800: oklch(35.5% 0.098 338deg);
|
||||
--color-tertiary-900: oklch(28.0% 0.080 337deg);
|
||||
--color-tertiary-900: oklch(28% 0.08 337deg);
|
||||
--color-tertiary-950: oklch(20.5% 0.062 336deg);
|
||||
--color-tertiary-contrast-dark: var(--color-tertiary-950);
|
||||
--color-tertiary-contrast-dark: var(--color-tertiary-950);
|
||||
--color-tertiary-contrast-light: var(--color-tertiary-50);
|
||||
--color-tertiary-contrast-50: var(--color-tertiary-contrast-dark);
|
||||
--color-tertiary-contrast-50: var(--color-tertiary-contrast-dark);
|
||||
--color-tertiary-contrast-100: var(--color-tertiary-contrast-dark);
|
||||
--color-tertiary-contrast-200: var(--color-tertiary-contrast-dark);
|
||||
--color-tertiary-contrast-300: var(--color-tertiary-contrast-dark);
|
||||
@@ -277,8 +277,8 @@ html.dark[data-theme='AE_Firefly_Indigo'] {
|
||||
* Hue: ~152°. Consistent with AE_Firefly for recognizable semantic
|
||||
* color meaning across OSIT themes.
|
||||
* =================================================================== */
|
||||
--color-success-50: oklch(95.77% 0.05 152.69deg);
|
||||
--color-success-100: oklch(91.59% 0.06 152.00deg);
|
||||
--color-success-50: oklch(95.77% 0.05 152.69deg);
|
||||
--color-success-100: oklch(91.59% 0.06 152deg);
|
||||
--color-success-200: oklch(87.45% 0.08 152.08deg);
|
||||
--color-success-300: oklch(83.57% 0.09 150.85deg);
|
||||
--color-success-400: oklch(79.47% 0.11 150.71deg);
|
||||
@@ -286,11 +286,11 @@ html.dark[data-theme='AE_Firefly_Indigo'] {
|
||||
--color-success-600: oklch(67.65% 0.11 149.94deg);
|
||||
--color-success-700: oklch(59.71% 0.09 150.42deg);
|
||||
--color-success-800: oklch(51.74% 0.08 150.24deg);
|
||||
--color-success-900: oklch(43.20% 0.06 151.12deg);
|
||||
--color-success-950: oklch(34.20% 0.04 151.44deg);
|
||||
--color-success-contrast-dark: var(--color-success-950);
|
||||
--color-success-900: oklch(43.2% 0.06 151.12deg);
|
||||
--color-success-950: oklch(34.2% 0.04 151.44deg);
|
||||
--color-success-contrast-dark: var(--color-success-950);
|
||||
--color-success-contrast-light: var(--color-success-50);
|
||||
--color-success-contrast-50: var(--color-success-contrast-dark);
|
||||
--color-success-contrast-50: var(--color-success-contrast-dark);
|
||||
--color-success-contrast-100: var(--color-success-contrast-dark);
|
||||
--color-success-contrast-200: var(--color-success-contrast-dark);
|
||||
--color-success-contrast-300: var(--color-success-contrast-dark);
|
||||
@@ -306,20 +306,20 @@ html.dark[data-theme='AE_Firefly_Indigo'] {
|
||||
* WARNING — Amber Orange
|
||||
* Consistent with AE_Firefly for recognizable semantic meaning.
|
||||
* =================================================================== */
|
||||
--color-warning-50: oklch(97.5% 0.065 78deg);
|
||||
--color-warning-100: oklch(93.5% 0.090 75deg);
|
||||
--color-warning-200: oklch(89.5% 0.120 73deg);
|
||||
--color-warning-50: oklch(97.5% 0.065 78deg);
|
||||
--color-warning-100: oklch(93.5% 0.09 75deg);
|
||||
--color-warning-200: oklch(89.5% 0.12 73deg);
|
||||
--color-warning-300: oklch(85.5% 0.145 70deg);
|
||||
--color-warning-400: oklch(81.5% 0.160 67deg);
|
||||
--color-warning-500: oklch(77.0% 0.165 65deg);
|
||||
--color-warning-400: oklch(81.5% 0.16 67deg);
|
||||
--color-warning-500: oklch(77% 0.165 65deg);
|
||||
--color-warning-600: oklch(69.5% 0.155 64deg);
|
||||
--color-warning-700: oklch(61.5% 0.140 63deg);
|
||||
--color-warning-700: oklch(61.5% 0.14 63deg);
|
||||
--color-warning-800: oklch(53.5% 0.125 62deg);
|
||||
--color-warning-900: oklch(45.0% 0.105 61deg);
|
||||
--color-warning-950: oklch(37.0% 0.088 60deg);
|
||||
--color-warning-contrast-dark: var(--color-warning-950);
|
||||
--color-warning-900: oklch(45% 0.105 61deg);
|
||||
--color-warning-950: oklch(37% 0.088 60deg);
|
||||
--color-warning-contrast-dark: var(--color-warning-950);
|
||||
--color-warning-contrast-light: var(--color-warning-50);
|
||||
--color-warning-contrast-50: var(--color-warning-contrast-dark);
|
||||
--color-warning-contrast-50: var(--color-warning-contrast-dark);
|
||||
--color-warning-contrast-100: var(--color-warning-contrast-dark);
|
||||
--color-warning-contrast-200: var(--color-warning-contrast-dark);
|
||||
--color-warning-contrast-300: var(--color-warning-contrast-dark);
|
||||
@@ -335,20 +335,20 @@ html.dark[data-theme='AE_Firefly_Indigo'] {
|
||||
* ERROR — Soft Coral/Rose
|
||||
* Consistent with AE_Firefly for recognizable semantic meaning.
|
||||
* =================================================================== */
|
||||
--color-error-50: oklch(95.0% 0.040 18deg);
|
||||
--color-error-100: oklch(88.0% 0.070 20deg);
|
||||
--color-error-200: oklch(80.0% 0.105 21deg);
|
||||
--color-error-300: oklch(72.0% 0.140 22deg);
|
||||
--color-error-400: oklch(64.5% 0.170 23deg);
|
||||
--color-error-50: oklch(95% 0.04 18deg);
|
||||
--color-error-100: oklch(88% 0.07 20deg);
|
||||
--color-error-200: oklch(80% 0.105 21deg);
|
||||
--color-error-300: oklch(72% 0.14 22deg);
|
||||
--color-error-400: oklch(64.5% 0.17 23deg);
|
||||
--color-error-500: oklch(57.5% 0.195 24deg);
|
||||
--color-error-600: oklch(51.5% 0.182 25deg);
|
||||
--color-error-700: oklch(45.5% 0.165 26deg);
|
||||
--color-error-800: oklch(39.5% 0.148 27deg);
|
||||
--color-error-900: oklch(33.0% 0.128 28deg);
|
||||
--color-error-900: oklch(33% 0.128 28deg);
|
||||
--color-error-950: oklch(26.5% 0.108 29deg);
|
||||
--color-error-contrast-dark: var(--color-error-950);
|
||||
--color-error-contrast-dark: var(--color-error-950);
|
||||
--color-error-contrast-light: var(--color-error-50);
|
||||
--color-error-contrast-50: var(--color-error-contrast-dark);
|
||||
--color-error-contrast-50: var(--color-error-contrast-dark);
|
||||
--color-error-contrast-100: var(--color-error-contrast-dark);
|
||||
--color-error-contrast-200: var(--color-error-contrast-dark);
|
||||
--color-error-contrast-300: var(--color-error-contrast-dark);
|
||||
@@ -370,20 +370,20 @@ html.dark[data-theme='AE_Firefly_Indigo'] {
|
||||
* 50 → body-bg light: near-white with ImperceptibleISTIC purple cast
|
||||
* 950 → body-bg dark: deep midnight with indigo depth
|
||||
* =================================================================== */
|
||||
--color-surface-50: oklch(99.0% 0.003 270deg);
|
||||
--color-surface-50: oklch(99% 0.003 270deg);
|
||||
--color-surface-100: oklch(96.5% 0.006 268deg);
|
||||
--color-surface-200: oklch(92.5% 0.010 266deg);
|
||||
--color-surface-300: oklch(87.0% 0.014 265deg);
|
||||
--color-surface-200: oklch(92.5% 0.01 266deg);
|
||||
--color-surface-300: oklch(87% 0.014 265deg);
|
||||
--color-surface-400: oklch(78.5% 0.018 265deg);
|
||||
--color-surface-500: oklch(66.5% 0.020 267deg);
|
||||
--color-surface-500: oklch(66.5% 0.02 267deg);
|
||||
--color-surface-600: oklch(54.5% 0.022 269deg);
|
||||
--color-surface-700: oklch(42.5% 0.024 270deg);
|
||||
--color-surface-800: oklch(31.0% 0.026 272deg);
|
||||
--color-surface-900: oklch(20.5% 0.030 274deg);
|
||||
--color-surface-950: oklch(13.0% 0.034 276deg);
|
||||
--color-surface-contrast-dark: var(--color-surface-950);
|
||||
--color-surface-800: oklch(31% 0.026 272deg);
|
||||
--color-surface-900: oklch(20.5% 0.03 274deg);
|
||||
--color-surface-950: oklch(13% 0.034 276deg);
|
||||
--color-surface-contrast-dark: var(--color-surface-950);
|
||||
--color-surface-contrast-light: var(--color-surface-50);
|
||||
--color-surface-contrast-50: var(--color-surface-contrast-dark);
|
||||
--color-surface-contrast-50: var(--color-surface-contrast-dark);
|
||||
--color-surface-contrast-100: var(--color-surface-contrast-dark);
|
||||
--color-surface-contrast-200: var(--color-surface-contrast-dark);
|
||||
--color-surface-contrast-300: var(--color-surface-contrast-dark);
|
||||
|
||||
@@ -63,50 +63,50 @@ html[data-theme='AE_Firefly_Rainbow'] {
|
||||
|
||||
/* --- Color ramps (light mode) copied from dark block so both modes have full ramps --- */
|
||||
html[data-theme='AE_Firefly_Rainbow'] {
|
||||
--color-primary-50: oklch(97.0% 0.020 15deg);
|
||||
--color-primary-100: oklch(92.0% 0.048 14deg);
|
||||
--color-primary-200: oklch(86.0% 0.085 13deg);
|
||||
--color-primary-300: oklch(79.0% 0.125 13deg);
|
||||
--color-primary-400: oklch(71.0% 0.160 13deg);
|
||||
--color-primary-500: oklch(60.0% 0.190 14deg);
|
||||
--color-primary-50: oklch(97% 0.02 15deg);
|
||||
--color-primary-100: oklch(92% 0.048 14deg);
|
||||
--color-primary-200: oklch(86% 0.085 13deg);
|
||||
--color-primary-300: oklch(79% 0.125 13deg);
|
||||
--color-primary-400: oklch(71% 0.16 13deg);
|
||||
--color-primary-500: oklch(60% 0.19 14deg);
|
||||
--color-primary-600: oklch(52.5% 0.178 15deg);
|
||||
--color-primary-700: oklch(45.0% 0.162 16deg);
|
||||
--color-primary-700: oklch(45% 0.162 16deg);
|
||||
--color-primary-800: oklch(37.5% 0.142 17deg);
|
||||
--color-primary-900: oklch(30.0% 0.118 18deg);
|
||||
--color-primary-900: oklch(30% 0.118 18deg);
|
||||
--color-primary-950: oklch(22.5% 0.092 19deg);
|
||||
--color-primary-contrast-dark: var(--color-primary-950);
|
||||
--color-primary-contrast-dark: var(--color-primary-950);
|
||||
--color-primary-contrast-light: var(--color-primary-50);
|
||||
|
||||
--color-secondary-50: oklch(97.0% 0.040 152deg);
|
||||
--color-secondary-50: oklch(97% 0.04 152deg);
|
||||
--color-secondary-100: oklch(92.5% 0.072 150deg);
|
||||
--color-secondary-200: oklch(87.0% 0.105 149deg);
|
||||
--color-secondary-300: oklch(81.0% 0.132 148deg);
|
||||
--color-secondary-200: oklch(87% 0.105 149deg);
|
||||
--color-secondary-300: oklch(81% 0.132 148deg);
|
||||
--color-secondary-400: oklch(74.5% 0.152 148deg);
|
||||
--color-secondary-500: oklch(62.0% 0.160 148deg);
|
||||
--color-secondary-500: oklch(62% 0.16 148deg);
|
||||
--color-secondary-600: oklch(53.5% 0.148 148deg);
|
||||
--color-secondary-700: oklch(45.5% 0.132 147deg);
|
||||
--color-secondary-800: oklch(37.5% 0.112 146deg);
|
||||
--color-secondary-900: oklch(29.5% 0.090 145deg);
|
||||
--color-secondary-900: oklch(29.5% 0.09 145deg);
|
||||
--color-secondary-950: oklch(21.5% 0.068 144deg);
|
||||
--color-secondary-contrast-dark: var(--color-secondary-950);
|
||||
--color-secondary-contrast-dark: var(--color-secondary-950);
|
||||
--color-secondary-contrast-light: var(--color-secondary-50);
|
||||
|
||||
--color-tertiary-50: oklch(96.5% 0.030 299deg);
|
||||
--color-tertiary-100: oklch(91.0% 0.058 297deg);
|
||||
--color-tertiary-50: oklch(96.5% 0.03 299deg);
|
||||
--color-tertiary-100: oklch(91% 0.058 297deg);
|
||||
--color-tertiary-200: oklch(84.5% 0.092 296deg);
|
||||
--color-tertiary-300: oklch(77.0% 0.122 295deg);
|
||||
--color-tertiary-300: oklch(77% 0.122 295deg);
|
||||
--color-tertiary-400: oklch(68.5% 0.148 295deg);
|
||||
--color-tertiary-500: oklch(57.0% 0.158 295deg);
|
||||
--color-tertiary-600: oklch(49.5% 0.150 294deg);
|
||||
--color-tertiary-500: oklch(57% 0.158 295deg);
|
||||
--color-tertiary-600: oklch(49.5% 0.15 294deg);
|
||||
--color-tertiary-700: oklch(42.5% 0.138 293deg);
|
||||
--color-tertiary-800: oklch(35.5% 0.122 292deg);
|
||||
--color-tertiary-900: oklch(28.5% 0.102 291deg);
|
||||
--color-tertiary-950: oklch(21.0% 0.080 290deg);
|
||||
--color-tertiary-contrast-dark: var(--color-tertiary-950);
|
||||
--color-tertiary-950: oklch(21% 0.08 290deg);
|
||||
--color-tertiary-contrast-dark: var(--color-tertiary-950);
|
||||
--color-tertiary-contrast-light: var(--color-tertiary-50);
|
||||
|
||||
--color-success-50: oklch(95.77% 0.05 152.69deg);
|
||||
--color-success-100: oklch(91.59% 0.06 152.00deg);
|
||||
--color-success-50: oklch(95.77% 0.05 152.69deg);
|
||||
--color-success-100: oklch(91.59% 0.06 152deg);
|
||||
--color-success-200: oklch(87.45% 0.08 152.08deg);
|
||||
--color-success-300: oklch(83.57% 0.09 150.85deg);
|
||||
--color-success-400: oklch(79.47% 0.11 150.71deg);
|
||||
@@ -114,51 +114,51 @@ html[data-theme='AE_Firefly_Rainbow'] {
|
||||
--color-success-600: oklch(67.65% 0.11 149.94deg);
|
||||
--color-success-700: oklch(59.71% 0.09 150.42deg);
|
||||
--color-success-800: oklch(51.74% 0.08 150.24deg);
|
||||
--color-success-900: oklch(43.20% 0.06 151.12deg);
|
||||
--color-success-950: oklch(34.20% 0.04 151.44deg);
|
||||
--color-success-contrast-dark: var(--color-success-950);
|
||||
--color-success-900: oklch(43.2% 0.06 151.12deg);
|
||||
--color-success-950: oklch(34.2% 0.04 151.44deg);
|
||||
--color-success-contrast-dark: var(--color-success-950);
|
||||
--color-success-contrast-light: var(--color-success-50);
|
||||
|
||||
--color-warning-50: oklch(97.5% 0.065 78deg);
|
||||
--color-warning-100: oklch(93.5% 0.090 75deg);
|
||||
--color-warning-200: oklch(89.5% 0.120 73deg);
|
||||
--color-warning-50: oklch(97.5% 0.065 78deg);
|
||||
--color-warning-100: oklch(93.5% 0.09 75deg);
|
||||
--color-warning-200: oklch(89.5% 0.12 73deg);
|
||||
--color-warning-300: oklch(85.5% 0.145 70deg);
|
||||
--color-warning-400: oklch(81.5% 0.160 67deg);
|
||||
--color-warning-500: oklch(77.0% 0.165 65deg);
|
||||
--color-warning-400: oklch(81.5% 0.16 67deg);
|
||||
--color-warning-500: oklch(77% 0.165 65deg);
|
||||
--color-warning-600: oklch(69.5% 0.155 64deg);
|
||||
--color-warning-700: oklch(61.5% 0.140 63deg);
|
||||
--color-warning-700: oklch(61.5% 0.14 63deg);
|
||||
--color-warning-800: oklch(53.5% 0.125 62deg);
|
||||
--color-warning-900: oklch(45.0% 0.105 61deg);
|
||||
--color-warning-950: oklch(37.0% 0.088 60deg);
|
||||
--color-warning-contrast-dark: var(--color-warning-950);
|
||||
--color-warning-900: oklch(45% 0.105 61deg);
|
||||
--color-warning-950: oklch(37% 0.088 60deg);
|
||||
--color-warning-contrast-dark: var(--color-warning-950);
|
||||
--color-warning-contrast-light: var(--color-warning-50);
|
||||
|
||||
--color-error-50: oklch(95.0% 0.040 18deg);
|
||||
--color-error-100: oklch(88.0% 0.070 20deg);
|
||||
--color-error-200: oklch(80.0% 0.105 21deg);
|
||||
--color-error-300: oklch(72.0% 0.140 22deg);
|
||||
--color-error-400: oklch(64.5% 0.170 23deg);
|
||||
--color-error-50: oklch(95% 0.04 18deg);
|
||||
--color-error-100: oklch(88% 0.07 20deg);
|
||||
--color-error-200: oklch(80% 0.105 21deg);
|
||||
--color-error-300: oklch(72% 0.14 22deg);
|
||||
--color-error-400: oklch(64.5% 0.17 23deg);
|
||||
--color-error-500: oklch(57.5% 0.195 24deg);
|
||||
--color-error-600: oklch(51.5% 0.182 25deg);
|
||||
--color-error-700: oklch(45.5% 0.165 26deg);
|
||||
--color-error-800: oklch(39.5% 0.148 27deg);
|
||||
--color-error-900: oklch(33.0% 0.128 28deg);
|
||||
--color-error-900: oklch(33% 0.128 28deg);
|
||||
--color-error-950: oklch(26.5% 0.108 29deg);
|
||||
--color-error-contrast-dark: var(--color-error-950);
|
||||
--color-error-contrast-dark: var(--color-error-950);
|
||||
--color-error-contrast-light: var(--color-error-50);
|
||||
|
||||
--color-surface-50: oklch(99.2% 0.004 75deg);
|
||||
--color-surface-100: oklch(97.0% 0.007 72deg);
|
||||
--color-surface-200: oklch(93.5% 0.010 70deg);
|
||||
--color-surface-50: oklch(99.2% 0.004 75deg);
|
||||
--color-surface-100: oklch(97% 0.007 72deg);
|
||||
--color-surface-200: oklch(93.5% 0.01 70deg);
|
||||
--color-surface-300: oklch(88.5% 0.013 68deg);
|
||||
--color-surface-400: oklch(81.5% 0.016 66deg);
|
||||
--color-surface-500: oklch(70.5% 0.018 64deg);
|
||||
--color-surface-600: oklch(59.0% 0.018 62deg);
|
||||
--color-surface-600: oklch(59% 0.018 62deg);
|
||||
--color-surface-700: oklch(47.5% 0.018 58deg);
|
||||
--color-surface-800: oklch(35.5% 0.020 55deg);
|
||||
--color-surface-900: oklch(24.5% 0.020 52deg);
|
||||
--color-surface-800: oklch(35.5% 0.02 55deg);
|
||||
--color-surface-900: oklch(24.5% 0.02 52deg);
|
||||
--color-surface-950: oklch(15.5% 0.022 48deg);
|
||||
--color-surface-contrast-dark: var(--color-surface-950);
|
||||
--color-surface-contrast-dark: var(--color-surface-950);
|
||||
--color-surface-contrast-light: var(--color-surface-50);
|
||||
}
|
||||
|
||||
@@ -180,20 +180,20 @@ html.dark[data-theme='AE_Firefly_Rainbow'] {
|
||||
* Kept within sRGB gamut across the full ramp.
|
||||
* At 500 (L≈60%): sufficient contrast with primary-50 text (≥4:1).
|
||||
* =================================================================== */
|
||||
--color-primary-50: oklch(97.0% 0.020 15deg);
|
||||
--color-primary-100: oklch(92.0% 0.048 14deg);
|
||||
--color-primary-200: oklch(86.0% 0.085 13deg);
|
||||
--color-primary-300: oklch(79.0% 0.125 13deg);
|
||||
--color-primary-400: oklch(71.0% 0.160 13deg);
|
||||
--color-primary-500: oklch(60.0% 0.190 14deg);
|
||||
--color-primary-50: oklch(97% 0.02 15deg);
|
||||
--color-primary-100: oklch(92% 0.048 14deg);
|
||||
--color-primary-200: oklch(86% 0.085 13deg);
|
||||
--color-primary-300: oklch(79% 0.125 13deg);
|
||||
--color-primary-400: oklch(71% 0.16 13deg);
|
||||
--color-primary-500: oklch(60% 0.19 14deg);
|
||||
--color-primary-600: oklch(52.5% 0.178 15deg);
|
||||
--color-primary-700: oklch(45.0% 0.162 16deg);
|
||||
--color-primary-700: oklch(45% 0.162 16deg);
|
||||
--color-primary-800: oklch(37.5% 0.142 17deg);
|
||||
--color-primary-900: oklch(30.0% 0.118 18deg);
|
||||
--color-primary-900: oklch(30% 0.118 18deg);
|
||||
--color-primary-950: oklch(22.5% 0.092 19deg);
|
||||
--color-primary-contrast-dark: var(--color-primary-950);
|
||||
--color-primary-contrast-dark: var(--color-primary-950);
|
||||
--color-primary-contrast-light: var(--color-primary-50);
|
||||
--color-primary-contrast-50: var(--color-primary-contrast-dark);
|
||||
--color-primary-contrast-50: var(--color-primary-contrast-dark);
|
||||
--color-primary-contrast-100: var(--color-primary-contrast-dark);
|
||||
--color-primary-contrast-200: var(--color-primary-contrast-dark);
|
||||
--color-primary-contrast-300: var(--color-primary-contrast-dark);
|
||||
@@ -212,20 +212,20 @@ html.dark[data-theme='AE_Firefly_Rainbow'] {
|
||||
* it bridges the warm red primary and the cool violet tertiary.
|
||||
* Used for secondary actions, success-adjacent highlights, badges.
|
||||
* =================================================================== */
|
||||
--color-secondary-50: oklch(97.0% 0.040 152deg);
|
||||
--color-secondary-50: oklch(97% 0.04 152deg);
|
||||
--color-secondary-100: oklch(92.5% 0.072 150deg);
|
||||
--color-secondary-200: oklch(87.0% 0.105 149deg);
|
||||
--color-secondary-300: oklch(81.0% 0.132 148deg);
|
||||
--color-secondary-200: oklch(87% 0.105 149deg);
|
||||
--color-secondary-300: oklch(81% 0.132 148deg);
|
||||
--color-secondary-400: oklch(74.5% 0.152 148deg);
|
||||
--color-secondary-500: oklch(62.0% 0.160 148deg);
|
||||
--color-secondary-500: oklch(62% 0.16 148deg);
|
||||
--color-secondary-600: oklch(53.5% 0.148 148deg);
|
||||
--color-secondary-700: oklch(45.5% 0.132 147deg);
|
||||
--color-secondary-800: oklch(37.5% 0.112 146deg);
|
||||
--color-secondary-900: oklch(29.5% 0.090 145deg);
|
||||
--color-secondary-900: oklch(29.5% 0.09 145deg);
|
||||
--color-secondary-950: oklch(21.5% 0.068 144deg);
|
||||
--color-secondary-contrast-dark: var(--color-secondary-950);
|
||||
--color-secondary-contrast-dark: var(--color-secondary-950);
|
||||
--color-secondary-contrast-light: var(--color-secondary-50);
|
||||
--color-secondary-contrast-50: var(--color-secondary-contrast-dark);
|
||||
--color-secondary-contrast-50: var(--color-secondary-contrast-dark);
|
||||
--color-secondary-contrast-100: var(--color-secondary-contrast-dark);
|
||||
--color-secondary-contrast-200: var(--color-secondary-contrast-dark);
|
||||
--color-secondary-contrast-300: var(--color-secondary-contrast-dark);
|
||||
@@ -244,20 +244,20 @@ html.dark[data-theme='AE_Firefly_Rainbow'] {
|
||||
* brand color slots. Creates striking contrast with the warm primary.
|
||||
* Used for location chips, deep accents, tertiary elements.
|
||||
* =================================================================== */
|
||||
--color-tertiary-50: oklch(96.5% 0.030 299deg);
|
||||
--color-tertiary-100: oklch(91.0% 0.058 297deg);
|
||||
--color-tertiary-50: oklch(96.5% 0.03 299deg);
|
||||
--color-tertiary-100: oklch(91% 0.058 297deg);
|
||||
--color-tertiary-200: oklch(84.5% 0.092 296deg);
|
||||
--color-tertiary-300: oklch(77.0% 0.122 295deg);
|
||||
--color-tertiary-300: oklch(77% 0.122 295deg);
|
||||
--color-tertiary-400: oklch(68.5% 0.148 295deg);
|
||||
--color-tertiary-500: oklch(57.0% 0.158 295deg);
|
||||
--color-tertiary-600: oklch(49.5% 0.150 294deg);
|
||||
--color-tertiary-500: oklch(57% 0.158 295deg);
|
||||
--color-tertiary-600: oklch(49.5% 0.15 294deg);
|
||||
--color-tertiary-700: oklch(42.5% 0.138 293deg);
|
||||
--color-tertiary-800: oklch(35.5% 0.122 292deg);
|
||||
--color-tertiary-900: oklch(28.5% 0.102 291deg);
|
||||
--color-tertiary-950: oklch(21.0% 0.080 290deg);
|
||||
--color-tertiary-contrast-dark: var(--color-tertiary-950);
|
||||
--color-tertiary-950: oklch(21% 0.08 290deg);
|
||||
--color-tertiary-contrast-dark: var(--color-tertiary-950);
|
||||
--color-tertiary-contrast-light: var(--color-tertiary-50);
|
||||
--color-tertiary-contrast-50: var(--color-tertiary-contrast-dark);
|
||||
--color-tertiary-contrast-50: var(--color-tertiary-contrast-dark);
|
||||
--color-tertiary-contrast-100: var(--color-tertiary-contrast-dark);
|
||||
--color-tertiary-contrast-200: var(--color-tertiary-contrast-dark);
|
||||
--color-tertiary-contrast-300: var(--color-tertiary-contrast-dark);
|
||||
@@ -274,8 +274,8 @@ html.dark[data-theme='AE_Firefly_Rainbow'] {
|
||||
* Hue: ~152°. Consistent with AE_Firefly for recognizable semantic
|
||||
* color meaning across OSIT themes.
|
||||
* =================================================================== */
|
||||
--color-success-50: oklch(95.77% 0.05 152.69deg);
|
||||
--color-success-100: oklch(91.59% 0.06 152.00deg);
|
||||
--color-success-50: oklch(95.77% 0.05 152.69deg);
|
||||
--color-success-100: oklch(91.59% 0.06 152deg);
|
||||
--color-success-200: oklch(87.45% 0.08 152.08deg);
|
||||
--color-success-300: oklch(83.57% 0.09 150.85deg);
|
||||
--color-success-400: oklch(79.47% 0.11 150.71deg);
|
||||
@@ -283,11 +283,11 @@ html.dark[data-theme='AE_Firefly_Rainbow'] {
|
||||
--color-success-600: oklch(67.65% 0.11 149.94deg);
|
||||
--color-success-700: oklch(59.71% 0.09 150.42deg);
|
||||
--color-success-800: oklch(51.74% 0.08 150.24deg);
|
||||
--color-success-900: oklch(43.20% 0.06 151.12deg);
|
||||
--color-success-950: oklch(34.20% 0.04 151.44deg);
|
||||
--color-success-contrast-dark: var(--color-success-950);
|
||||
--color-success-900: oklch(43.2% 0.06 151.12deg);
|
||||
--color-success-950: oklch(34.2% 0.04 151.44deg);
|
||||
--color-success-contrast-dark: var(--color-success-950);
|
||||
--color-success-contrast-light: var(--color-success-50);
|
||||
--color-success-contrast-50: var(--color-success-contrast-dark);
|
||||
--color-success-contrast-50: var(--color-success-contrast-dark);
|
||||
--color-success-contrast-100: var(--color-success-contrast-dark);
|
||||
--color-success-contrast-200: var(--color-success-contrast-dark);
|
||||
--color-success-contrast-300: var(--color-success-contrast-dark);
|
||||
@@ -303,20 +303,20 @@ html.dark[data-theme='AE_Firefly_Rainbow'] {
|
||||
* WARNING — Amber Orange
|
||||
* Consistent with AE_Firefly for recognizable semantic meaning.
|
||||
* =================================================================== */
|
||||
--color-warning-50: oklch(97.5% 0.065 78deg);
|
||||
--color-warning-100: oklch(93.5% 0.090 75deg);
|
||||
--color-warning-200: oklch(89.5% 0.120 73deg);
|
||||
--color-warning-50: oklch(97.5% 0.065 78deg);
|
||||
--color-warning-100: oklch(93.5% 0.09 75deg);
|
||||
--color-warning-200: oklch(89.5% 0.12 73deg);
|
||||
--color-warning-300: oklch(85.5% 0.145 70deg);
|
||||
--color-warning-400: oklch(81.5% 0.160 67deg);
|
||||
--color-warning-500: oklch(77.0% 0.165 65deg);
|
||||
--color-warning-400: oklch(81.5% 0.16 67deg);
|
||||
--color-warning-500: oklch(77% 0.165 65deg);
|
||||
--color-warning-600: oklch(69.5% 0.155 64deg);
|
||||
--color-warning-700: oklch(61.5% 0.140 63deg);
|
||||
--color-warning-700: oklch(61.5% 0.14 63deg);
|
||||
--color-warning-800: oklch(53.5% 0.125 62deg);
|
||||
--color-warning-900: oklch(45.0% 0.105 61deg);
|
||||
--color-warning-950: oklch(37.0% 0.088 60deg);
|
||||
--color-warning-contrast-dark: var(--color-warning-950);
|
||||
--color-warning-900: oklch(45% 0.105 61deg);
|
||||
--color-warning-950: oklch(37% 0.088 60deg);
|
||||
--color-warning-contrast-dark: var(--color-warning-950);
|
||||
--color-warning-contrast-light: var(--color-warning-50);
|
||||
--color-warning-contrast-50: var(--color-warning-contrast-dark);
|
||||
--color-warning-contrast-50: var(--color-warning-contrast-dark);
|
||||
--color-warning-contrast-100: var(--color-warning-contrast-dark);
|
||||
--color-warning-contrast-200: var(--color-warning-contrast-dark);
|
||||
--color-warning-contrast-300: var(--color-warning-contrast-dark);
|
||||
@@ -332,20 +332,20 @@ html.dark[data-theme='AE_Firefly_Rainbow'] {
|
||||
* ERROR — Soft Coral/Rose
|
||||
* Consistent with AE_Firefly for recognizable semantic meaning.
|
||||
* =================================================================== */
|
||||
--color-error-50: oklch(95.0% 0.040 18deg);
|
||||
--color-error-100: oklch(88.0% 0.070 20deg);
|
||||
--color-error-200: oklch(80.0% 0.105 21deg);
|
||||
--color-error-300: oklch(72.0% 0.140 22deg);
|
||||
--color-error-400: oklch(64.5% 0.170 23deg);
|
||||
--color-error-50: oklch(95% 0.04 18deg);
|
||||
--color-error-100: oklch(88% 0.07 20deg);
|
||||
--color-error-200: oklch(80% 0.105 21deg);
|
||||
--color-error-300: oklch(72% 0.14 22deg);
|
||||
--color-error-400: oklch(64.5% 0.17 23deg);
|
||||
--color-error-500: oklch(57.5% 0.195 24deg);
|
||||
--color-error-600: oklch(51.5% 0.182 25deg);
|
||||
--color-error-700: oklch(45.5% 0.165 26deg);
|
||||
--color-error-800: oklch(39.5% 0.148 27deg);
|
||||
--color-error-900: oklch(33.0% 0.128 28deg);
|
||||
--color-error-900: oklch(33% 0.128 28deg);
|
||||
--color-error-950: oklch(26.5% 0.108 29deg);
|
||||
--color-error-contrast-dark: var(--color-error-950);
|
||||
--color-error-contrast-dark: var(--color-error-950);
|
||||
--color-error-contrast-light: var(--color-error-50);
|
||||
--color-error-contrast-50: var(--color-error-contrast-dark);
|
||||
--color-error-contrast-50: var(--color-error-contrast-dark);
|
||||
--color-error-contrast-100: var(--color-error-contrast-dark);
|
||||
--color-error-contrast-200: var(--color-error-contrast-dark);
|
||||
--color-error-contrast-300: var(--color-error-contrast-dark);
|
||||
@@ -366,20 +366,20 @@ html.dark[data-theme='AE_Firefly_Rainbow'] {
|
||||
* 50 → body-bg light: warm near-white, like morning paper
|
||||
* 950 → body-bg dark: deep warm charcoal, like a dim theatre
|
||||
* =================================================================== */
|
||||
--color-surface-50: oklch(99.2% 0.004 75deg);
|
||||
--color-surface-100: oklch(97.0% 0.007 72deg);
|
||||
--color-surface-200: oklch(93.5% 0.010 70deg);
|
||||
--color-surface-50: oklch(99.2% 0.004 75deg);
|
||||
--color-surface-100: oklch(97% 0.007 72deg);
|
||||
--color-surface-200: oklch(93.5% 0.01 70deg);
|
||||
--color-surface-300: oklch(88.5% 0.013 68deg);
|
||||
--color-surface-400: oklch(81.5% 0.016 66deg);
|
||||
--color-surface-500: oklch(70.5% 0.018 64deg);
|
||||
--color-surface-600: oklch(59.0% 0.018 62deg);
|
||||
--color-surface-600: oklch(59% 0.018 62deg);
|
||||
--color-surface-700: oklch(47.5% 0.018 58deg);
|
||||
--color-surface-800: oklch(35.5% 0.020 55deg);
|
||||
--color-surface-900: oklch(24.5% 0.020 52deg);
|
||||
--color-surface-800: oklch(35.5% 0.02 55deg);
|
||||
--color-surface-900: oklch(24.5% 0.02 52deg);
|
||||
--color-surface-950: oklch(15.5% 0.022 48deg);
|
||||
--color-surface-contrast-dark: var(--color-surface-950);
|
||||
--color-surface-contrast-dark: var(--color-surface-950);
|
||||
--color-surface-contrast-light: var(--color-surface-50);
|
||||
--color-surface-contrast-50: var(--color-surface-contrast-dark);
|
||||
--color-surface-contrast-50: var(--color-surface-contrast-dark);
|
||||
--color-surface-contrast-100: var(--color-surface-contrast-dark);
|
||||
--color-surface-contrast-200: var(--color-surface-contrast-dark);
|
||||
--color-surface-contrast-300: var(--color-surface-contrast-dark);
|
||||
|
||||
@@ -60,50 +60,50 @@ html[data-theme='AE_Firefly_SteelBlue'] {
|
||||
--radius-base: 0.375rem;
|
||||
--radius-container: 0.875rem;
|
||||
/* --- Color ramps (light mode) copied from dark block so both modes have full ramps --- */
|
||||
--color-primary-50: oklch(96.5% 0.022 214deg);
|
||||
--color-primary-100: oklch(91.0% 0.045 213deg);
|
||||
--color-primary-50: oklch(96.5% 0.022 214deg);
|
||||
--color-primary-100: oklch(91% 0.045 213deg);
|
||||
--color-primary-200: oklch(84.5% 0.072 212deg);
|
||||
--color-primary-300: oklch(76.5% 0.097 212deg);
|
||||
--color-primary-400: oklch(67.0% 0.115 213deg);
|
||||
--color-primary-500: oklch(56.0% 0.115 214deg);
|
||||
--color-primary-600: oklch(49.0% 0.112 214deg);
|
||||
--color-primary-400: oklch(67% 0.115 213deg);
|
||||
--color-primary-500: oklch(56% 0.115 214deg);
|
||||
--color-primary-600: oklch(49% 0.112 214deg);
|
||||
--color-primary-700: oklch(41.5% 0.105 213deg);
|
||||
--color-primary-800: oklch(34.0% 0.095 212deg);
|
||||
--color-primary-900: oklch(26.5% 0.080 211deg);
|
||||
--color-primary-800: oklch(34% 0.095 212deg);
|
||||
--color-primary-900: oklch(26.5% 0.08 211deg);
|
||||
--color-primary-950: oklch(18.5% 0.065 210deg);
|
||||
--color-primary-contrast-dark: var(--color-primary-950);
|
||||
--color-primary-contrast-dark: var(--color-primary-950);
|
||||
--color-primary-contrast-light: var(--color-primary-50);
|
||||
|
||||
--color-secondary-50: oklch(97.5% 0.055 56deg);
|
||||
--color-secondary-100: oklch(93.5% 0.090 55deg);
|
||||
--color-secondary-200: oklch(89.5% 0.120 54deg);
|
||||
--color-secondary-50: oklch(97.5% 0.055 56deg);
|
||||
--color-secondary-100: oklch(93.5% 0.09 55deg);
|
||||
--color-secondary-200: oklch(89.5% 0.12 54deg);
|
||||
--color-secondary-300: oklch(85.5% 0.148 53deg);
|
||||
--color-secondary-400: oklch(81.5% 0.162 52deg);
|
||||
--color-secondary-500: oklch(76.5% 0.162 51deg);
|
||||
--color-secondary-600: oklch(68.5% 0.152 50deg);
|
||||
--color-secondary-700: oklch(60.5% 0.138 49deg);
|
||||
--color-secondary-800: oklch(52.0% 0.122 48deg);
|
||||
--color-secondary-800: oklch(52% 0.122 48deg);
|
||||
--color-secondary-900: oklch(43.5% 0.102 47deg);
|
||||
--color-secondary-950: oklch(35.0% 0.084 46deg);
|
||||
--color-secondary-contrast-dark: var(--color-secondary-950);
|
||||
--color-secondary-950: oklch(35% 0.084 46deg);
|
||||
--color-secondary-contrast-dark: var(--color-secondary-950);
|
||||
--color-secondary-contrast-light: var(--color-secondary-50);
|
||||
|
||||
--color-tertiary-50: oklch(95.5% 0.025 232deg);
|
||||
--color-tertiary-50: oklch(95.5% 0.025 232deg);
|
||||
--color-tertiary-100: oklch(89.5% 0.048 231deg);
|
||||
--color-tertiary-200: oklch(82.5% 0.072 230deg);
|
||||
--color-tertiary-300: oklch(74.5% 0.095 229deg);
|
||||
--color-tertiary-400: oklch(65.5% 0.120 229deg);
|
||||
--color-tertiary-400: oklch(65.5% 0.12 229deg);
|
||||
--color-tertiary-500: oklch(54.5% 0.135 230deg);
|
||||
--color-tertiary-600: oklch(47.0% 0.132 230deg);
|
||||
--color-tertiary-600: oklch(47% 0.132 230deg);
|
||||
--color-tertiary-700: oklch(39.5% 0.122 229deg);
|
||||
--color-tertiary-800: oklch(32.0% 0.108 228deg);
|
||||
--color-tertiary-900: oklch(25.0% 0.090 227deg);
|
||||
--color-tertiary-800: oklch(32% 0.108 228deg);
|
||||
--color-tertiary-900: oklch(25% 0.09 227deg);
|
||||
--color-tertiary-950: oklch(17.5% 0.072 226deg);
|
||||
--color-tertiary-contrast-dark: var(--color-tertiary-950);
|
||||
--color-tertiary-contrast-dark: var(--color-tertiary-950);
|
||||
--color-tertiary-contrast-light: var(--color-tertiary-50);
|
||||
|
||||
--color-success-50: oklch(95.77% 0.05 152.69deg);
|
||||
--color-success-100: oklch(91.59% 0.06 152.00deg);
|
||||
--color-success-50: oklch(95.77% 0.05 152.69deg);
|
||||
--color-success-100: oklch(91.59% 0.06 152deg);
|
||||
--color-success-200: oklch(87.45% 0.08 152.08deg);
|
||||
--color-success-300: oklch(83.57% 0.09 150.85deg);
|
||||
--color-success-400: oklch(79.47% 0.11 150.71deg);
|
||||
@@ -111,51 +111,51 @@ html[data-theme='AE_Firefly_SteelBlue'] {
|
||||
--color-success-600: oklch(67.65% 0.11 149.94deg);
|
||||
--color-success-700: oklch(59.71% 0.09 150.42deg);
|
||||
--color-success-800: oklch(51.74% 0.08 150.24deg);
|
||||
--color-success-900: oklch(43.20% 0.06 151.12deg);
|
||||
--color-success-950: oklch(34.20% 0.04 151.44deg);
|
||||
--color-success-contrast-dark: var(--color-success-950);
|
||||
--color-success-900: oklch(43.2% 0.06 151.12deg);
|
||||
--color-success-950: oklch(34.2% 0.04 151.44deg);
|
||||
--color-success-contrast-dark: var(--color-success-950);
|
||||
--color-success-contrast-light: var(--color-success-50);
|
||||
|
||||
--color-warning-50: oklch(97.5% 0.065 78deg);
|
||||
--color-warning-100: oklch(93.5% 0.090 75deg);
|
||||
--color-warning-200: oklch(89.5% 0.120 73deg);
|
||||
--color-warning-50: oklch(97.5% 0.065 78deg);
|
||||
--color-warning-100: oklch(93.5% 0.09 75deg);
|
||||
--color-warning-200: oklch(89.5% 0.12 73deg);
|
||||
--color-warning-300: oklch(85.5% 0.145 70deg);
|
||||
--color-warning-400: oklch(81.5% 0.160 67deg);
|
||||
--color-warning-500: oklch(77.0% 0.165 65deg);
|
||||
--color-warning-400: oklch(81.5% 0.16 67deg);
|
||||
--color-warning-500: oklch(77% 0.165 65deg);
|
||||
--color-warning-600: oklch(69.5% 0.155 64deg);
|
||||
--color-warning-700: oklch(61.5% 0.140 63deg);
|
||||
--color-warning-700: oklch(61.5% 0.14 63deg);
|
||||
--color-warning-800: oklch(53.5% 0.125 62deg);
|
||||
--color-warning-900: oklch(45.0% 0.105 61deg);
|
||||
--color-warning-950: oklch(37.0% 0.088 60deg);
|
||||
--color-warning-contrast-dark: var(--color-warning-950);
|
||||
--color-warning-900: oklch(45% 0.105 61deg);
|
||||
--color-warning-950: oklch(37% 0.088 60deg);
|
||||
--color-warning-contrast-dark: var(--color-warning-950);
|
||||
--color-warning-contrast-light: var(--color-warning-50);
|
||||
|
||||
--color-error-50: oklch(95.0% 0.040 18deg);
|
||||
--color-error-100: oklch(88.0% 0.070 20deg);
|
||||
--color-error-200: oklch(80.0% 0.105 21deg);
|
||||
--color-error-300: oklch(72.0% 0.140 22deg);
|
||||
--color-error-400: oklch(64.5% 0.170 23deg);
|
||||
--color-error-50: oklch(95% 0.04 18deg);
|
||||
--color-error-100: oklch(88% 0.07 20deg);
|
||||
--color-error-200: oklch(80% 0.105 21deg);
|
||||
--color-error-300: oklch(72% 0.14 22deg);
|
||||
--color-error-400: oklch(64.5% 0.17 23deg);
|
||||
--color-error-500: oklch(57.5% 0.195 24deg);
|
||||
--color-error-600: oklch(51.5% 0.182 25deg);
|
||||
--color-error-700: oklch(45.5% 0.165 26deg);
|
||||
--color-error-800: oklch(39.5% 0.148 27deg);
|
||||
--color-error-900: oklch(33.0% 0.128 28deg);
|
||||
--color-error-900: oklch(33% 0.128 28deg);
|
||||
--color-error-950: oklch(26.5% 0.108 29deg);
|
||||
--color-error-contrast-dark: var(--color-error-950);
|
||||
--color-error-contrast-dark: var(--color-error-950);
|
||||
--color-error-contrast-light: var(--color-error-50);
|
||||
|
||||
--color-surface-50: oklch(99.0% 0.004 220deg);
|
||||
--color-surface-50: oklch(99% 0.004 220deg);
|
||||
--color-surface-100: oklch(96.5% 0.008 218deg);
|
||||
--color-surface-200: oklch(92.5% 0.012 217deg);
|
||||
--color-surface-300: oklch(87.0% 0.016 216deg);
|
||||
--color-surface-400: oklch(78.5% 0.020 215deg);
|
||||
--color-surface-300: oklch(87% 0.016 216deg);
|
||||
--color-surface-400: oklch(78.5% 0.02 215deg);
|
||||
--color-surface-500: oklch(66.5% 0.022 217deg);
|
||||
--color-surface-600: oklch(54.5% 0.025 220deg);
|
||||
--color-surface-700: oklch(42.5% 0.028 223deg);
|
||||
--color-surface-800: oklch(31.0% 0.032 226deg);
|
||||
--color-surface-800: oklch(31% 0.032 226deg);
|
||||
--color-surface-900: oklch(20.5% 0.035 228deg);
|
||||
--color-surface-950: oklch(13.0% 0.040 232deg);
|
||||
--color-surface-contrast-dark: var(--color-surface-950);
|
||||
--color-surface-950: oklch(13% 0.04 232deg);
|
||||
--color-surface-contrast-dark: var(--color-surface-950);
|
||||
--color-surface-contrast-light: var(--color-surface-50);
|
||||
}
|
||||
|
||||
@@ -175,20 +175,20 @@ html.dark[data-theme='AE_Firefly_SteelBlue'] {
|
||||
* Approx: #4682B4 (CSS SteelBlue) sits at oklch(56%, 0.113, 214°).
|
||||
* At 500 (L≈56%): sufficient contrast with primary-50 text (≥4:1).
|
||||
* =================================================================== */
|
||||
--color-primary-50: oklch(96.5% 0.022 214deg);
|
||||
--color-primary-100: oklch(91.0% 0.045 213deg);
|
||||
--color-primary-50: oklch(96.5% 0.022 214deg);
|
||||
--color-primary-100: oklch(91% 0.045 213deg);
|
||||
--color-primary-200: oklch(84.5% 0.072 212deg);
|
||||
--color-primary-300: oklch(76.5% 0.097 212deg);
|
||||
--color-primary-400: oklch(67.0% 0.115 213deg);
|
||||
--color-primary-500: oklch(56.0% 0.115 214deg);
|
||||
--color-primary-600: oklch(49.0% 0.112 214deg);
|
||||
--color-primary-400: oklch(67% 0.115 213deg);
|
||||
--color-primary-500: oklch(56% 0.115 214deg);
|
||||
--color-primary-600: oklch(49% 0.112 214deg);
|
||||
--color-primary-700: oklch(41.5% 0.105 213deg);
|
||||
--color-primary-800: oklch(34.0% 0.095 212deg);
|
||||
--color-primary-900: oklch(26.5% 0.080 211deg);
|
||||
--color-primary-800: oklch(34% 0.095 212deg);
|
||||
--color-primary-900: oklch(26.5% 0.08 211deg);
|
||||
--color-primary-950: oklch(18.5% 0.065 210deg);
|
||||
--color-primary-contrast-dark: var(--color-primary-950);
|
||||
--color-primary-contrast-dark: var(--color-primary-950);
|
||||
--color-primary-contrast-light: var(--color-primary-50);
|
||||
--color-primary-contrast-50: var(--color-primary-contrast-dark);
|
||||
--color-primary-contrast-50: var(--color-primary-contrast-dark);
|
||||
--color-primary-contrast-100: var(--color-primary-contrast-dark);
|
||||
--color-primary-contrast-200: var(--color-primary-contrast-dark);
|
||||
--color-primary-contrast-300: var(--color-primary-contrast-dark);
|
||||
@@ -206,20 +206,20 @@ html.dark[data-theme='AE_Firefly_SteelBlue'] {
|
||||
* of steel blue. The classic "metal on metal" contrast pairing —
|
||||
* used for secondary actions, badges, and call-to-action highlights.
|
||||
* =================================================================== */
|
||||
--color-secondary-50: oklch(97.5% 0.055 56deg);
|
||||
--color-secondary-100: oklch(93.5% 0.090 55deg);
|
||||
--color-secondary-200: oklch(89.5% 0.120 54deg);
|
||||
--color-secondary-50: oklch(97.5% 0.055 56deg);
|
||||
--color-secondary-100: oklch(93.5% 0.09 55deg);
|
||||
--color-secondary-200: oklch(89.5% 0.12 54deg);
|
||||
--color-secondary-300: oklch(85.5% 0.148 53deg);
|
||||
--color-secondary-400: oklch(81.5% 0.162 52deg);
|
||||
--color-secondary-500: oklch(76.5% 0.162 51deg);
|
||||
--color-secondary-600: oklch(68.5% 0.152 50deg);
|
||||
--color-secondary-700: oklch(60.5% 0.138 49deg);
|
||||
--color-secondary-800: oklch(52.0% 0.122 48deg);
|
||||
--color-secondary-800: oklch(52% 0.122 48deg);
|
||||
--color-secondary-900: oklch(43.5% 0.102 47deg);
|
||||
--color-secondary-950: oklch(35.0% 0.084 46deg);
|
||||
--color-secondary-contrast-dark: var(--color-secondary-950);
|
||||
--color-secondary-950: oklch(35% 0.084 46deg);
|
||||
--color-secondary-contrast-dark: var(--color-secondary-950);
|
||||
--color-secondary-contrast-light: var(--color-secondary-50);
|
||||
--color-secondary-contrast-50: var(--color-secondary-contrast-dark);
|
||||
--color-secondary-contrast-50: var(--color-secondary-contrast-dark);
|
||||
--color-secondary-contrast-100: var(--color-secondary-contrast-dark);
|
||||
--color-secondary-contrast-200: var(--color-secondary-contrast-dark);
|
||||
--color-secondary-contrast-300: var(--color-secondary-contrast-dark);
|
||||
@@ -237,20 +237,20 @@ html.dark[data-theme='AE_Firefly_SteelBlue'] {
|
||||
* like the heavy cobalt-blue depths under polished chrome.
|
||||
* Used for accents, location chips, and depth elements.
|
||||
* =================================================================== */
|
||||
--color-tertiary-50: oklch(95.5% 0.025 232deg);
|
||||
--color-tertiary-50: oklch(95.5% 0.025 232deg);
|
||||
--color-tertiary-100: oklch(89.5% 0.048 231deg);
|
||||
--color-tertiary-200: oklch(82.5% 0.072 230deg);
|
||||
--color-tertiary-300: oklch(74.5% 0.095 229deg);
|
||||
--color-tertiary-400: oklch(65.5% 0.120 229deg);
|
||||
--color-tertiary-400: oklch(65.5% 0.12 229deg);
|
||||
--color-tertiary-500: oklch(54.5% 0.135 230deg);
|
||||
--color-tertiary-600: oklch(47.0% 0.132 230deg);
|
||||
--color-tertiary-600: oklch(47% 0.132 230deg);
|
||||
--color-tertiary-700: oklch(39.5% 0.122 229deg);
|
||||
--color-tertiary-800: oklch(32.0% 0.108 228deg);
|
||||
--color-tertiary-900: oklch(25.0% 0.090 227deg);
|
||||
--color-tertiary-800: oklch(32% 0.108 228deg);
|
||||
--color-tertiary-900: oklch(25% 0.09 227deg);
|
||||
--color-tertiary-950: oklch(17.5% 0.072 226deg);
|
||||
--color-tertiary-contrast-dark: var(--color-tertiary-950);
|
||||
--color-tertiary-contrast-dark: var(--color-tertiary-950);
|
||||
--color-tertiary-contrast-light: var(--color-tertiary-50);
|
||||
--color-tertiary-contrast-50: var(--color-tertiary-contrast-dark);
|
||||
--color-tertiary-contrast-50: var(--color-tertiary-contrast-dark);
|
||||
--color-tertiary-contrast-100: var(--color-tertiary-contrast-dark);
|
||||
--color-tertiary-contrast-200: var(--color-tertiary-contrast-dark);
|
||||
--color-tertiary-contrast-300: var(--color-tertiary-contrast-dark);
|
||||
@@ -267,8 +267,8 @@ html.dark[data-theme='AE_Firefly_SteelBlue'] {
|
||||
* Hue: ~152°. Consistent with AE_Firefly for recognizable semantic
|
||||
* color meaning across OSIT themes.
|
||||
* =================================================================== */
|
||||
--color-success-50: oklch(95.77% 0.05 152.69deg);
|
||||
--color-success-100: oklch(91.59% 0.06 152.00deg);
|
||||
--color-success-50: oklch(95.77% 0.05 152.69deg);
|
||||
--color-success-100: oklch(91.59% 0.06 152deg);
|
||||
--color-success-200: oklch(87.45% 0.08 152.08deg);
|
||||
--color-success-300: oklch(83.57% 0.09 150.85deg);
|
||||
--color-success-400: oklch(79.47% 0.11 150.71deg);
|
||||
@@ -276,11 +276,11 @@ html.dark[data-theme='AE_Firefly_SteelBlue'] {
|
||||
--color-success-600: oklch(67.65% 0.11 149.94deg);
|
||||
--color-success-700: oklch(59.71% 0.09 150.42deg);
|
||||
--color-success-800: oklch(51.74% 0.08 150.24deg);
|
||||
--color-success-900: oklch(43.20% 0.06 151.12deg);
|
||||
--color-success-950: oklch(34.20% 0.04 151.44deg);
|
||||
--color-success-contrast-dark: var(--color-success-950);
|
||||
--color-success-900: oklch(43.2% 0.06 151.12deg);
|
||||
--color-success-950: oklch(34.2% 0.04 151.44deg);
|
||||
--color-success-contrast-dark: var(--color-success-950);
|
||||
--color-success-contrast-light: var(--color-success-50);
|
||||
--color-success-contrast-50: var(--color-success-contrast-dark);
|
||||
--color-success-contrast-50: var(--color-success-contrast-dark);
|
||||
--color-success-contrast-100: var(--color-success-contrast-dark);
|
||||
--color-success-contrast-200: var(--color-success-contrast-dark);
|
||||
--color-success-contrast-300: var(--color-success-contrast-dark);
|
||||
@@ -296,20 +296,20 @@ html.dark[data-theme='AE_Firefly_SteelBlue'] {
|
||||
* WARNING — Amber Orange
|
||||
* Consistent with AE_Firefly for recognizable semantic meaning.
|
||||
* =================================================================== */
|
||||
--color-warning-50: oklch(97.5% 0.065 78deg);
|
||||
--color-warning-100: oklch(93.5% 0.090 75deg);
|
||||
--color-warning-200: oklch(89.5% 0.120 73deg);
|
||||
--color-warning-50: oklch(97.5% 0.065 78deg);
|
||||
--color-warning-100: oklch(93.5% 0.09 75deg);
|
||||
--color-warning-200: oklch(89.5% 0.12 73deg);
|
||||
--color-warning-300: oklch(85.5% 0.145 70deg);
|
||||
--color-warning-400: oklch(81.5% 0.160 67deg);
|
||||
--color-warning-500: oklch(77.0% 0.165 65deg);
|
||||
--color-warning-400: oklch(81.5% 0.16 67deg);
|
||||
--color-warning-500: oklch(77% 0.165 65deg);
|
||||
--color-warning-600: oklch(69.5% 0.155 64deg);
|
||||
--color-warning-700: oklch(61.5% 0.140 63deg);
|
||||
--color-warning-700: oklch(61.5% 0.14 63deg);
|
||||
--color-warning-800: oklch(53.5% 0.125 62deg);
|
||||
--color-warning-900: oklch(45.0% 0.105 61deg);
|
||||
--color-warning-950: oklch(37.0% 0.088 60deg);
|
||||
--color-warning-contrast-dark: var(--color-warning-950);
|
||||
--color-warning-900: oklch(45% 0.105 61deg);
|
||||
--color-warning-950: oklch(37% 0.088 60deg);
|
||||
--color-warning-contrast-dark: var(--color-warning-950);
|
||||
--color-warning-contrast-light: var(--color-warning-50);
|
||||
--color-warning-contrast-50: var(--color-warning-contrast-dark);
|
||||
--color-warning-contrast-50: var(--color-warning-contrast-dark);
|
||||
--color-warning-contrast-100: var(--color-warning-contrast-dark);
|
||||
--color-warning-contrast-200: var(--color-warning-contrast-dark);
|
||||
--color-warning-contrast-300: var(--color-warning-contrast-dark);
|
||||
@@ -325,20 +325,20 @@ html.dark[data-theme='AE_Firefly_SteelBlue'] {
|
||||
* ERROR — Soft Coral/Rose
|
||||
* Consistent with AE_Firefly for recognizable semantic meaning.
|
||||
* =================================================================== */
|
||||
--color-error-50: oklch(95.0% 0.040 18deg);
|
||||
--color-error-100: oklch(88.0% 0.070 20deg);
|
||||
--color-error-200: oklch(80.0% 0.105 21deg);
|
||||
--color-error-300: oklch(72.0% 0.140 22deg);
|
||||
--color-error-400: oklch(64.5% 0.170 23deg);
|
||||
--color-error-50: oklch(95% 0.04 18deg);
|
||||
--color-error-100: oklch(88% 0.07 20deg);
|
||||
--color-error-200: oklch(80% 0.105 21deg);
|
||||
--color-error-300: oklch(72% 0.14 22deg);
|
||||
--color-error-400: oklch(64.5% 0.17 23deg);
|
||||
--color-error-500: oklch(57.5% 0.195 24deg);
|
||||
--color-error-600: oklch(51.5% 0.182 25deg);
|
||||
--color-error-700: oklch(45.5% 0.165 26deg);
|
||||
--color-error-800: oklch(39.5% 0.148 27deg);
|
||||
--color-error-900: oklch(33.0% 0.128 28deg);
|
||||
--color-error-900: oklch(33% 0.128 28deg);
|
||||
--color-error-950: oklch(26.5% 0.108 29deg);
|
||||
--color-error-contrast-dark: var(--color-error-950);
|
||||
--color-error-contrast-dark: var(--color-error-950);
|
||||
--color-error-contrast-light: var(--color-error-50);
|
||||
--color-error-contrast-50: var(--color-error-contrast-dark);
|
||||
--color-error-contrast-50: var(--color-error-contrast-dark);
|
||||
--color-error-contrast-100: var(--color-error-contrast-dark);
|
||||
--color-error-contrast-200: var(--color-error-contrast-dark);
|
||||
--color-error-contrast-300: var(--color-error-contrast-dark);
|
||||
@@ -359,20 +359,20 @@ html.dark[data-theme='AE_Firefly_SteelBlue'] {
|
||||
* 50 → body-bg light: brilliant near-white with a chrome whisper
|
||||
* 950 → body-bg dark: deep gunmetal with subtle cool-blue depth
|
||||
* =================================================================== */
|
||||
--color-surface-50: oklch(99.0% 0.004 220deg);
|
||||
--color-surface-50: oklch(99% 0.004 220deg);
|
||||
--color-surface-100: oklch(96.5% 0.008 218deg);
|
||||
--color-surface-200: oklch(92.5% 0.012 217deg);
|
||||
--color-surface-300: oklch(87.0% 0.016 216deg);
|
||||
--color-surface-400: oklch(78.5% 0.020 215deg);
|
||||
--color-surface-300: oklch(87% 0.016 216deg);
|
||||
--color-surface-400: oklch(78.5% 0.02 215deg);
|
||||
--color-surface-500: oklch(66.5% 0.022 217deg);
|
||||
--color-surface-600: oklch(54.5% 0.025 220deg);
|
||||
--color-surface-700: oklch(42.5% 0.028 223deg);
|
||||
--color-surface-800: oklch(31.0% 0.032 226deg);
|
||||
--color-surface-800: oklch(31% 0.032 226deg);
|
||||
--color-surface-900: oklch(20.5% 0.035 228deg);
|
||||
--color-surface-950: oklch(13.0% 0.040 232deg);
|
||||
--color-surface-contrast-dark: var(--color-surface-950);
|
||||
--color-surface-950: oklch(13% 0.04 232deg);
|
||||
--color-surface-contrast-dark: var(--color-surface-950);
|
||||
--color-surface-contrast-light: var(--color-surface-50);
|
||||
--color-surface-contrast-50: var(--color-surface-contrast-dark);
|
||||
--color-surface-contrast-50: var(--color-surface-contrast-dark);
|
||||
--color-surface-contrast-100: var(--color-surface-contrast-dark);
|
||||
--color-surface-contrast-200: var(--color-surface-contrast-dark);
|
||||
--color-surface-contrast-300: var(--color-surface-contrast-dark);
|
||||
|
||||
@@ -63,50 +63,50 @@ html[data-theme='AE_Firefly'] {
|
||||
|
||||
/* --- Color ramps (light mode) copied from dark block so both modes have full ramps --- */
|
||||
html[data-theme='AE_Firefly'] {
|
||||
--color-primary-50: oklch(96.5% 0.025 192deg);
|
||||
--color-primary-100: oklch(91.0% 0.050 190deg);
|
||||
--color-primary-50: oklch(96.5% 0.025 192deg);
|
||||
--color-primary-100: oklch(91% 0.05 190deg);
|
||||
--color-primary-200: oklch(84.5% 0.078 188deg);
|
||||
--color-primary-300: oklch(76.5% 0.105 186deg);
|
||||
--color-primary-400: oklch(67.5% 0.125 185deg);
|
||||
--color-primary-500: oklch(50.5% 0.130 184deg);
|
||||
--color-primary-600: oklch(44.0% 0.125 183deg);
|
||||
--color-primary-500: oklch(50.5% 0.13 184deg);
|
||||
--color-primary-600: oklch(44% 0.125 183deg);
|
||||
--color-primary-700: oklch(37.5% 0.115 182deg);
|
||||
--color-primary-800: oklch(30.5% 0.105 181deg);
|
||||
--color-primary-900: oklch(23.5% 0.090 180deg);
|
||||
--color-primary-950: oklch(16.0% 0.075 179deg);
|
||||
--color-primary-contrast-dark: var(--color-primary-950);
|
||||
--color-primary-900: oklch(23.5% 0.09 180deg);
|
||||
--color-primary-950: oklch(16% 0.075 179deg);
|
||||
--color-primary-contrast-dark: var(--color-primary-950);
|
||||
--color-primary-contrast-light: var(--color-primary-50);
|
||||
|
||||
--color-secondary-50: oklch(97.5% 0.060 102deg);
|
||||
--color-secondary-50: oklch(97.5% 0.06 102deg);
|
||||
--color-secondary-100: oklch(93.5% 0.095 100deg);
|
||||
--color-secondary-200: oklch(89.5% 0.128 98deg);
|
||||
--color-secondary-300: oklch(85.5% 0.155 95deg);
|
||||
--color-secondary-400: oklch(81.0% 0.170 93deg);
|
||||
--color-secondary-500: oklch(76.0% 0.170 90deg);
|
||||
--color-secondary-600: oklch(68.5% 0.160 87deg);
|
||||
--color-secondary-400: oklch(81% 0.17 93deg);
|
||||
--color-secondary-500: oklch(76% 0.17 90deg);
|
||||
--color-secondary-600: oklch(68.5% 0.16 87deg);
|
||||
--color-secondary-700: oklch(60.5% 0.145 85deg);
|
||||
--color-secondary-800: oklch(52.0% 0.130 83deg);
|
||||
--color-secondary-900: oklch(43.5% 0.110 81deg);
|
||||
--color-secondary-950: oklch(35.0% 0.090 79deg);
|
||||
--color-secondary-contrast-dark: var(--color-secondary-950);
|
||||
--color-secondary-800: oklch(52% 0.13 83deg);
|
||||
--color-secondary-900: oklch(43.5% 0.11 81deg);
|
||||
--color-secondary-950: oklch(35% 0.09 79deg);
|
||||
--color-secondary-contrast-dark: var(--color-secondary-950);
|
||||
--color-secondary-contrast-light: var(--color-secondary-50);
|
||||
|
||||
--color-tertiary-50: oklch(95.5% 0.042 283deg);
|
||||
--color-tertiary-100: oklch(89.0% 0.068 281deg);
|
||||
--color-tertiary-50: oklch(95.5% 0.042 283deg);
|
||||
--color-tertiary-100: oklch(89% 0.068 281deg);
|
||||
--color-tertiary-200: oklch(81.5% 0.092 279deg);
|
||||
--color-tertiary-300: oklch(73.5% 0.112 278deg);
|
||||
--color-tertiary-400: oklch(65.0% 0.132 277deg);
|
||||
--color-tertiary-400: oklch(65% 0.132 277deg);
|
||||
--color-tertiary-500: oklch(55.5% 0.142 276deg);
|
||||
--color-tertiary-600: oklch(48.5% 0.138 275deg);
|
||||
--color-tertiary-700: oklch(41.5% 0.128 274deg);
|
||||
--color-tertiary-800: oklch(34.5% 0.112 273deg);
|
||||
--color-tertiary-900: oklch(27.5% 0.098 272deg);
|
||||
--color-tertiary-950: oklch(20.0% 0.082 271deg);
|
||||
--color-tertiary-contrast-dark: var(--color-tertiary-950);
|
||||
--color-tertiary-950: oklch(20% 0.082 271deg);
|
||||
--color-tertiary-contrast-dark: var(--color-tertiary-950);
|
||||
--color-tertiary-contrast-light: var(--color-tertiary-50);
|
||||
|
||||
--color-success-50: oklch(95.77% 0.05 152.69deg);
|
||||
--color-success-100: oklch(91.59% 0.06 152.00deg);
|
||||
--color-success-50: oklch(95.77% 0.05 152.69deg);
|
||||
--color-success-100: oklch(91.59% 0.06 152deg);
|
||||
--color-success-200: oklch(87.45% 0.08 152.08deg);
|
||||
--color-success-300: oklch(83.57% 0.09 150.85deg);
|
||||
--color-success-400: oklch(79.47% 0.11 150.71deg);
|
||||
@@ -114,51 +114,51 @@ html[data-theme='AE_Firefly'] {
|
||||
--color-success-600: oklch(67.65% 0.11 149.94deg);
|
||||
--color-success-700: oklch(59.71% 0.09 150.42deg);
|
||||
--color-success-800: oklch(51.74% 0.08 150.24deg);
|
||||
--color-success-900: oklch(43.20% 0.06 151.12deg);
|
||||
--color-success-950: oklch(34.20% 0.04 151.44deg);
|
||||
--color-success-contrast-dark: var(--color-success-950);
|
||||
--color-success-900: oklch(43.2% 0.06 151.12deg);
|
||||
--color-success-950: oklch(34.2% 0.04 151.44deg);
|
||||
--color-success-contrast-dark: var(--color-success-950);
|
||||
--color-success-contrast-light: var(--color-success-50);
|
||||
|
||||
--color-warning-50: oklch(97.5% 0.065 78deg);
|
||||
--color-warning-100: oklch(93.5% 0.090 75deg);
|
||||
--color-warning-200: oklch(89.5% 0.120 73deg);
|
||||
--color-warning-50: oklch(97.5% 0.065 78deg);
|
||||
--color-warning-100: oklch(93.5% 0.09 75deg);
|
||||
--color-warning-200: oklch(89.5% 0.12 73deg);
|
||||
--color-warning-300: oklch(85.5% 0.145 70deg);
|
||||
--color-warning-400: oklch(81.5% 0.160 67deg);
|
||||
--color-warning-500: oklch(77.0% 0.165 65deg);
|
||||
--color-warning-400: oklch(81.5% 0.16 67deg);
|
||||
--color-warning-500: oklch(77% 0.165 65deg);
|
||||
--color-warning-600: oklch(69.5% 0.155 64deg);
|
||||
--color-warning-700: oklch(61.5% 0.140 63deg);
|
||||
--color-warning-700: oklch(61.5% 0.14 63deg);
|
||||
--color-warning-800: oklch(53.5% 0.125 62deg);
|
||||
--color-warning-900: oklch(45.0% 0.105 61deg);
|
||||
--color-warning-950: oklch(37.0% 0.088 60deg);
|
||||
--color-warning-contrast-dark: var(--color-warning-950);
|
||||
--color-warning-900: oklch(45% 0.105 61deg);
|
||||
--color-warning-950: oklch(37% 0.088 60deg);
|
||||
--color-warning-contrast-dark: var(--color-warning-950);
|
||||
--color-warning-contrast-light: var(--color-warning-50);
|
||||
|
||||
--color-error-50: oklch(95.0% 0.040 18deg);
|
||||
--color-error-100: oklch(88.0% 0.070 20deg);
|
||||
--color-error-200: oklch(80.0% 0.105 21deg);
|
||||
--color-error-300: oklch(72.0% 0.140 22deg);
|
||||
--color-error-400: oklch(64.5% 0.170 23deg);
|
||||
--color-error-50: oklch(95% 0.04 18deg);
|
||||
--color-error-100: oklch(88% 0.07 20deg);
|
||||
--color-error-200: oklch(80% 0.105 21deg);
|
||||
--color-error-300: oklch(72% 0.14 22deg);
|
||||
--color-error-400: oklch(64.5% 0.17 23deg);
|
||||
--color-error-500: oklch(57.5% 0.195 24deg);
|
||||
--color-error-600: oklch(51.5% 0.182 25deg);
|
||||
--color-error-700: oklch(45.5% 0.165 26deg);
|
||||
--color-error-800: oklch(39.5% 0.148 27deg);
|
||||
--color-error-900: oklch(33.0% 0.128 28deg);
|
||||
--color-error-900: oklch(33% 0.128 28deg);
|
||||
--color-error-950: oklch(26.5% 0.108 29deg);
|
||||
--color-error-contrast-dark: var(--color-error-950);
|
||||
--color-error-contrast-dark: var(--color-error-950);
|
||||
--color-error-contrast-light: var(--color-error-50);
|
||||
|
||||
--color-surface-50: oklch(99.2% 0.003 220deg);
|
||||
--color-surface-100: oklch(97.0% 0.006 217deg);
|
||||
--color-surface-50: oklch(99.2% 0.003 220deg);
|
||||
--color-surface-100: oklch(97% 0.006 217deg);
|
||||
--color-surface-200: oklch(93.5% 0.009 215deg);
|
||||
--color-surface-300: oklch(88.5% 0.012 213deg);
|
||||
--color-surface-400: oklch(81.5% 0.015 212deg);
|
||||
--color-surface-500: oklch(70.5% 0.016 215deg);
|
||||
--color-surface-600: oklch(59.0% 0.018 218deg);
|
||||
--color-surface-700: oklch(47.5% 0.020 222deg);
|
||||
--color-surface-600: oklch(59% 0.018 218deg);
|
||||
--color-surface-700: oklch(47.5% 0.02 222deg);
|
||||
--color-surface-800: oklch(30.5% 0.022 226deg);
|
||||
--color-surface-900: oklch(24.5% 0.025 229deg);
|
||||
--color-surface-950: oklch(15.5% 0.028 233deg);
|
||||
--color-surface-contrast-dark: var(--color-surface-950);
|
||||
--color-surface-contrast-dark: var(--color-surface-950);
|
||||
--color-surface-contrast-light: var(--color-surface-50);
|
||||
}
|
||||
|
||||
@@ -174,20 +174,20 @@ html.dark[data-theme='AE_Firefly'] {
|
||||
* PRIMARY — Luminescent Firefly Teal
|
||||
* ...existing code...
|
||||
*/
|
||||
--color-primary-50: oklch(96.5% 0.025 192deg);
|
||||
--color-primary-100: oklch(91.0% 0.050 190deg);
|
||||
--color-primary-50: oklch(96.5% 0.025 192deg);
|
||||
--color-primary-100: oklch(91% 0.05 190deg);
|
||||
--color-primary-200: oklch(84.5% 0.078 188deg);
|
||||
--color-primary-300: oklch(76.5% 0.105 186deg);
|
||||
--color-primary-400: oklch(67.5% 0.125 185deg);
|
||||
--color-primary-500: oklch(50.5% 0.130 184deg);
|
||||
--color-primary-600: oklch(44.0% 0.125 183deg);
|
||||
--color-primary-500: oklch(50.5% 0.13 184deg);
|
||||
--color-primary-600: oklch(44% 0.125 183deg);
|
||||
--color-primary-700: oklch(37.5% 0.115 182deg);
|
||||
--color-primary-800: oklch(30.5% 0.105 181deg);
|
||||
--color-primary-900: oklch(23.5% 0.090 180deg);
|
||||
--color-primary-950: oklch(16.0% 0.075 179deg);
|
||||
--color-primary-contrast-dark: var(--color-primary-950);
|
||||
--color-primary-900: oklch(23.5% 0.09 180deg);
|
||||
--color-primary-950: oklch(16% 0.075 179deg);
|
||||
--color-primary-contrast-dark: var(--color-primary-950);
|
||||
--color-primary-contrast-light: var(--color-primary-50);
|
||||
--color-primary-contrast-50: var(--color-primary-contrast-dark);
|
||||
--color-primary-contrast-50: var(--color-primary-contrast-dark);
|
||||
--color-primary-contrast-100: var(--color-primary-contrast-dark);
|
||||
--color-primary-contrast-200: var(--color-primary-contrast-dark);
|
||||
--color-primary-contrast-300: var(--color-primary-contrast-dark);
|
||||
@@ -200,20 +200,20 @@ html.dark[data-theme='AE_Firefly'] {
|
||||
--color-primary-contrast-950: var(--color-primary-contrast-light);
|
||||
|
||||
/* ...existing code for secondary, tertiary, success, warning, error, surface... */
|
||||
--color-secondary-50: oklch(97.5% 0.060 102deg);
|
||||
--color-secondary-50: oklch(97.5% 0.06 102deg);
|
||||
--color-secondary-100: oklch(93.5% 0.095 100deg);
|
||||
--color-secondary-200: oklch(89.5% 0.128 98deg);
|
||||
--color-secondary-300: oklch(85.5% 0.155 95deg);
|
||||
--color-secondary-400: oklch(81.0% 0.170 93deg);
|
||||
--color-secondary-500: oklch(76.0% 0.170 90deg);
|
||||
--color-secondary-600: oklch(68.5% 0.160 87deg);
|
||||
--color-secondary-400: oklch(81% 0.17 93deg);
|
||||
--color-secondary-500: oklch(76% 0.17 90deg);
|
||||
--color-secondary-600: oklch(68.5% 0.16 87deg);
|
||||
--color-secondary-700: oklch(60.5% 0.145 85deg);
|
||||
--color-secondary-800: oklch(52.0% 0.130 83deg);
|
||||
--color-secondary-900: oklch(43.5% 0.110 81deg);
|
||||
--color-secondary-950: oklch(35.0% 0.090 79deg);
|
||||
--color-secondary-contrast-dark: var(--color-secondary-950);
|
||||
--color-secondary-800: oklch(52% 0.13 83deg);
|
||||
--color-secondary-900: oklch(43.5% 0.11 81deg);
|
||||
--color-secondary-950: oklch(35% 0.09 79deg);
|
||||
--color-secondary-contrast-dark: var(--color-secondary-950);
|
||||
--color-secondary-contrast-light: var(--color-secondary-50);
|
||||
--color-secondary-contrast-50: var(--color-secondary-contrast-dark);
|
||||
--color-secondary-contrast-50: var(--color-secondary-contrast-dark);
|
||||
--color-secondary-contrast-100: var(--color-secondary-contrast-dark);
|
||||
--color-secondary-contrast-200: var(--color-secondary-contrast-dark);
|
||||
--color-secondary-contrast-300: var(--color-secondary-contrast-dark);
|
||||
@@ -225,20 +225,20 @@ html.dark[data-theme='AE_Firefly'] {
|
||||
--color-secondary-contrast-900: var(--color-secondary-contrast-light);
|
||||
--color-secondary-contrast-950: var(--color-secondary-contrast-light);
|
||||
|
||||
--color-tertiary-50: oklch(95.5% 0.042 283deg);
|
||||
--color-tertiary-100: oklch(89.0% 0.068 281deg);
|
||||
--color-tertiary-50: oklch(95.5% 0.042 283deg);
|
||||
--color-tertiary-100: oklch(89% 0.068 281deg);
|
||||
--color-tertiary-200: oklch(81.5% 0.092 279deg);
|
||||
--color-tertiary-300: oklch(73.5% 0.112 278deg);
|
||||
--color-tertiary-400: oklch(65.0% 0.132 277deg);
|
||||
--color-tertiary-400: oklch(65% 0.132 277deg);
|
||||
--color-tertiary-500: oklch(55.5% 0.142 276deg);
|
||||
--color-tertiary-600: oklch(48.5% 0.138 275deg);
|
||||
--color-tertiary-700: oklch(41.5% 0.128 274deg);
|
||||
--color-tertiary-800: oklch(34.5% 0.112 273deg);
|
||||
--color-tertiary-900: oklch(27.5% 0.098 272deg);
|
||||
--color-tertiary-950: oklch(20.0% 0.082 271deg);
|
||||
--color-tertiary-contrast-dark: var(--color-tertiary-950);
|
||||
--color-tertiary-950: oklch(20% 0.082 271deg);
|
||||
--color-tertiary-contrast-dark: var(--color-tertiary-950);
|
||||
--color-tertiary-contrast-light: var(--color-tertiary-50);
|
||||
--color-tertiary-contrast-50: var(--color-tertiary-contrast-dark);
|
||||
--color-tertiary-contrast-50: var(--color-tertiary-contrast-dark);
|
||||
--color-tertiary-contrast-100: var(--color-tertiary-contrast-dark);
|
||||
--color-tertiary-contrast-200: var(--color-tertiary-contrast-dark);
|
||||
--color-tertiary-contrast-300: var(--color-tertiary-contrast-dark);
|
||||
@@ -250,8 +250,8 @@ html.dark[data-theme='AE_Firefly'] {
|
||||
--color-tertiary-contrast-900: var(--color-tertiary-contrast-light);
|
||||
--color-tertiary-contrast-950: var(--color-tertiary-contrast-light);
|
||||
|
||||
--color-success-50: oklch(95.77% 0.05 152.69deg);
|
||||
--color-success-100: oklch(91.59% 0.06 152.00deg);
|
||||
--color-success-50: oklch(95.77% 0.05 152.69deg);
|
||||
--color-success-100: oklch(91.59% 0.06 152deg);
|
||||
--color-success-200: oklch(87.45% 0.08 152.08deg);
|
||||
--color-success-300: oklch(83.57% 0.09 150.85deg);
|
||||
--color-success-400: oklch(79.47% 0.11 150.71deg);
|
||||
@@ -259,11 +259,11 @@ html.dark[data-theme='AE_Firefly'] {
|
||||
--color-success-600: oklch(67.65% 0.11 149.94deg);
|
||||
--color-success-700: oklch(59.71% 0.09 150.42deg);
|
||||
--color-success-800: oklch(51.74% 0.08 150.24deg);
|
||||
--color-success-900: oklch(43.20% 0.06 151.12deg);
|
||||
--color-success-950: oklch(34.20% 0.04 151.44deg);
|
||||
--color-success-contrast-dark: var(--color-success-950);
|
||||
--color-success-900: oklch(43.2% 0.06 151.12deg);
|
||||
--color-success-950: oklch(34.2% 0.04 151.44deg);
|
||||
--color-success-contrast-dark: var(--color-success-950);
|
||||
--color-success-contrast-light: var(--color-success-50);
|
||||
--color-success-contrast-50: var(--color-success-contrast-dark);
|
||||
--color-success-contrast-50: var(--color-success-contrast-dark);
|
||||
--color-success-contrast-100: var(--color-success-contrast-dark);
|
||||
--color-success-contrast-200: var(--color-success-contrast-dark);
|
||||
--color-success-contrast-300: var(--color-success-contrast-dark);
|
||||
@@ -275,20 +275,20 @@ html.dark[data-theme='AE_Firefly'] {
|
||||
--color-success-contrast-900: var(--color-success-contrast-light);
|
||||
--color-success-contrast-950: var(--color-success-contrast-light);
|
||||
|
||||
--color-warning-50: oklch(97.5% 0.065 78deg);
|
||||
--color-warning-100: oklch(93.5% 0.090 75deg);
|
||||
--color-warning-200: oklch(89.5% 0.120 73deg);
|
||||
--color-warning-50: oklch(97.5% 0.065 78deg);
|
||||
--color-warning-100: oklch(93.5% 0.09 75deg);
|
||||
--color-warning-200: oklch(89.5% 0.12 73deg);
|
||||
--color-warning-300: oklch(85.5% 0.145 70deg);
|
||||
--color-warning-400: oklch(81.5% 0.160 67deg);
|
||||
--color-warning-500: oklch(77.0% 0.165 65deg);
|
||||
--color-warning-400: oklch(81.5% 0.16 67deg);
|
||||
--color-warning-500: oklch(77% 0.165 65deg);
|
||||
--color-warning-600: oklch(69.5% 0.155 64deg);
|
||||
--color-warning-700: oklch(61.5% 0.140 63deg);
|
||||
--color-warning-700: oklch(61.5% 0.14 63deg);
|
||||
--color-warning-800: oklch(53.5% 0.125 62deg);
|
||||
--color-warning-900: oklch(45.0% 0.105 61deg);
|
||||
--color-warning-950: oklch(37.0% 0.088 60deg);
|
||||
--color-warning-contrast-dark: var(--color-warning-950);
|
||||
--color-warning-900: oklch(45% 0.105 61deg);
|
||||
--color-warning-950: oklch(37% 0.088 60deg);
|
||||
--color-warning-contrast-dark: var(--color-warning-950);
|
||||
--color-warning-contrast-light: var(--color-warning-50);
|
||||
--color-warning-contrast-50: var(--color-warning-contrast-dark);
|
||||
--color-warning-contrast-50: var(--color-warning-contrast-dark);
|
||||
--color-warning-contrast-100: var(--color-warning-contrast-dark);
|
||||
--color-warning-contrast-200: var(--color-warning-contrast-dark);
|
||||
--color-warning-contrast-300: var(--color-warning-contrast-dark);
|
||||
@@ -300,20 +300,20 @@ html.dark[data-theme='AE_Firefly'] {
|
||||
--color-warning-contrast-900: var(--color-warning-contrast-light);
|
||||
--color-warning-contrast-950: var(--color-warning-contrast-light);
|
||||
|
||||
--color-error-50: oklch(95.0% 0.040 18deg);
|
||||
--color-error-100: oklch(88.0% 0.070 20deg);
|
||||
--color-error-200: oklch(80.0% 0.105 21deg);
|
||||
--color-error-300: oklch(72.0% 0.140 22deg);
|
||||
--color-error-400: oklch(64.5% 0.170 23deg);
|
||||
--color-error-50: oklch(95% 0.04 18deg);
|
||||
--color-error-100: oklch(88% 0.07 20deg);
|
||||
--color-error-200: oklch(80% 0.105 21deg);
|
||||
--color-error-300: oklch(72% 0.14 22deg);
|
||||
--color-error-400: oklch(64.5% 0.17 23deg);
|
||||
--color-error-500: oklch(57.5% 0.195 24deg);
|
||||
--color-error-600: oklch(51.5% 0.182 25deg);
|
||||
--color-error-700: oklch(45.5% 0.165 26deg);
|
||||
--color-error-800: oklch(39.5% 0.148 27deg);
|
||||
--color-error-900: oklch(33.0% 0.128 28deg);
|
||||
--color-error-900: oklch(33% 0.128 28deg);
|
||||
--color-error-950: oklch(26.5% 0.108 29deg);
|
||||
--color-error-contrast-dark: var(--color-error-950);
|
||||
--color-error-contrast-dark: var(--color-error-950);
|
||||
--color-error-contrast-light: var(--color-error-50);
|
||||
--color-error-contrast-50: var(--color-error-contrast-dark);
|
||||
--color-error-contrast-50: var(--color-error-contrast-dark);
|
||||
--color-error-contrast-100: var(--color-error-contrast-dark);
|
||||
--color-error-contrast-200: var(--color-error-contrast-dark);
|
||||
--color-error-contrast-300: var(--color-error-contrast-dark);
|
||||
@@ -325,20 +325,20 @@ html.dark[data-theme='AE_Firefly'] {
|
||||
--color-error-contrast-900: var(--color-error-contrast-light);
|
||||
--color-error-contrast-950: var(--color-error-contrast-light);
|
||||
|
||||
--color-surface-50: oklch(99.2% 0.003 220deg);
|
||||
--color-surface-100: oklch(97.0% 0.006 217deg);
|
||||
--color-surface-50: oklch(99.2% 0.003 220deg);
|
||||
--color-surface-100: oklch(97% 0.006 217deg);
|
||||
--color-surface-200: oklch(93.5% 0.009 215deg);
|
||||
--color-surface-300: oklch(88.5% 0.012 213deg);
|
||||
--color-surface-400: oklch(81.5% 0.015 212deg);
|
||||
--color-surface-500: oklch(70.5% 0.016 215deg);
|
||||
--color-surface-600: oklch(59.0% 0.018 218deg);
|
||||
--color-surface-700: oklch(47.5% 0.020 222deg);
|
||||
--color-surface-600: oklch(59% 0.018 218deg);
|
||||
--color-surface-700: oklch(47.5% 0.02 222deg);
|
||||
--color-surface-800: oklch(35.5% 0.022 226deg);
|
||||
--color-surface-900: oklch(24.5% 0.025 229deg);
|
||||
--color-surface-950: oklch(15.5% 0.028 233deg);
|
||||
--color-surface-contrast-dark: var(--color-surface-950);
|
||||
--color-surface-contrast-dark: var(--color-surface-950);
|
||||
--color-surface-contrast-light: var(--color-surface-50);
|
||||
--color-surface-contrast-50: var(--color-surface-contrast-dark);
|
||||
--color-surface-contrast-50: var(--color-surface-contrast-dark);
|
||||
--color-surface-contrast-100: var(--color-surface-contrast-dark);
|
||||
--color-surface-contrast-200: var(--color-surface-contrast-dark);
|
||||
--color-surface-contrast-300: var(--color-surface-contrast-dark);
|
||||
|
||||
89
src/app.css
89
src/app.css
@@ -10,12 +10,15 @@
|
||||
/* Sync native browser control rendering (select dropdowns, scrollbars, etc.)
|
||||
with the app's dark/light mode toggle. Without this, native controls follow
|
||||
the OS theme rather than the app's .dark/.light class on <html>. */
|
||||
html.dark { color-scheme: dark; }
|
||||
html.light { color-scheme: light; }
|
||||
html.dark {
|
||||
color-scheme: dark;
|
||||
}
|
||||
html.light {
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
@import '@skeletonlabs/skeleton';
|
||||
|
||||
|
||||
/* Register Preset Themes */
|
||||
/* @import '@skeletonlabs/skeleton/themes/{theme-name}'; */
|
||||
@import '@skeletonlabs/skeleton/themes/cerberus';
|
||||
@@ -154,13 +157,13 @@ html.light { color-scheme: light; }
|
||||
.dark .input:not([type='checkbox']):not([type='radio']):not([type='range']),
|
||||
.dark .select,
|
||||
.dark .textarea {
|
||||
color: rgb(243 244 246); /* gray-100 */
|
||||
color: rgb(243 244 246); /* gray-100 */
|
||||
background-color: rgb(55 65 81); /* gray-700 */
|
||||
border-color: rgb(75 85 99); /* gray-600 */
|
||||
border-color: rgb(75 85 99); /* gray-600 */
|
||||
}
|
||||
.dark .input::placeholder,
|
||||
.dark .textarea::placeholder {
|
||||
color: rgb(156 163 175); /* gray-400 — legible at reduced opacity */
|
||||
color: rgb(156 163 175); /* gray-400 — legible at reduced opacity */
|
||||
}
|
||||
/* Option elements in dark selects — forces browser native dark chrome */
|
||||
.dark .select option {
|
||||
@@ -198,8 +201,12 @@ body {
|
||||
/* Font size accessibility modes — cycled via the font size button in the sys menu.
|
||||
Applied as a class on <html> by the layout DOM effect.
|
||||
The 'default' mode has no class (browser default, typically 16px). */
|
||||
html.font-size-larger { font-size: 112.5%; } /* ~18px base */
|
||||
html.font-size-smaller { font-size: 87.5%; } /* ~14px base */
|
||||
html.font-size-larger {
|
||||
font-size: 112.5%;
|
||||
} /* ~18px base */
|
||||
html.font-size-smaller {
|
||||
font-size: 87.5%;
|
||||
} /* ~14px base */
|
||||
|
||||
html.super_access #appShell {
|
||||
background-color: hsla(0, 100%, 50%, 0.5);
|
||||
@@ -356,51 +363,51 @@ html.trusted_access #appShell {
|
||||
/* @apply preset-tonal-primary border border-primary-500 transition-all; */
|
||||
}
|
||||
.ae_btn_secondary {
|
||||
@apply preset-tonal-secondary border border-secondary-500 transition-all;
|
||||
@apply preset-tonal-secondary border-secondary-500 border transition-all;
|
||||
/* hover:preset-filled-secondary-500 */
|
||||
}
|
||||
.ae_btn_tertiary {
|
||||
@apply preset-tonal-tertiary border border-tertiary-500 transition-all;
|
||||
@apply preset-tonal-tertiary border-tertiary-500 border transition-all;
|
||||
}
|
||||
.ae_btn_success {
|
||||
@apply preset-tonal-success border border-success-500 transition-all;
|
||||
@apply preset-tonal-success border-success-500 border transition-all;
|
||||
}
|
||||
.ae_btn_warning {
|
||||
@apply preset-tonal-warning border border-warning-500 text-warning-950-50 transition-all;
|
||||
@apply preset-tonal-warning border-warning-500 text-warning-950-50 border transition-all;
|
||||
}
|
||||
.ae_btn_error {
|
||||
@apply preset-tonal-error border border-error-500 transition-all;
|
||||
@apply preset-tonal-error border-error-500 border transition-all;
|
||||
}
|
||||
.ae_btn_surface {
|
||||
@apply preset-tonal-surface border border-surface-500 transition-all;
|
||||
@apply preset-tonal-surface border-surface-500 border transition-all;
|
||||
}
|
||||
/* Buttons customized for Aether using Skeleton Tailwind preset classes */
|
||||
.ae_btn_info {
|
||||
@apply border text-cyan-950 dark:text-cyan-50 bg-cyan-50 dark:bg-cyan-950 border-cyan-100 dark:border-cyan-900 hover:bg-cyan-200 hover:dark:bg-cyan-800 transition-all;
|
||||
@apply border border-cyan-100 bg-cyan-50 text-cyan-950 transition-all hover:bg-cyan-200 dark:border-cyan-900 dark:bg-cyan-950 dark:text-cyan-50 hover:dark:bg-cyan-800;
|
||||
}
|
||||
|
||||
/* Buttons are for filled and outlined presets */
|
||||
.ae_btn_secondary_filled {
|
||||
@apply preset-filled-secondary-200-800 border border-secondary-500 transition-all;
|
||||
@apply preset-filled-secondary-200-800 border-secondary-500 border transition-all;
|
||||
/* hover:preset-filled-secondary-500 */
|
||||
}
|
||||
.ae_btn_secondary_outlined {
|
||||
@apply preset-outlined-secondary-200-800 hover:preset-filled-secondary-400-600 text-secondary-950-50 transition-all;
|
||||
}
|
||||
.ae_btn_success_filled {
|
||||
@apply preset-filled-success-200-800 border border-success-500 transition-all;
|
||||
@apply preset-filled-success-200-800 border-success-500 border transition-all;
|
||||
}
|
||||
.ae_btn_success_outlined {
|
||||
@apply preset-outlined-success-200-800 hover:preset-filled-success-400-600 text-success-950-50 transition-all;
|
||||
}
|
||||
.ae_btn_warning_filled {
|
||||
@apply preset-filled-warning-200-800 border border-warning-500 transition-all;
|
||||
@apply preset-filled-warning-200-800 border-warning-500 border transition-all;
|
||||
}
|
||||
.ae_btn_warning_outlined {
|
||||
@apply preset-outlined-warning-200-800 hover:preset-filled-warning-400-600 text-warning-950-50 transition-all;
|
||||
}
|
||||
.ae_btn_surface_filled {
|
||||
@apply preset-filled-surface-200-800 border border-surface-500 transition-all;
|
||||
@apply preset-filled-surface-200-800 border-surface-500 border transition-all;
|
||||
}
|
||||
.ae_btn_surface_outlined {
|
||||
@apply preset-outlined-surface-200-800 hover:preset-filled-surface-400-600 text-surface-950-50 transition-all;
|
||||
@@ -409,10 +416,10 @@ html.trusted_access #appShell {
|
||||
@apply preset-outlined-error-200-800 hover:preset-filled-error-400-600 text-error-950-50 transition-all;
|
||||
}
|
||||
.ae_btn_info_filled {
|
||||
@apply border text-cyan-950 dark:text-cyan-50 bg-cyan-200 dark:bg-cyan-800 border-cyan-200 dark:border-cyan-800 transition-all;
|
||||
@apply border border-cyan-200 bg-cyan-200 text-cyan-950 transition-all dark:border-cyan-800 dark:bg-cyan-800 dark:text-cyan-50;
|
||||
}
|
||||
.ae_btn_info_outlined {
|
||||
@apply border text-cyan-950 dark:text-cyan-50 bg-cyan-50 dark:bg-cyan-950 border-cyan-200 dark:border-cyan-800 transition-all;
|
||||
@apply border border-cyan-200 bg-cyan-50 text-cyan-950 transition-all dark:border-cyan-800 dark:bg-cyan-950 dark:text-cyan-50;
|
||||
}
|
||||
|
||||
/* Containers customized for Aether using Skeleton Tailwind preset classes */
|
||||
@@ -436,7 +443,7 @@ html.trusted_access #appShell {
|
||||
.ae_module_header {
|
||||
/* LCI request 3a5997 */
|
||||
/* bg-gray-300 */
|
||||
@apply preset-tonal-surface rounded-md flex flex-col md:flex-row flex-wrap gap-0.25 items-center justify-between w-full max-w-7xl p-1 px-2;
|
||||
@apply preset-tonal-surface flex w-full max-w-7xl flex-col flex-wrap items-center justify-between gap-0.25 rounded-md p-1 px-2 md:flex-row;
|
||||
}
|
||||
|
||||
[data-theme='AE_c_LCI'] .ae_module_header {
|
||||
@@ -453,32 +460,18 @@ html.trusted_access #appShell {
|
||||
@apply container;
|
||||
}
|
||||
.ae_container_module_menu {
|
||||
@apply w-full max-w-7xl flex flex-col items-center justify-center gap-1 p-1
|
||||
border rounded-md border-gray-200 dark:border-gray-800 hover:bg-gray-100 dark:hover:bg-gray-900 transition-all duration-700 hover:duration-300;
|
||||
@apply flex w-full max-w-7xl flex-col items-center justify-center gap-1 rounded-md border border-gray-200 p-1 transition-all duration-700 hover:bg-gray-100 hover:duration-300 dark:border-gray-800 dark:hover:bg-gray-900;
|
||||
}
|
||||
.ae_container_module_options {
|
||||
@apply text-cyan-950 dark:text-cyan-50
|
||||
bg-cyan-50 dark:bg-cyan-950 hover:bg-cyan-100 dark:hover:bg-cyan-900
|
||||
border border-cyan-200 dark:border-cyan-800 hover:border-cyan-400 dark:hover:border-cyan-600
|
||||
rounded-md
|
||||
flex flex-row flex-wrap items-center justify-around
|
||||
w-full max-w-full
|
||||
p-2
|
||||
transition-all;
|
||||
@apply flex w-full max-w-full flex-row flex-wrap items-center justify-around rounded-md border border-cyan-200 bg-cyan-50 p-2 text-cyan-950 transition-all hover:border-cyan-400 hover:bg-cyan-100 dark:border-cyan-800 dark:bg-cyan-950 dark:text-cyan-50 dark:hover:border-cyan-600 dark:hover:bg-cyan-900;
|
||||
}
|
||||
.ae_container_module_help {
|
||||
@apply text-yellow-950 dark:text-yellow-50
|
||||
bg-yellow-50 dark:bg-yellow-950 hover:bg-yellow-100 dark:hover:bg-yellow-900
|
||||
border border-yellow-200 dark:border-yellow-800 hover:border-yellow-400 dark:hover:border-yellow-600
|
||||
rounded-md
|
||||
w-lg max-w-full
|
||||
p-2
|
||||
transition-all;
|
||||
@apply w-lg max-w-full rounded-md border border-yellow-200 bg-yellow-50 p-2 text-yellow-950 transition-all hover:border-yellow-400 hover:bg-yellow-100 dark:border-yellow-800 dark:bg-yellow-950 dark:text-yellow-50 dark:hover:border-yellow-600 dark:hover:bg-yellow-900;
|
||||
/* bg-yellow-100 border border-yellow-400 p-2 rounded-md max-w-xl */
|
||||
}
|
||||
|
||||
.ae_container_actions {
|
||||
@apply container preset-tonal-success border border-success-500 rounded-md flex flex-row items-center my-2 p-2;
|
||||
@apply preset-tonal-success border-success-500 container my-2 flex flex-row items-center rounded-md border p-2;
|
||||
}
|
||||
.ae_container_results {
|
||||
@apply container;
|
||||
@@ -500,23 +493,11 @@ html.trusted_access #appShell {
|
||||
@apply container;
|
||||
}
|
||||
.ae_container_help {
|
||||
@apply text-yellow-950 dark:text-yellow-50
|
||||
bg-yellow-50 dark:bg-yellow-950 hover:bg-yellow-100 dark:hover:bg-yellow-900
|
||||
border border-yellow-200 dark:border-yellow-800 hover:border-yellow-400 dark:hover:border-yellow-600
|
||||
rounded-md
|
||||
max-w-full
|
||||
p-2
|
||||
transition-all;
|
||||
@apply max-w-full rounded-md border border-yellow-200 bg-yellow-50 p-2 text-yellow-950 transition-all hover:border-yellow-400 hover:bg-yellow-100 dark:border-yellow-800 dark:bg-yellow-950 dark:text-yellow-50 dark:hover:border-yellow-600 dark:hover:bg-yellow-900;
|
||||
/* bg-yellow-100 border border-yellow-400 p-2 rounded-md max-w-xl */
|
||||
}
|
||||
.ae_container_info {
|
||||
@apply text-cyan-950 dark:text-cyan-50
|
||||
bg-cyan-50 dark:bg-cyan-950 hover:bg-cyan-100 dark:hover:bg-cyan-900
|
||||
border border-cyan-200 dark:border-cyan-800 hover:border-cyan-400 dark:hover:border-cyan-600
|
||||
rounded-md
|
||||
max-w-full
|
||||
p-2
|
||||
transition-all;
|
||||
@apply max-w-full rounded-md border border-cyan-200 bg-cyan-50 p-2 text-cyan-950 transition-all hover:border-cyan-400 hover:bg-cyan-100 dark:border-cyan-800 dark:bg-cyan-950 dark:text-cyan-50 dark:hover:border-cyan-600 dark:hover:bg-cyan-900;
|
||||
}
|
||||
.ae_container_msg {
|
||||
@apply container;
|
||||
|
||||
2
src/app.d.ts
vendored
2
src/app.d.ts
vendored
@@ -14,7 +14,7 @@ declare global {
|
||||
namespace App {
|
||||
interface Platform {}
|
||||
}
|
||||
|
||||
|
||||
interface Window {
|
||||
native_app: any;
|
||||
}
|
||||
|
||||
@@ -12,16 +12,13 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,400;0,700;1,400;1,700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
rel="stylesheet" />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Noto+Serif:ital,wght@0,400;0,700;1,400;1,700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
rel="stylesheet" />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
rel="stylesheet" />
|
||||
|
||||
<!-- <link href="app.css" rel="stylesheet"> -->
|
||||
|
||||
|
||||
@@ -43,7 +43,9 @@ export const delete_object = async function delete_object({
|
||||
// Construct the URL with query parameters
|
||||
const url = new URL(endpoint, api_cfg['base_url']);
|
||||
if (params) {
|
||||
Object.keys(params).forEach((key) => url.searchParams.append(key, params[key]));
|
||||
Object.keys(params).forEach((key) =>
|
||||
url.searchParams.append(key, params[key])
|
||||
);
|
||||
}
|
||||
|
||||
// Clean and merge headers without mutating the original api_cfg
|
||||
@@ -70,8 +72,13 @@ export const delete_object = async function delete_object({
|
||||
}
|
||||
|
||||
// Auto-inject Authorization header if JWT is present but header is missing
|
||||
const jwt = headers_cleaned['jwt'] || headers_cleaned['JWT'] || api_cfg['jwt'];
|
||||
if (jwt && !headers_cleaned['Authorization'] && !headers_cleaned['authorization']) {
|
||||
const jwt =
|
||||
headers_cleaned['jwt'] || headers_cleaned['JWT'] || api_cfg['jwt'];
|
||||
if (
|
||||
jwt &&
|
||||
!headers_cleaned['Authorization'] &&
|
||||
!headers_cleaned['authorization']
|
||||
) {
|
||||
headers_cleaned['Authorization'] = `Bearer ${jwt}`;
|
||||
}
|
||||
|
||||
@@ -93,20 +100,26 @@ export const delete_object = async function delete_object({
|
||||
try {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => {
|
||||
console.error(`API DELETE request timed out after ${timeout}ms.`);
|
||||
console.error(
|
||||
`API DELETE request timed out after ${timeout}ms.`
|
||||
);
|
||||
controller.abort();
|
||||
}, timeout);
|
||||
|
||||
const fetchOptions: RequestInit = {
|
||||
method: 'DELETE',
|
||||
headers: headers_cleaned,
|
||||
body: Object.keys(data).length > 0 ? JSON.stringify(data) : undefined,
|
||||
body:
|
||||
Object.keys(data).length > 0
|
||||
? JSON.stringify(data)
|
||||
: undefined,
|
||||
signal: controller.signal
|
||||
};
|
||||
|
||||
const response = await fetch_method(url.toString(), fetchOptions).catch(function (
|
||||
error: any
|
||||
) {
|
||||
const response = await fetch_method(
|
||||
url.toString(),
|
||||
fetchOptions
|
||||
).catch(function (error: any) {
|
||||
console.log(
|
||||
'API DELETE Object *fetch* request was aborted or failed in an unexpected way.',
|
||||
error
|
||||
@@ -121,7 +134,9 @@ export const delete_object = async function delete_object({
|
||||
}
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`Response: status=${response.status} attempt=${attempt}`);
|
||||
console.log(
|
||||
`Response: status=${response.status} attempt=${attempt}`
|
||||
);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -129,15 +144,20 @@ export const delete_object = async function delete_object({
|
||||
console.warn('404 Not Found. Returning null.');
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
const errorBody = await response.text();
|
||||
console.error(`HTTP error! status: ${response.status}`, errorBody);
|
||||
console.error(
|
||||
`HTTP error! status: ${response.status}`,
|
||||
errorBody
|
||||
);
|
||||
|
||||
if (response.status >= 400 && response.status < 404) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new Error(`HTTP error! status: ${response.status} - ${errorBody}`);
|
||||
throw new Error(
|
||||
`HTTP error! status: ${response.status} - ${errorBody}`
|
||||
);
|
||||
}
|
||||
|
||||
const json = await response.json();
|
||||
@@ -148,7 +168,11 @@ export const delete_object = async function delete_object({
|
||||
|
||||
// Return the response data or metadata
|
||||
// Robustly handle V3 response envelopes
|
||||
return return_meta ? json : (json.data !== undefined ? json.data : json);
|
||||
return return_meta
|
||||
? json
|
||||
: json.data !== undefined
|
||||
? json.data
|
||||
: json;
|
||||
} catch (error) {
|
||||
console.error(`API DELETE error on attempt ${attempt}:`, error);
|
||||
|
||||
|
||||
@@ -44,7 +44,9 @@ export async function get_ae_obj_id_crud({
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** get_ae_obj_id_crud() *** Type: ${obj_type} ID: ${obj_id}`);
|
||||
console.log(
|
||||
`*** get_ae_obj_id_crud() *** Type: ${obj_type} ID: ${obj_id}`
|
||||
);
|
||||
}
|
||||
|
||||
// V3 Standard: Unified endpoint for all objects
|
||||
@@ -77,7 +79,10 @@ export async function get_ae_obj_id_crud({
|
||||
log_lvl: log_lvl,
|
||||
return_meta: return_meta
|
||||
}).catch(function (error: any) {
|
||||
console.error(`API GET CRUD object ID request failed for ${obj_type}/${obj_id}`, error);
|
||||
console.error(
|
||||
`API GET CRUD object ID request failed for ${obj_type}/${obj_id}`,
|
||||
error
|
||||
);
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -86,4 +91,4 @@ export async function get_ae_obj_id_crud({
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ interface GetAeObjV3Params {
|
||||
/**
|
||||
* Get a single object by ID (V3)
|
||||
*/
|
||||
export async function get_ae_obj_v3({
|
||||
export async function get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type,
|
||||
obj_id,
|
||||
@@ -27,7 +27,7 @@ export async function get_ae_obj_v3({
|
||||
const query_params: key_val = { view, ...params };
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('*** get_ae_obj_v3 ***');
|
||||
console.log('*** get_ae_obj ***');
|
||||
console.log('Endpoint:', endpoint);
|
||||
console.log('Params:', query_params);
|
||||
}
|
||||
@@ -56,7 +56,7 @@ interface GetNestedAeObjV3Params {
|
||||
/**
|
||||
* Get a single nested object by ID (V3)
|
||||
*/
|
||||
export async function get_nested_ae_obj_v3({
|
||||
export async function get_nested_ae_obj({
|
||||
api_cfg,
|
||||
parent_type,
|
||||
parent_id,
|
||||
@@ -71,7 +71,7 @@ export async function get_nested_ae_obj_v3({
|
||||
const query_params: key_val = { view, ...params };
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('*** get_nested_ae_obj_v3 ***');
|
||||
console.log('*** get_nested_ae_obj ***');
|
||||
console.log('Endpoint:', endpoint);
|
||||
console.log('Params:', query_params);
|
||||
}
|
||||
@@ -95,14 +95,17 @@ interface GetAeObjLiV3Params {
|
||||
view?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
order_by_li?: Record<string, 'ASC' | 'DESC'> | Record<string, 'ASC' | 'DESC'>[] | null;
|
||||
order_by_li?:
|
||||
| Record<string, 'ASC' | 'DESC'>
|
||||
| Record<string, 'ASC' | 'DESC'>[]
|
||||
| null;
|
||||
delay_ms?: number;
|
||||
params?: key_val;
|
||||
headers?: key_val;
|
||||
log_lvl?: number;
|
||||
}
|
||||
|
||||
export async function get_ae_obj_li_v3({
|
||||
export async function get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type,
|
||||
for_obj_type,
|
||||
@@ -137,7 +140,7 @@ export async function get_ae_obj_li_v3({
|
||||
if (delay_ms > 0) query_params['delay_ms'] = delay_ms;
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('*** get_ae_obj_li_v3 ***');
|
||||
console.log('*** get_ae_obj_li ***');
|
||||
console.log('Endpoint:', endpoint);
|
||||
console.log('Params:', query_params);
|
||||
console.log('Headers:', headers);
|
||||
@@ -162,12 +165,15 @@ interface GetNestedObjLiV3Params {
|
||||
view?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
order_by_li?: Record<string, 'ASC' | 'DESC'> | Record<string, 'ASC' | 'DESC'>[] | null;
|
||||
order_by_li?:
|
||||
| Record<string, 'ASC' | 'DESC'>
|
||||
| Record<string, 'ASC' | 'DESC'>[]
|
||||
| null;
|
||||
delay_ms?: number;
|
||||
log_lvl?: number;
|
||||
}
|
||||
|
||||
export async function get_nested_obj_li_v3({
|
||||
export async function get_nested_obj_li({
|
||||
api_cfg,
|
||||
parent_type,
|
||||
parent_id,
|
||||
@@ -195,7 +201,7 @@ export async function get_nested_obj_li_v3({
|
||||
if (delay_ms > 0) params['delay_ms'] = delay_ms;
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('*** get_nested_obj_li_v3 ***');
|
||||
console.log('*** get_nested_obj_li ***');
|
||||
console.log('Endpoint:', endpoint);
|
||||
console.log('Params:', params);
|
||||
}
|
||||
@@ -206,4 +212,4 @@ export async function get_nested_obj_li_v3({
|
||||
params,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
import { get_object } from './api_get_object';
|
||||
|
||||
// The lookup "obj_type" should broken out into a separate function. - 2024-08-07
|
||||
// Updated 2023-11-15
|
||||
export async function get_ae_obj_li_for_obj_id_crud({
|
||||
api_cfg,
|
||||
obj_type,
|
||||
for_obj_type,
|
||||
for_obj_id, // NOTE: Changed 2023-12-06 to no longer required
|
||||
use_alt_table = false,
|
||||
use_alt_base = false,
|
||||
// inc = {},
|
||||
enabled = 'enabled',
|
||||
hidden = 'not_hidden',
|
||||
order_by_li = null,
|
||||
limit = 999999,
|
||||
offset = 0,
|
||||
// key,
|
||||
// jwt = null,
|
||||
headers = {},
|
||||
params_json = null, // NOTE: This is a JSON object that needs to be safely converted to a string for the params. This is used for the API endpoint. Example: { "fulltext_search": { "default_qry_str": "Search string for default", "address_default_qry_str": "Search string for address", "contact_1_default_qry_str": "Search string for contact_1" } }
|
||||
// json_obj = null, // NOTE: This is a JSON object that needs to be safely converted to a string for the params. This is used for the search endpoint.
|
||||
params = {},
|
||||
return_meta = false,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
obj_type: string;
|
||||
for_obj_type: null | string;
|
||||
for_obj_id?: string;
|
||||
use_alt_table?: boolean;
|
||||
use_alt_base?: boolean;
|
||||
// inc?: key_val
|
||||
enabled?: 'enabled' | 'all' | 'not_enabled' | undefined;
|
||||
hidden?: 'hidden' | 'all' | 'not_hidden' | undefined;
|
||||
order_by_li?: any;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
// key: string,
|
||||
// jwt?: string,
|
||||
headers?: any;
|
||||
params_json?: any;
|
||||
// json_obj?: any,
|
||||
params?: key_val;
|
||||
return_meta?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** get_ae_obj_li_for_obj_id_crud() *** [${obj_type}]`);
|
||||
}
|
||||
|
||||
// data = {};
|
||||
// data['super_key'] = key;
|
||||
// data['jwt'] = jwt;
|
||||
// NOTE: The key and or JWT should be in the header of the DELETE, GET, PATCH, POST
|
||||
|
||||
// const endpoint = `/crud/${obj_type}/list`;
|
||||
|
||||
let endpoint = '';
|
||||
if (obj_type == 'account') {
|
||||
endpoint = `/crud/account/list`;
|
||||
} else if (obj_type == 'address') {
|
||||
endpoint = `/crud/address/list`;
|
||||
} else if (obj_type == 'archive') {
|
||||
endpoint = `/crud/archive/list`;
|
||||
} else if (obj_type == 'archive_content') {
|
||||
endpoint = `/crud/archive/content/list`;
|
||||
} else if (obj_type == 'contact') {
|
||||
endpoint = `/crud/contact/list`;
|
||||
} else if (obj_type == 'data_store') {
|
||||
endpoint = `/crud/data_store/list`;
|
||||
} else if (obj_type == 'event') {
|
||||
endpoint = `/crud/event/list`;
|
||||
} else if (obj_type == 'event_abstract') {
|
||||
endpoint = `/crud/event/abstract/list`;
|
||||
} else if (obj_type == 'event_badge') {
|
||||
endpoint = `/crud/event/badge/list`;
|
||||
} else if (obj_type == 'event_device') {
|
||||
endpoint = `/crud/event/device/list`;
|
||||
} else if (obj_type == 'event_exhibit') {
|
||||
endpoint = `/crud/event/exhibit/list`;
|
||||
} else if (obj_type == 'event_exhibit_tracking') {
|
||||
endpoint = `/crud/event/exhibit/tracking/list`;
|
||||
} else if (obj_type == 'event_file') {
|
||||
endpoint = `/crud/event/file/list`;
|
||||
} else if (obj_type == 'event_location') {
|
||||
endpoint = `/crud/event/location/list`;
|
||||
} else if (obj_type == 'event_person') {
|
||||
endpoint = `/crud/event/person/list`;
|
||||
} else if (obj_type == 'event_presentation') {
|
||||
endpoint = `/crud/event/presentation/list`;
|
||||
} else if (obj_type == 'event_presenter') {
|
||||
endpoint = `/crud/event/presenter/list`;
|
||||
} else if (obj_type == 'event_session') {
|
||||
endpoint = `/crud/event/session/list`;
|
||||
} else if (obj_type == 'event_track') {
|
||||
endpoint = `/crud/event/track/list`;
|
||||
} else if (obj_type == 'grant') {
|
||||
endpoint = `/crud/grant/list`;
|
||||
} else if (obj_type == 'hosted_file') {
|
||||
endpoint = `/crud/hosted_file/list`;
|
||||
} else if (obj_type == 'journal') {
|
||||
endpoint = `/crud/journal/list`;
|
||||
} else if (obj_type == 'journal_entry') {
|
||||
endpoint = `/crud/journal/entry/list`;
|
||||
} else if (obj_type == 'order') {
|
||||
endpoint = `/crud/order/list`;
|
||||
} else if (obj_type == 'order_line') {
|
||||
endpoint = `/crud/order/line/list`;
|
||||
} else if (obj_type == 'page') {
|
||||
endpoint = `/crud/page/list`;
|
||||
} else if (obj_type == 'person') {
|
||||
endpoint = `/crud/person/list`;
|
||||
} else if (obj_type == 'post') {
|
||||
endpoint = `/crud/post/list`;
|
||||
} else if (obj_type == 'post_comment') {
|
||||
endpoint = `/crud/post/comment/list`;
|
||||
} else if (obj_type == 'site') {
|
||||
endpoint = `/crud/site/list`;
|
||||
} else if (obj_type == 'sponsorship_cfg') {
|
||||
endpoint = `/crud/sponsorship/cfg/list`;
|
||||
} else if (obj_type == 'sponsorship') {
|
||||
endpoint = `/crud/sponsorship/list`;
|
||||
// } else if (obj_type == 'user') {
|
||||
// endpoint = `/crud/user/list`;
|
||||
} else if (obj_type == 'lu' && for_obj_type == 'country_subdivision') {
|
||||
endpoint = `/crud/lu/country_subdivision/list`;
|
||||
for_obj_type = null;
|
||||
} else if (obj_type == 'lu' && for_obj_type == 'country') {
|
||||
endpoint = `/crud/lu/country/list`;
|
||||
for_obj_type = null;
|
||||
} else if (obj_type == 'lu' && for_obj_type == 'time_zone') {
|
||||
endpoint = `/crud/lu/time_zone/list`;
|
||||
for_obj_type = null;
|
||||
} else {
|
||||
console.log(`Unknown object type: ${obj_type}`);
|
||||
return false;
|
||||
}
|
||||
if (log_lvl) {
|
||||
console.log('Endpoint:', endpoint);
|
||||
}
|
||||
|
||||
if (for_obj_type) {
|
||||
params['for_obj_type'] = for_obj_type;
|
||||
}
|
||||
if (for_obj_id) {
|
||||
params['for_obj_id'] = for_obj_id;
|
||||
}
|
||||
|
||||
params['use_alt_table'] = use_alt_table;
|
||||
params['use_alt_base'] = use_alt_base;
|
||||
|
||||
/* Need to deal with inc params here */
|
||||
|
||||
const allowed_enabled_list = ['all', 'enabled', 'not_enabled'];
|
||||
if (allowed_enabled_list.includes(enabled)) {
|
||||
params['enabled'] = enabled;
|
||||
}
|
||||
|
||||
const allowed_hidden_list = ['all', 'hidden', 'not_hidden'];
|
||||
if (allowed_hidden_list.includes(hidden)) {
|
||||
params['hidden'] = hidden;
|
||||
}
|
||||
|
||||
// NOTE: The order_by_li variable is in the "headers" because if is a the URL GET params do not handle multiple values very well. Maybe base64 encore in the future or something? Reminder that GET requests should not have a body (no JSON).
|
||||
// NOTE: The order_by_li should be a key value pair of the property/DB field to sort and how to sort (ASC or DESC)
|
||||
if (order_by_li) {
|
||||
if (log_lvl) {
|
||||
console.log('Order By:', order_by_li);
|
||||
}
|
||||
headers['order_by_li'] = order_by_li;
|
||||
}
|
||||
|
||||
if (limit >= 0) {
|
||||
params['limit'] = limit;
|
||||
}
|
||||
|
||||
if (offset >= 0) {
|
||||
params['offset'] = offset;
|
||||
}
|
||||
|
||||
if (params_json) {
|
||||
// NOTE: This is a JSON object that needs to be safely converted to a string for the params. This is used for the search endpoint.
|
||||
// Max characters for a GET request is 2083. This is a limitation of the browser (Microsoft IE and Edge).
|
||||
if (log_lvl) {
|
||||
console.log('JSON Object:', params_json);
|
||||
console.log(JSON.stringify(params_json));
|
||||
}
|
||||
// NOTE: "jp" stands for "JSON Params"
|
||||
params['jp'] = encodeURIComponent(JSON.stringify(params_json));
|
||||
if (params['jp'].length > 2083) {
|
||||
console.log(
|
||||
`The JSON object is too large to be used as a GET parameter. The overall max URL length is 2083 characters. Please use the POST endpoint instead. Length = ${params['jp'].length} [THIS DOES NOT EXIST YET]`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// if (json_obj) {
|
||||
// // NOTE: This is a JSON object that needs to be safely converted to a string for the params. This is used for the search endpoint.
|
||||
// // Max characters for a GET request is 2083. This is a limitation of the browser (Microsoft IE and Edge).
|
||||
// console.log('JSON Object:', json_obj);
|
||||
// params['json_str'] = encodeURIComponent(JSON.stringify(json_obj));
|
||||
// if (params['json_str'].length > 2083) {
|
||||
// console.log(`The JSON object is too large to be used as a GET parameter. The overall max URL length is 2083 characters. Please use the POST endpoint instead. Length = ${params['json_str'].length} [THIS DOES NOT EXIST YET]`);
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('Params:', params);
|
||||
}
|
||||
|
||||
const object_li_get_promise = await get_object({
|
||||
api_cfg: api_cfg,
|
||||
endpoint: endpoint,
|
||||
headers: headers,
|
||||
params: params,
|
||||
return_meta: return_meta,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
|
||||
if (log_lvl > 1) {
|
||||
console.log(object_li_get_promise);
|
||||
}
|
||||
|
||||
return object_li_get_promise;
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
import { get_object } from './api_get_object';
|
||||
|
||||
// Refactored 2025-11-13 to use a lookup map for endpoints.
|
||||
const objTypeToEndpointMap: Record<string, string> = {
|
||||
account: '/crud/account/list',
|
||||
address: '/crud/address/list',
|
||||
archive: '/crud/archive/list',
|
||||
archive_content: '/crud/archive/content/list',
|
||||
activity_log: '/crud/activity_log/list',
|
||||
contact: '/crud/contact/list',
|
||||
data_store: '/crud/data_store/list',
|
||||
event: '/crud/event/list',
|
||||
event_abstract: '/crud/event/abstract/list',
|
||||
event_badge: '/crud/event/badge/list',
|
||||
event_badge_template: '/crud/event/badge/template/list',
|
||||
event_device: '/crud/event/device/list',
|
||||
event_exhibit: '/crud/event/exhibit/list',
|
||||
event_exhibit_tracking: '/crud/event/exhibit/tracking/list',
|
||||
event_file: '/crud/event/file/list',
|
||||
event_location: '/crud/event/location/list',
|
||||
event_person: '/crud/event/person/list',
|
||||
event_presentation: '/crud/event/presentation/list',
|
||||
event_presenter: '/crud/event/presenter/list',
|
||||
event_session: '/crud/event/session/list',
|
||||
event_track: '/crud/event/track/list',
|
||||
grant: '/crud/grant/list',
|
||||
hosted_file: '/crud/hosted_file/list',
|
||||
journal: '/crud/journal/list',
|
||||
journal_entry: '/crud/journal/entry/list',
|
||||
order: '/crud/order/list',
|
||||
order_line: '/crud/order/line/list',
|
||||
page: '/crud/page/list',
|
||||
person: '/crud/person/list',
|
||||
post: '/crud/post/list',
|
||||
post_comment: '/crud/post/comment/list',
|
||||
site: '/crud/site/list',
|
||||
sponsorship_cfg: '/crud/sponsorship/cfg/list',
|
||||
sponsorship: '/crud/sponsorship/list',
|
||||
// user: '/crud/user/list',
|
||||
'lu-country_subdivision': '/crud/lu/country_subdivision/list',
|
||||
'lu-country': '/crud/lu/country/list',
|
||||
'lu-time_zone': '/crud/lu/time_zone/list'
|
||||
};
|
||||
|
||||
function getEndpointForObjType(obj_type: string, for_obj_type?: string): string {
|
||||
if (obj_type === 'lu' && for_obj_type) {
|
||||
const key = `lu-${for_obj_type}`;
|
||||
const endpoint = objTypeToEndpointMap[key];
|
||||
if (endpoint) return endpoint;
|
||||
}
|
||||
|
||||
const endpoint = objTypeToEndpointMap[obj_type];
|
||||
if (endpoint) return endpoint;
|
||||
|
||||
throw new Error(`Unknown object type: ${obj_type}`);
|
||||
}
|
||||
|
||||
type OrderBy = { [key: string]: 'ASC' | 'DESC' };
|
||||
|
||||
interface GetAeObjLiForObjIdCrudV2Params {
|
||||
api_cfg: any; // Consider defining a specific type for api_cfg
|
||||
obj_type: string;
|
||||
for_obj_type: string;
|
||||
for_obj_id?: string;
|
||||
use_alt_tbl?: boolean | string;
|
||||
use_alt_mdl?: boolean | string;
|
||||
use_alt_exp?: boolean | string;
|
||||
inc?: key_val;
|
||||
enabled?: 'all' | 'enabled' | 'not_enabled';
|
||||
hidden?: 'all' | 'hidden' | 'not_hidden';
|
||||
order_by_li?: OrderBy | OrderBy[] | null;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
headers?: Record<string, string>;
|
||||
params_json?: any;
|
||||
params?: key_val;
|
||||
log_lvl?: number;
|
||||
}
|
||||
|
||||
export async function get_ae_obj_li_for_obj_id_crud_v2({
|
||||
api_cfg,
|
||||
obj_type,
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
use_alt_tbl = false,
|
||||
use_alt_mdl = false,
|
||||
use_alt_exp = false,
|
||||
enabled = 'enabled',
|
||||
hidden = 'not_hidden',
|
||||
order_by_li = null,
|
||||
limit = 999999,
|
||||
offset = 0,
|
||||
headers = {},
|
||||
params_json = null,
|
||||
params = {},
|
||||
log_lvl = 0
|
||||
}: GetAeObjLiForObjIdCrudV2Params) {
|
||||
if (log_lvl) {
|
||||
console.log('*** get_ae_obj_li_for_obj_id_crud_v2() ***');
|
||||
}
|
||||
|
||||
try {
|
||||
const endpoint = `/v2${getEndpointForObjType(obj_type, for_obj_type)}`;
|
||||
if (log_lvl) {
|
||||
console.log('Endpoint:', endpoint);
|
||||
}
|
||||
|
||||
// We need to remove a few parameters from the params object that are not allowed.
|
||||
delete params['qry__enabled'];
|
||||
delete params['qry__hidden'];
|
||||
delete params['qry__limit'];
|
||||
delete params['qry__offset'];
|
||||
|
||||
if (for_obj_type) params['for_obj_type'] = for_obj_type;
|
||||
if (for_obj_id) params['for_obj_id'] = for_obj_id;
|
||||
|
||||
if (use_alt_tbl === true) params['tbl_alt'] = 'alt';
|
||||
if (use_alt_mdl === true) params['mdl_alt'] = 'alt';
|
||||
if (use_alt_exp === true) params['exp_alt'] = 'alt';
|
||||
|
||||
const allowed_enabled_list = ['all', 'enabled', 'not_enabled'];
|
||||
if (allowed_enabled_list.includes(enabled)) {
|
||||
params['enabled'] = enabled;
|
||||
}
|
||||
|
||||
const allowed_hidden_list = ['all', 'hidden', 'not_hidden'];
|
||||
if (allowed_hidden_list.includes(hidden)) {
|
||||
params['hidden'] = hidden;
|
||||
}
|
||||
|
||||
// NOTE: The order_by_li variable is in the "headers" because URL GET params do not handle complex objects very well.
|
||||
if (order_by_li) {
|
||||
headers['order_by_li'] = JSON.stringify(order_by_li);
|
||||
}
|
||||
|
||||
if (limit > 0) params['limit'] = limit;
|
||||
if (offset > 0) params['offset'] = offset;
|
||||
|
||||
if (params_json) {
|
||||
// NOTE: "jp" stands for "JSON Params". This is a JSON object that needs to be safely converted to a string for the params.
|
||||
// Max characters for a GET request is ~2000. This is a limitation of the browser.
|
||||
const json_params_str = encodeURIComponent(JSON.stringify(params_json));
|
||||
if (json_params_str.length > 2083) {
|
||||
// Using console.error instead of throwing an error to avoid crashing the app for a known limitation.
|
||||
console.error(
|
||||
`The JSON object is too large to be used as a GET parameter. Max length is 2083 characters. Length = ${json_params_str.length}`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
params['jp'] = json_params_str;
|
||||
}
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('Params:', params);
|
||||
}
|
||||
|
||||
const object_li_get_promise = await get_object({
|
||||
api_cfg: api_cfg,
|
||||
endpoint: endpoint,
|
||||
headers: headers,
|
||||
params: params,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
|
||||
if (log_lvl > 1) {
|
||||
console.log(object_li_get_promise);
|
||||
}
|
||||
|
||||
return object_li_get_promise;
|
||||
} catch (error) {
|
||||
console.error('Error in get_ae_obj_li_for_obj_id_crud_v2:', error);
|
||||
return false; // Or handle the error as appropriate
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ interface GetDataStoreV3Params {
|
||||
* Uses hierarchical fallback logic (Specific -> Account -> Global)
|
||||
* Path: GET /v3/data_store/code/{code}
|
||||
*/
|
||||
export async function get_data_store_v3({
|
||||
export async function get_data_store({
|
||||
api_cfg,
|
||||
code,
|
||||
for_type = null,
|
||||
@@ -24,7 +24,9 @@ export async function get_data_store_v3({
|
||||
log_lvl = 0
|
||||
}: GetDataStoreV3Params): Promise<any> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** get_data_store_v3() *** code=${code} no_account_id=${no_account_id}`);
|
||||
console.log(
|
||||
`*** get_data_store() *** code=${code} no_account_id=${no_account_id}`
|
||||
);
|
||||
}
|
||||
|
||||
const endpoint = `/v3/data_store/code/${code}`;
|
||||
@@ -5,10 +5,10 @@ import type { key_val } from '$lib/stores/ae_stores';
|
||||
* Get a list of lookup objects (V3)
|
||||
* Standardized lookup data like countries, timezones, and subdivisions.
|
||||
* Updated 2026-02-20
|
||||
*
|
||||
*
|
||||
* Endpoint: GET /v3/lookup/{lu_type}/list
|
||||
*/
|
||||
export async function get_ae_lookup_li_v3({
|
||||
export async function get_ae_lookup_li({
|
||||
api_cfg,
|
||||
lu_type,
|
||||
site_id,
|
||||
@@ -16,6 +16,9 @@ export async function get_ae_lookup_li_v3({
|
||||
for_id,
|
||||
include_disabled = false,
|
||||
only_priority = false,
|
||||
order_by_li = null,
|
||||
limit = null,
|
||||
offset = null,
|
||||
params = {},
|
||||
headers = {},
|
||||
log_lvl = 0
|
||||
@@ -27,28 +30,34 @@ export async function get_ae_lookup_li_v3({
|
||||
for_id?: string;
|
||||
include_disabled?: boolean;
|
||||
only_priority?: boolean;
|
||||
order_by_li?: Record<string, 'ASC' | 'DESC'> | null;
|
||||
limit?: number | null;
|
||||
offset?: number | null;
|
||||
params?: key_val;
|
||||
headers?: Record<string, string>;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** get_ae_lookup_li_v3() *** lu_type=${lu_type}`);
|
||||
console.log(`*** get_ae_lookup_li() *** lu_type=${lu_type}`);
|
||||
}
|
||||
|
||||
const endpoint = `/v3/lookup/${lu_type}/list`;
|
||||
|
||||
|
||||
// Build query params
|
||||
if (site_id) params['site_id'] = site_id;
|
||||
if (for_type) params['for_type'] = for_type;
|
||||
if (for_id) params['for_id'] = for_id;
|
||||
if (include_disabled) params['include_disabled'] = true;
|
||||
if (only_priority) params['only_priority'] = true;
|
||||
if (order_by_li) params['order_by_li'] = JSON.stringify(order_by_li);
|
||||
if (limit != null) params['limit'] = limit;
|
||||
if (offset != null) params['offset'] = offset;
|
||||
|
||||
// Lookup data is often global; ensure account context is handled if needed,
|
||||
// Lookup data is often global; ensure account context is handled if needed,
|
||||
// but GUIDE says it uses site Whitelist Policy.
|
||||
// If no account_id is present in api_cfg, we might need 'x-no-account-id'
|
||||
// If no account_id is present in api_cfg, we might need 'x-no-account-id'
|
||||
// for some lookups if they are public.
|
||||
|
||||
|
||||
return await get_object({
|
||||
api_cfg,
|
||||
endpoint,
|
||||
@@ -41,7 +41,9 @@ export const get_object = async function get_object({
|
||||
retry_count?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** get_object() *** Endpoint: ${endpoint} AE Task ID: ${task_id}`);
|
||||
console.log(
|
||||
`*** get_object() *** Endpoint: ${endpoint} AE Task ID: ${task_id}`
|
||||
);
|
||||
console.log('Params:', params);
|
||||
if (log_lvl > 1) {
|
||||
console.log('Data:', data);
|
||||
@@ -55,7 +57,10 @@ export const get_object = async function get_object({
|
||||
|
||||
// FAIL FAST: Check if we are explicitly offline to avoid long browser timeouts
|
||||
if (typeof navigator !== 'undefined' && !navigator.onLine) {
|
||||
if (log_lvl) console.log('get_object: Browser is offline. Failing fast to allow cache fallback.');
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
'get_object: Browser is offline. Failing fast to allow cache fallback.'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -64,7 +69,9 @@ export const get_object = async function get_object({
|
||||
}
|
||||
|
||||
const url = new URL(endpoint, api_cfg['base_url']);
|
||||
Object.keys(params).forEach((key) => url.searchParams.append(key, params[key]));
|
||||
Object.keys(params).forEach((key) =>
|
||||
url.searchParams.append(key, params[key])
|
||||
);
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
||||
@@ -96,14 +103,19 @@ export const get_object = async function get_object({
|
||||
}
|
||||
|
||||
// Handle "Bootstrap Paradox" for unauthenticated requests
|
||||
const bypass_val = merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
|
||||
const is_valid_bypass = bypass_val === 'bypass' ||
|
||||
bypass_val === 'Nothing to See Here' ||
|
||||
params['key'] ||
|
||||
bypass_val === 'direct-download';
|
||||
const bypass_val =
|
||||
merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
|
||||
const is_valid_bypass =
|
||||
bypass_val === 'bypass' ||
|
||||
bypass_val === 'Nothing to See Here' ||
|
||||
params['key'] ||
|
||||
bypass_val === 'direct-download';
|
||||
|
||||
if (is_valid_bypass) {
|
||||
if (log_lvl > 1) console.log('api_get_object: Valid bypass detected. Stripping account ID context.');
|
||||
if (log_lvl > 1)
|
||||
console.log(
|
||||
'api_get_object: Valid bypass detected. Stripping account ID context.'
|
||||
);
|
||||
delete merged_headers['x-account-id'];
|
||||
delete merged_headers['x_account_id'];
|
||||
} else {
|
||||
@@ -126,11 +138,12 @@ export const get_object = async function get_object({
|
||||
}
|
||||
|
||||
// Auto-inject Authorization header if JWT is present but header is missing
|
||||
let jwt = headers_cleaned['jwt'] ||
|
||||
headers_cleaned['JWT'] ||
|
||||
api_cfg['jwt'] ||
|
||||
api_cfg['headers']?.['jwt'] ||
|
||||
api_cfg['headers']?.['JWT'];
|
||||
let jwt =
|
||||
headers_cleaned['jwt'] ||
|
||||
headers_cleaned['JWT'] ||
|
||||
api_cfg['jwt'] ||
|
||||
api_cfg['headers']?.['jwt'] ||
|
||||
api_cfg['headers']?.['JWT'];
|
||||
|
||||
// Final Fallback: Direct check of primary ae_loc key
|
||||
if (!jwt && typeof localStorage !== 'undefined') {
|
||||
@@ -145,7 +158,11 @@ export const get_object = async function get_object({
|
||||
}
|
||||
}
|
||||
|
||||
if (jwt && !headers_cleaned['Authorization'] && !headers_cleaned['authorization']) {
|
||||
if (
|
||||
jwt &&
|
||||
!headers_cleaned['Authorization'] &&
|
||||
!headers_cleaned['authorization']
|
||||
) {
|
||||
headers_cleaned['Authorization'] = `Bearer ${jwt}`;
|
||||
}
|
||||
|
||||
@@ -174,18 +191,29 @@ export const get_object = async function get_object({
|
||||
for (let attempt = 1; attempt <= retry_count; attempt++) {
|
||||
// FAIL FAST: Check if we are explicitly offline to avoid long browser timeouts
|
||||
if (typeof navigator !== 'undefined' && !navigator.onLine) {
|
||||
if (log_lvl) console.log(`get_object: Browser is offline (attempt ${attempt}). Failing fast to allow cache fallback.`);
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`get_object: Browser is offline (attempt ${attempt}). Failing fast to allow cache fallback.`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch_method(url.toString(), fetchOptions).catch(function (
|
||||
error: any
|
||||
) {
|
||||
const response = await fetch_method(
|
||||
url.toString(),
|
||||
fetchOptions
|
||||
).catch(function (error: any) {
|
||||
// SILENCE NOISE: Aborted requests (common in SWR/Background loads) shouldn't spam logs
|
||||
if (error.name === 'AbortError' || error.message?.includes('aborted') || error.name === 'TypeError') {
|
||||
if (
|
||||
error.name === 'AbortError' ||
|
||||
error.message?.includes('aborted') ||
|
||||
error.name === 'TypeError'
|
||||
) {
|
||||
if (log_lvl > 1) {
|
||||
console.log('API GET: Request was aborted or terminated by browser. This is expected during navigation.', error);
|
||||
console.log(
|
||||
'API GET: Request was aborted or terminated by browser. This is expected during navigation.',
|
||||
error
|
||||
);
|
||||
}
|
||||
return error; // Return error to be handled below
|
||||
}
|
||||
@@ -199,11 +227,19 @@ export const get_object = async function get_object({
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
// Check if we should stop due to abort or network failure
|
||||
if (response instanceof Error || (response && (response.name === 'TypeError' || response.name === 'AbortError'))) {
|
||||
if (
|
||||
response instanceof Error ||
|
||||
(response &&
|
||||
(response.name === 'TypeError' ||
|
||||
response.name === 'AbortError'))
|
||||
) {
|
||||
// If it was an explicit abort, definitely stop
|
||||
if (response.name === 'AbortError') return false;
|
||||
|
||||
if (log_lvl > 1) console.log('API GET Object: Detected NetworkError or TypeError. Failing fast.');
|
||||
if (log_lvl > 1)
|
||||
console.log(
|
||||
'API GET Object: Detected NetworkError or TypeError. Failing fast.'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -231,24 +267,45 @@ export const get_object = async function get_object({
|
||||
if (!response.ok) {
|
||||
if (response.status === 404) {
|
||||
if (log_lvl) {
|
||||
console.log('The response was a 404 not found "error". Returning null.');
|
||||
console.log(
|
||||
'The response was a 404 not found "error". Returning null.'
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// FAIL FAST (Section 2D): Do not retry on Auth or Client errors (400, 401, 403, 422)
|
||||
if (response.status === 400 || response.status === 401 || response.status === 403 || response.status === 422) {
|
||||
if (log_lvl) console.error(`API Client Failure (${response.status}). Failing fast.`);
|
||||
if (
|
||||
response.status === 400 ||
|
||||
response.status === 401 ||
|
||||
response.status === 403 ||
|
||||
response.status === 422
|
||||
) {
|
||||
if (log_lvl)
|
||||
console.error(
|
||||
`API Client Failure (${response.status}). Failing fast.`
|
||||
);
|
||||
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
console.warn(`AUTH DIAGNOSTICS: Headers sent for ${endpoint}:`, {
|
||||
has_auth: !!headers_cleaned['Authorization'],
|
||||
has_api_key: !!headers_cleaned['x-aether-api-key'],
|
||||
has_account_id: !!headers_cleaned['x-account-id'],
|
||||
jwt_preview: jwt ? `${jwt.slice(0, 8)}...` : 'MISSING'
|
||||
});
|
||||
console.warn(
|
||||
`AUTH DIAGNOSTICS: Headers sent for ${endpoint}:`,
|
||||
{
|
||||
has_auth: !!headers_cleaned['Authorization'],
|
||||
has_api_key:
|
||||
!!headers_cleaned['x-aether-api-key'],
|
||||
has_account_id:
|
||||
!!headers_cleaned['x-account-id'],
|
||||
jwt_preview: jwt
|
||||
? `${jwt.slice(0, 8)}...`
|
||||
: 'MISSING'
|
||||
}
|
||||
);
|
||||
// Signal the root layout to show the session-expired banner.
|
||||
if (browser) ae_auth_error.set({ type: 'expired', ts: Date.now() });
|
||||
if (browser)
|
||||
ae_auth_error.set({
|
||||
type: 'expired',
|
||||
ts: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
// Structured Error Handling (V3): Attempt to get rich error metadata
|
||||
@@ -259,7 +316,11 @@ export const get_object = async function get_object({
|
||||
// Not JSON
|
||||
}
|
||||
|
||||
if (log_lvl) console.log('The response was not ok. Structured Error Check:', error_json);
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
'The response was not ok. Structured Error Check:',
|
||||
error_json
|
||||
);
|
||||
|
||||
if (error_json?.meta?.details) {
|
||||
return error_json;
|
||||
@@ -273,7 +334,10 @@ export const get_object = async function get_object({
|
||||
status_code: response.status,
|
||||
details: {
|
||||
category: 'validation',
|
||||
message: typeof error_json.detail === 'string' ? error_json.detail : JSON.stringify(error_json.detail),
|
||||
message:
|
||||
typeof error_json.detail === 'string'
|
||||
? error_json.detail
|
||||
: JSON.stringify(error_json.detail),
|
||||
raw: error_json.detail
|
||||
}
|
||||
}
|
||||
@@ -307,7 +371,9 @@ export const get_object = async function get_object({
|
||||
chunks.push(value);
|
||||
receivedLength += value.length;
|
||||
|
||||
const percent_completed = Math.round((receivedLength * 100) / contentLength);
|
||||
const percent_completed = Math.round(
|
||||
(receivedLength * 100) / contentLength
|
||||
);
|
||||
if (log_lvl > 1) {
|
||||
console.log(
|
||||
'GET Blob Progress:',
|
||||
@@ -359,7 +425,10 @@ export const get_object = async function get_object({
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`API GET object request *fetch* error on attempt ${attempt}:`, error);
|
||||
console.log(
|
||||
`API GET object request *fetch* error on attempt ${attempt}:`,
|
||||
error
|
||||
);
|
||||
|
||||
if (attempt === retry_count) {
|
||||
console.log('Max retry attempts reached. Returning false.');
|
||||
|
||||
@@ -1,524 +0,0 @@
|
||||
import axios from 'axios';
|
||||
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
|
||||
export let temp_get_blob_percent_completed = 0;
|
||||
// export let get_blob_percent_completed = readable(temp_get_blob_percent_completed);
|
||||
export const get_blob_percent_completed = temp_get_blob_percent_completed;
|
||||
|
||||
export let temp_get_object_percent_completed = 0;
|
||||
// export let get_object_percent_completed = readable(temp_get_object_percent_completed);
|
||||
export const get_object_percent_completed = temp_get_object_percent_completed;
|
||||
|
||||
// Updated 2024-05-23
|
||||
export const get_object = async function get_object({
|
||||
api_cfg = null,
|
||||
endpoint = '',
|
||||
headers = {},
|
||||
params = {},
|
||||
data = {},
|
||||
timeout = 60000,
|
||||
return_meta = false,
|
||||
return_blob = false,
|
||||
filename = '',
|
||||
auto_download = false,
|
||||
as_list = false,
|
||||
// The task_id value should be a random string that is unique to the task. This is used to identify the task in the message event.
|
||||
task_id = crypto.randomUUID(),
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
endpoint: string;
|
||||
headers?: any;
|
||||
params?: any;
|
||||
data?: any;
|
||||
timeout?: number;
|
||||
return_meta?: boolean;
|
||||
return_blob?: boolean;
|
||||
filename?: null | string;
|
||||
auto_download?: boolean;
|
||||
as_list?: boolean;
|
||||
task_id?: string;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** get_object() *** Endpoint: ${endpoint} AE Task ID: ${task_id}`);
|
||||
console.log('Params:', params);
|
||||
if (log_lvl > 1) {
|
||||
console.log('Data:', data);
|
||||
console.log(`Base URL: ${api_cfg['base_url']}; Timeout: ${timeout}`);
|
||||
console.log('API Config:', api_cfg);
|
||||
}
|
||||
if (log_lvl > 2) {
|
||||
console.log(
|
||||
`Return Meta: ${return_meta}; Return Blob: ${return_blob}; Filename: ${filename}; Auto Download: ${auto_download}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!api_cfg) {
|
||||
console.log('No API Config was provided. Returning false.');
|
||||
return false;
|
||||
}
|
||||
|
||||
const axios_api = axios.create({
|
||||
baseURL: api_cfg['base_url'],
|
||||
timeout: timeout // in milliseconds; 60000 = 60 seconds
|
||||
/* other custom settings */
|
||||
});
|
||||
axios_api.defaults.headers = api_cfg['headers'];
|
||||
if (log_lvl) {
|
||||
console.log('axios_api.defaults.headers:', axios_api.defaults.headers);
|
||||
console.log('Additional headers:', headers);
|
||||
}
|
||||
|
||||
// console.log('Clean the headers. No _underscores_!')
|
||||
const headers_cleaned: key_val = {};
|
||||
for (const prop in headers) {
|
||||
// No underscores allowed in the header parameters!
|
||||
const prop_cleaned = prop.replaceAll('_', '-');
|
||||
|
||||
// The value must be a string for the header!
|
||||
if (typeof headers[prop] != 'string') {
|
||||
headers[prop] = JSON.stringify(headers[prop]);
|
||||
}
|
||||
|
||||
headers_cleaned[prop_cleaned] = headers[prop];
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`${prop_cleaned}: ${headers_cleaned[prop_cleaned]}`);
|
||||
}
|
||||
}
|
||||
headers = headers_cleaned;
|
||||
if (log_lvl) {
|
||||
console.log('All headers cleaned:', headers);
|
||||
}
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('URL params:');
|
||||
}
|
||||
for (const prop in params) {
|
||||
if (log_lvl > 1) {
|
||||
console.log(`URL param: ${prop}: ${params[prop]}`);
|
||||
}
|
||||
if (params[prop] === null) {
|
||||
params[prop] = 'null';
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the case where there is no Blob expected to be returned. Mainly JSON and text data.
|
||||
if (!return_blob) {
|
||||
const response_data_promise = await axios_api
|
||||
.get(endpoint, {
|
||||
headers: headers,
|
||||
params: params,
|
||||
onDownloadProgress: (progressEvent) => {
|
||||
const total = progressEvent.total ?? 0;
|
||||
const percent_completed = total > 0 ? Math.round((progressEvent.loaded * 100) / total) : 0;
|
||||
if (log_lvl > 1) {
|
||||
console.log(
|
||||
'GET Data Progress:',
|
||||
progressEvent.progress,
|
||||
'Total:',
|
||||
total,
|
||||
'Loaded:',
|
||||
progressEvent.loaded,
|
||||
'Percent Completed',
|
||||
percent_completed
|
||||
);
|
||||
}
|
||||
|
||||
temp_get_object_percent_completed = percent_completed;
|
||||
|
||||
// WARNING: This needs to be tied to an object type and ID. This is a temporary solution.
|
||||
try {
|
||||
// Check if window is defined. This is to prevent errors in SvelteKit.
|
||||
if (typeof window !== 'undefined') {
|
||||
window.postMessage(
|
||||
{
|
||||
type: 'api_download_data',
|
||||
status: 'downloading',
|
||||
task_id: task_id,
|
||||
endpoint: endpoint,
|
||||
filename: filename,
|
||||
size_total: total,
|
||||
size_loaded: progressEvent.loaded,
|
||||
percent_completed: percent_completed
|
||||
},
|
||||
'*'
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error posting message to window:', error);
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`GET Response: status=${response.status} statusText=${response.statusText} baseURL=${response.config.baseURL} url=${response.config.url} method=${response.config.method} headers=${response.config.headers} params=${JSON.stringify(response.config.params)}`
|
||||
);
|
||||
}
|
||||
if (log_lvl > 1) {
|
||||
console.log('GET Response:', response);
|
||||
}
|
||||
|
||||
// Post file download message
|
||||
try {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.postMessage(
|
||||
{
|
||||
type: 'api_download_data',
|
||||
status: 'complete',
|
||||
task_id: task_id,
|
||||
endpoint: endpoint,
|
||||
filename: filename,
|
||||
size_total: 0,
|
||||
size_loaded: 0,
|
||||
percent_completed: 100
|
||||
},
|
||||
'*'
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error posting message to window:', error);
|
||||
}
|
||||
|
||||
if (!Array.isArray(response.data['data']) && as_list) {
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
'Data result is a dictionary/object, not an array/list. Forcing return as an array/list'
|
||||
);
|
||||
}
|
||||
const return_data = [];
|
||||
return_data.push(response.data['data']);
|
||||
return return_data;
|
||||
} else if (response.data['data']) {
|
||||
const return_data = response.data['data'];
|
||||
if (log_lvl) {
|
||||
if (Array.isArray(return_data)) {
|
||||
console.log(
|
||||
`Data result is an array/list. Array length: ${return_data.length}`
|
||||
);
|
||||
} else {
|
||||
console.log(`Data result is a dictionary/object, not an array/list.`);
|
||||
}
|
||||
}
|
||||
return return_data;
|
||||
} else {
|
||||
const return_data = response.data;
|
||||
if (log_lvl) {
|
||||
if (Array.isArray(return_data)) {
|
||||
console.log(
|
||||
`Not a standard response from Aether's API. Data result is an array/list. Array length: ${return_data.length}`
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
`Not a standard response from Aether's API. Data result is a dictionary/object, not an array/list.`
|
||||
);
|
||||
}
|
||||
}
|
||||
return return_data;
|
||||
}
|
||||
})
|
||||
.catch(function (error: any) {
|
||||
// Handle the common and expected 404 "error" first
|
||||
if (error.response && error.response.status === 404) {
|
||||
if (log_lvl) {
|
||||
console.log('The response was a 404 not found "error". Returning null.');
|
||||
}
|
||||
if (log_lvl > 1) {
|
||||
console.log(error.response);
|
||||
}
|
||||
if (log_lvl > 2) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
// Post file download message
|
||||
try {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.postMessage(
|
||||
{
|
||||
type: 'api_download_data',
|
||||
status: 'complete',
|
||||
task_id: task_id,
|
||||
endpoint: endpoint,
|
||||
filename: filename,
|
||||
size_total: 0,
|
||||
size_loaded: 0,
|
||||
percent_completed: 0
|
||||
},
|
||||
'*'
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error posting message to window:', error);
|
||||
}
|
||||
return null; // Returning null since there were no results
|
||||
}
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`Base URL: ${api_cfg['base_url']} | Endpoint: ${endpoint}`);
|
||||
console.log('Error Message:', error.message); // Is this needed here or below in the in the else portion???
|
||||
|
||||
if (error.response) {
|
||||
// The request was made and the server responded with a status code that falls out of the range of 2xx
|
||||
console.log('Error Response Data', error.response.data);
|
||||
console.log('Error Response Status', error.response.status);
|
||||
console.log('Error Response Headers', error.response.headers);
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received `error.request` is an instance of XMLHttpRequest in the browser and an instance of http.ClientRequest in node.js
|
||||
if (log_lvl > 1) {
|
||||
console.log('Error Request', error.request);
|
||||
}
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
console.log('Error Message', error.message);
|
||||
}
|
||||
}
|
||||
if (log_lvl > 2) {
|
||||
console.log('Error:', error);
|
||||
console.log(error.config);
|
||||
}
|
||||
|
||||
if (error.code === 'ECONNABORTED') {
|
||||
// Timeout Error (You can implement retry here where suitable)
|
||||
console.log('Timeout Error: ', error.message);
|
||||
}
|
||||
if (log_lvl) {
|
||||
console.log('The response was an error. Returning false.');
|
||||
}
|
||||
|
||||
return false; // Returning false since something may have gone wrong. This includes timeouts. Also more in line with what the API returns.
|
||||
// return error;
|
||||
});
|
||||
|
||||
if (log_lvl > 1) {
|
||||
// console.log(`Response Data: ${response_data_promise}`);
|
||||
console.log(`Response Data:`, response_data_promise);
|
||||
// console.log(response_data_promise);
|
||||
}
|
||||
if (response_data_promise) {
|
||||
// The most common and expected response.
|
||||
// console.log('Returning result. This is generally expected.');
|
||||
return response_data_promise;
|
||||
} else if (response_data_promise === null) {
|
||||
// Less common, but expected response if no results were returned.
|
||||
if (log_lvl) {
|
||||
console.log('Returning null. This is expected if no results were found. (404)');
|
||||
}
|
||||
return response_data_promise;
|
||||
} else if (response_data_promise === false) {
|
||||
// Not common, but expected response if the request to the API had an issue.
|
||||
console.log('Returning false. There may have been an issue with this request.');
|
||||
return response_data_promise;
|
||||
} else {
|
||||
// This generally should not happen. It likely means the query was bad or an API issue.
|
||||
console.log('Returning (JSON/text) unknown. This should not happen in most cases.');
|
||||
Promise.reject(new Error('fail')).then(resolved, rejected);
|
||||
}
|
||||
|
||||
// Handle the case where a Blob is expected to be returned.
|
||||
} else {
|
||||
// console.log('Expecting a Blob to be returned...');
|
||||
|
||||
const response_data_promise = await axios_api
|
||||
.get(endpoint, {
|
||||
params: params,
|
||||
responseType: 'blob',
|
||||
onDownloadProgress: (progressEvent) => {
|
||||
const total = progressEvent.total ?? 0;
|
||||
const percent_completed = total > 0 ? Math.round((progressEvent.loaded * 100) / total) : 0;
|
||||
console.log(
|
||||
'GET Blob Progress:',
|
||||
progressEvent.progress,
|
||||
'Total:',
|
||||
total,
|
||||
'Loaded:',
|
||||
progressEvent.loaded,
|
||||
'Percent Completed',
|
||||
percent_completed
|
||||
);
|
||||
|
||||
temp_get_blob_percent_completed = percent_completed;
|
||||
|
||||
// WARNING: This needs to be tied to an object type and ID. This is a temporary solution.
|
||||
try {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.postMessage(
|
||||
{
|
||||
type: 'api_download_blob',
|
||||
status: 'downloading',
|
||||
task_id: task_id,
|
||||
endpoint: endpoint,
|
||||
filename: filename,
|
||||
size_total: total,
|
||||
size_loaded: progressEvent.loaded,
|
||||
percent_completed: percent_completed
|
||||
},
|
||||
'*'
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error posting message to window:', error);
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`GET (blob) Response: status=${response.status} statusText=${response.statusText} baseURL=${response.config.baseURL} url=${response.config.url} method=${response.config.method} headers=${response.config.headers} params=${response.config.params}`
|
||||
);
|
||||
}
|
||||
if (log_lvl > 1) {
|
||||
console.log('GET (blob) Response:', response);
|
||||
}
|
||||
|
||||
const { data, headers } = response;
|
||||
|
||||
// Careful if this download filename needs to be changed to a different file extension. The browser/client may not know how to handle it.
|
||||
if (filename) {
|
||||
} else if (headers['content-disposition']) {
|
||||
filename = headers['content-disposition'].replace(/\w+;filename=(.*)/, '$1');
|
||||
} else {
|
||||
filename = 'unknown_file.ext';
|
||||
}
|
||||
|
||||
// WARNING: This needs to be tied to an object type and ID. This is a temporary solution.
|
||||
try {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.postMessage(
|
||||
{
|
||||
type: 'api_download_blob',
|
||||
status: 'complete',
|
||||
task_id: task_id,
|
||||
endpoint: endpoint,
|
||||
filename: filename,
|
||||
size_total: 0,
|
||||
size_loaded: 0,
|
||||
percent_completed: 100
|
||||
},
|
||||
'*'
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error posting message to window:', error);
|
||||
}
|
||||
|
||||
if (auto_download) {
|
||||
if (log_lvl) {
|
||||
console.log(`Auto Download: ${filename}`);
|
||||
}
|
||||
const url = window.URL.createObjectURL(new Blob([response.data]));
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.setAttribute('download', filename || 'download');
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
return true;
|
||||
} else {
|
||||
return response;
|
||||
}
|
||||
})
|
||||
.catch(function (error: any) {
|
||||
// Handle the common and expected 404 "error" first
|
||||
if (error.response && error.response.status === 404) {
|
||||
if (log_lvl) {
|
||||
console.log('The response was a 404 not found "error". Returning null.');
|
||||
}
|
||||
if (log_lvl > 1) {
|
||||
console.log(error.response);
|
||||
}
|
||||
if (log_lvl > 2) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
// Post file download message
|
||||
try {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.postMessage(
|
||||
{
|
||||
type: 'api_download_blob',
|
||||
status: 'complete',
|
||||
task_id: task_id,
|
||||
endpoint: endpoint,
|
||||
filename: filename,
|
||||
size_total: 0,
|
||||
size_loaded: 0,
|
||||
percent_completed: 0
|
||||
},
|
||||
'*'
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error posting message to window:', error);
|
||||
}
|
||||
return null; // Returning null since there were no results
|
||||
}
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`Base URL: ${api_cfg['base_url']} | Endpoint: ${endpoint}`);
|
||||
console.log('Error Message:', error.message); // Is this needed here or below in the in the else portion???
|
||||
|
||||
if (error.response) {
|
||||
// The request was made and the server responded with a status code that falls out of the range of 2xx
|
||||
console.log('Error Response Data', error.response.data);
|
||||
console.log('Error Response Status', error.response.status);
|
||||
console.log('Error Response Headers', error.response.headers);
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received `error.request` is an instance of XMLHttpRequest in the browser and an instance of http.ClientRequest in node.js
|
||||
if (log_lvl > 1) {
|
||||
console.log('Error Request', error.request);
|
||||
}
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
console.log('Error Message', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (error.code === 'ECONNABORTED') {
|
||||
// Timeout Error (You can implement retry here where suitable)
|
||||
console.log('Timeout Error: ', error.message);
|
||||
}
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('The response was an error. Returning false.');
|
||||
}
|
||||
|
||||
return false; // Returning false since something may have gone wrong. This includes timeouts. Also more in line with what the API returns.
|
||||
// return error;
|
||||
});
|
||||
|
||||
if (response_data_promise) {
|
||||
// The most common and expected response.
|
||||
// console.log('Returning result. This is generally expected.');
|
||||
// let test_blob = new Blob([response_data_promise.data]);
|
||||
// console.log(test_blob);
|
||||
// return test_blob;
|
||||
// console.log(response_data_promise.blob());
|
||||
return response_data_promise;
|
||||
} else if (response_data_promise === null) {
|
||||
// Less common, but expected response if no results were returned.
|
||||
if (log_lvl) {
|
||||
console.log('Returning null. This is expected if no results were found. (404)');
|
||||
}
|
||||
return response_data_promise;
|
||||
} else if (response_data_promise === false) {
|
||||
// Not common, but expected response if the request to the API had an issue.
|
||||
console.log('Returning false. There may have been an issue with this request.');
|
||||
return response_data_promise;
|
||||
} else {
|
||||
// This generally should not happen. It likely means the query was bad or an API issue.
|
||||
console.log('Returning (blob) unknown. This should not happen in most cases.');
|
||||
Promise.reject(new Error('fail')).then(resolved, rejected);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function resolved(result: any) {
|
||||
console.log('Resolved');
|
||||
}
|
||||
|
||||
function rejected(result: any) {
|
||||
console.error(result);
|
||||
}
|
||||
@@ -45,7 +45,9 @@ export const patch_object = async function patch_object({
|
||||
// Construct the URL with query parameters
|
||||
const url = new URL(endpoint, api_cfg['base_url']);
|
||||
if (params) {
|
||||
Object.keys(params).forEach((key) => url.searchParams.append(key, params[key]));
|
||||
Object.keys(params).forEach((key) =>
|
||||
url.searchParams.append(key, params[key])
|
||||
);
|
||||
}
|
||||
|
||||
// Clean and merge headers without mutating the original api_cfg
|
||||
@@ -75,14 +77,19 @@ export const patch_object = async function patch_object({
|
||||
}
|
||||
|
||||
// Handle "Bootstrap Paradox" for unauthenticated requests
|
||||
const bypass_val = merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
|
||||
const is_valid_bypass = bypass_val === 'bypass' ||
|
||||
bypass_val === 'Nothing to See Here' ||
|
||||
params['key'] ||
|
||||
bypass_val === 'direct-download';
|
||||
const bypass_val =
|
||||
merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
|
||||
const is_valid_bypass =
|
||||
bypass_val === 'bypass' ||
|
||||
bypass_val === 'Nothing to See Here' ||
|
||||
params['key'] ||
|
||||
bypass_val === 'direct-download';
|
||||
|
||||
if (is_valid_bypass) {
|
||||
if (log_lvl > 1) console.log('api_patch_object: Valid bypass detected. Stripping account ID context.');
|
||||
if (log_lvl > 1)
|
||||
console.log(
|
||||
'api_patch_object: Valid bypass detected. Stripping account ID context.'
|
||||
);
|
||||
delete merged_headers['x-account-id'];
|
||||
delete merged_headers['x_account_id'];
|
||||
} else {
|
||||
@@ -104,11 +111,12 @@ export const patch_object = async function patch_object({
|
||||
}
|
||||
|
||||
// Auto-inject Authorization header if JWT is present but header is missing
|
||||
let jwt = headers_cleaned['jwt'] ||
|
||||
headers_cleaned['JWT'] ||
|
||||
api_cfg['jwt'] ||
|
||||
api_cfg['headers']?.['jwt'] ||
|
||||
api_cfg['headers']?.['JWT'];
|
||||
let jwt =
|
||||
headers_cleaned['jwt'] ||
|
||||
headers_cleaned['JWT'] ||
|
||||
api_cfg['jwt'] ||
|
||||
api_cfg['headers']?.['jwt'] ||
|
||||
api_cfg['headers']?.['JWT'];
|
||||
|
||||
// Final Fallback: Direct check of primary ae_loc key
|
||||
if (!jwt && typeof localStorage !== 'undefined') {
|
||||
@@ -123,7 +131,11 @@ export const patch_object = async function patch_object({
|
||||
}
|
||||
}
|
||||
|
||||
if (jwt && !headers_cleaned['Authorization'] && !headers_cleaned['authorization']) {
|
||||
if (
|
||||
jwt &&
|
||||
!headers_cleaned['Authorization'] &&
|
||||
!headers_cleaned['authorization']
|
||||
) {
|
||||
headers_cleaned['Authorization'] = `Bearer ${jwt}`;
|
||||
}
|
||||
|
||||
@@ -145,7 +157,9 @@ export const patch_object = async function patch_object({
|
||||
try {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => {
|
||||
console.error(`API PATCH request timed out after ${timeout}ms.`);
|
||||
console.error(
|
||||
`API PATCH request timed out after ${timeout}ms.`
|
||||
);
|
||||
controller.abort();
|
||||
}, timeout);
|
||||
|
||||
@@ -156,9 +170,10 @@ export const patch_object = async function patch_object({
|
||||
signal: controller.signal
|
||||
};
|
||||
|
||||
const response = await fetch_method(url.toString(), fetchOptions).catch(function (
|
||||
error: any
|
||||
) {
|
||||
const response = await fetch_method(
|
||||
url.toString(),
|
||||
fetchOptions
|
||||
).catch(function (error: any) {
|
||||
console.log(
|
||||
'API PATCH Object *fetch* request was aborted or failed in an unexpected way.',
|
||||
error
|
||||
@@ -173,30 +188,53 @@ export const patch_object = async function patch_object({
|
||||
}
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`Response: status=${response.status} attempt=${attempt}`);
|
||||
console.log(
|
||||
`Response: status=${response.status} attempt=${attempt}`
|
||||
);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 404) {
|
||||
if (log_lvl) {
|
||||
console.log('The response was a 404 not found "error". Returning null.');
|
||||
console.log(
|
||||
'The response was a 404 not found "error". Returning null.'
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// FAIL FAST (Section 2D): Do not retry on Auth or Client errors (400, 401, 403, 422)
|
||||
if (response.status === 400 || response.status === 401 || response.status === 403 || response.status === 422) {
|
||||
if (log_lvl) console.error(`API Client Failure (${response.status}). Failing fast.`);
|
||||
if (
|
||||
response.status === 400 ||
|
||||
response.status === 401 ||
|
||||
response.status === 403 ||
|
||||
response.status === 422
|
||||
) {
|
||||
if (log_lvl)
|
||||
console.error(
|
||||
`API Client Failure (${response.status}). Failing fast.`
|
||||
);
|
||||
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
console.warn(`AUTH DIAGNOSTICS (PATCH): Headers sent for ${endpoint}:`, {
|
||||
has_auth: !!headers_cleaned['Authorization'],
|
||||
has_api_key: !!headers_cleaned['x-aether-api-key'],
|
||||
has_account_id: !!headers_cleaned['x-account-id'],
|
||||
jwt_preview: jwt ? `${jwt.slice(0, 8)}...` : 'MISSING'
|
||||
});
|
||||
console.warn(
|
||||
`AUTH DIAGNOSTICS (PATCH): Headers sent for ${endpoint}:`,
|
||||
{
|
||||
has_auth: !!headers_cleaned['Authorization'],
|
||||
has_api_key:
|
||||
!!headers_cleaned['x-aether-api-key'],
|
||||
has_account_id:
|
||||
!!headers_cleaned['x-account-id'],
|
||||
jwt_preview: jwt
|
||||
? `${jwt.slice(0, 8)}...`
|
||||
: 'MISSING'
|
||||
}
|
||||
);
|
||||
// Signal the root layout to show the session-expired banner.
|
||||
if (browser) ae_auth_error.set({ type: 'expired', ts: Date.now() });
|
||||
if (browser)
|
||||
ae_auth_error.set({
|
||||
type: 'expired',
|
||||
ts: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
// Structured Error Handling (V3): Attempt to get rich error metadata
|
||||
@@ -207,7 +245,11 @@ export const patch_object = async function patch_object({
|
||||
// Not JSON
|
||||
}
|
||||
|
||||
if (log_lvl) console.log('The response was not ok. Structured Error Check:', error_json);
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
'The response was not ok. Structured Error Check:',
|
||||
error_json
|
||||
);
|
||||
|
||||
if (error_json?.meta?.details) {
|
||||
return error_json;
|
||||
@@ -221,7 +263,10 @@ export const patch_object = async function patch_object({
|
||||
status_code: response.status,
|
||||
details: {
|
||||
category: 'validation',
|
||||
message: typeof error_json.detail === 'string' ? error_json.detail : JSON.stringify(error_json.detail),
|
||||
message:
|
||||
typeof error_json.detail === 'string'
|
||||
? error_json.detail
|
||||
: JSON.stringify(error_json.detail),
|
||||
raw: error_json.detail
|
||||
}
|
||||
}
|
||||
@@ -242,7 +287,11 @@ export const patch_object = async function patch_object({
|
||||
|
||||
// Return the response data or metadata
|
||||
// Robustly handle V3 response envelopes
|
||||
return return_meta ? json : (json.data !== undefined ? json.data : json);
|
||||
return return_meta
|
||||
? json
|
||||
: json.data !== undefined
|
||||
? json.data
|
||||
: json;
|
||||
} catch (error) {
|
||||
console.error(`API PATCH error on attempt ${attempt}:`, error);
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ interface CreateAeObjV3Params {
|
||||
log_lvl?: number;
|
||||
}
|
||||
|
||||
export async function create_ae_obj_v3({
|
||||
export async function create_ae_obj({
|
||||
api_cfg,
|
||||
obj_type,
|
||||
fields,
|
||||
@@ -23,9 +23,9 @@ export async function create_ae_obj_v3({
|
||||
log_lvl = 0
|
||||
}: CreateAeObjV3Params) {
|
||||
const endpoint = `/v3/crud/${obj_type}/`;
|
||||
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('*** create_ae_obj_v3 ***');
|
||||
console.log('*** create_ae_obj ***');
|
||||
console.log('Endpoint:', endpoint);
|
||||
console.log('Fields:', fields);
|
||||
}
|
||||
@@ -33,7 +33,11 @@ export async function create_ae_obj_v3({
|
||||
// Standard Aether Pattern: Auto-serialize any key ending in _json
|
||||
const cleaned_fields = { ...fields };
|
||||
for (const key in cleaned_fields) {
|
||||
if (key.endsWith('_json') && cleaned_fields[key] !== null && typeof cleaned_fields[key] === 'object') {
|
||||
if (
|
||||
key.endsWith('_json') &&
|
||||
cleaned_fields[key] !== null &&
|
||||
typeof cleaned_fields[key] === 'object'
|
||||
) {
|
||||
if (log_lvl) console.log(`Auto-serializing field: ${key}`);
|
||||
cleaned_fields[key] = JSON.stringify(cleaned_fields[key]);
|
||||
}
|
||||
@@ -61,13 +65,13 @@ interface CreateNestedObjV3Params {
|
||||
for_obj_type?: string;
|
||||
for_obj_id?: string;
|
||||
obj_type?: string;
|
||||
|
||||
|
||||
fields: key_val;
|
||||
params?: key_val;
|
||||
log_lvl?: number;
|
||||
}
|
||||
|
||||
export async function create_nested_obj_v3({
|
||||
export async function create_nested_obj({
|
||||
api_cfg,
|
||||
parent_type,
|
||||
parent_id,
|
||||
@@ -86,7 +90,7 @@ export async function create_nested_obj_v3({
|
||||
const endpoint = `/v3/crud/${p_type}/${p_id}/${c_type}/`;
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('*** create_nested_obj_v3 ***');
|
||||
console.log('*** create_nested_obj ***');
|
||||
console.log('Endpoint:', endpoint);
|
||||
console.log('Fields:', fields);
|
||||
}
|
||||
@@ -94,7 +98,11 @@ export async function create_nested_obj_v3({
|
||||
// Standard Aether Pattern: Auto-serialize any key ending in _json
|
||||
const cleaned_fields = { ...fields };
|
||||
for (const key in cleaned_fields) {
|
||||
if (key.endsWith('_json') && cleaned_fields[key] !== null && typeof cleaned_fields[key] === 'object') {
|
||||
if (
|
||||
key.endsWith('_json') &&
|
||||
cleaned_fields[key] !== null &&
|
||||
typeof cleaned_fields[key] === 'object'
|
||||
) {
|
||||
cleaned_fields[key] = JSON.stringify(cleaned_fields[key]);
|
||||
}
|
||||
}
|
||||
@@ -121,7 +129,7 @@ interface UpdateAeObjV3Params {
|
||||
log_lvl?: number;
|
||||
}
|
||||
|
||||
export async function update_ae_obj_v3({
|
||||
export async function update_ae_obj({
|
||||
api_cfg,
|
||||
obj_type,
|
||||
obj_id,
|
||||
@@ -132,7 +140,7 @@ export async function update_ae_obj_v3({
|
||||
const endpoint = `/v3/crud/${obj_type}/${obj_id}`;
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('*** update_ae_obj_v3 ***');
|
||||
console.log('*** update_ae_obj ***');
|
||||
console.log('Endpoint:', endpoint);
|
||||
console.log('Fields:', fields);
|
||||
}
|
||||
@@ -140,7 +148,11 @@ export async function update_ae_obj_v3({
|
||||
// Standard Aether Pattern: Auto-serialize any key ending in _json
|
||||
const cleaned_fields = { ...fields };
|
||||
for (const key in cleaned_fields) {
|
||||
if (key.endsWith('_json') && cleaned_fields[key] !== null && typeof cleaned_fields[key] === 'object') {
|
||||
if (
|
||||
key.endsWith('_json') &&
|
||||
cleaned_fields[key] !== null &&
|
||||
typeof cleaned_fields[key] === 'object'
|
||||
) {
|
||||
if (log_lvl > 1) console.log(`Auto-serializing field: ${key}`);
|
||||
cleaned_fields[key] = JSON.stringify(cleaned_fields[key]);
|
||||
}
|
||||
@@ -172,7 +184,7 @@ interface UpdateNestedObjV3Params {
|
||||
log_lvl?: number;
|
||||
}
|
||||
|
||||
export async function update_nested_obj_v3({
|
||||
export async function update_nested_obj({
|
||||
api_cfg,
|
||||
parent_type,
|
||||
parent_id,
|
||||
@@ -194,7 +206,7 @@ export async function update_nested_obj_v3({
|
||||
const endpoint = `/v3/crud/${p_type}/${p_id}/${c_type}/${c_id}`;
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('*** update_nested_obj_v3 ***');
|
||||
console.log('*** update_nested_obj ***');
|
||||
console.log('Endpoint:', endpoint);
|
||||
console.log('Fields:', fields);
|
||||
}
|
||||
@@ -202,7 +214,11 @@ export async function update_nested_obj_v3({
|
||||
// Standard Aether Pattern: Auto-serialize any key ending in _json
|
||||
const cleaned_fields = { ...fields };
|
||||
for (const key in cleaned_fields) {
|
||||
if (key.endsWith('_json') && cleaned_fields[key] !== null && typeof cleaned_fields[key] === 'object') {
|
||||
if (
|
||||
key.endsWith('_json') &&
|
||||
cleaned_fields[key] !== null &&
|
||||
typeof cleaned_fields[key] === 'object'
|
||||
) {
|
||||
cleaned_fields[key] = JSON.stringify(cleaned_fields[key]);
|
||||
}
|
||||
}
|
||||
@@ -233,7 +249,7 @@ interface DeleteAeObjV3Params {
|
||||
* Delete a single object by ID (V3)
|
||||
* Supports 'delete' (hard), 'soft_delete', 'disable' (enable=false), and 'hide' (hide=true).
|
||||
*/
|
||||
export async function delete_ae_obj_v3({
|
||||
export async function delete_ae_obj({
|
||||
api_cfg,
|
||||
obj_type,
|
||||
obj_id,
|
||||
@@ -245,7 +261,7 @@ export async function delete_ae_obj_v3({
|
||||
const query_params = { ...params, method };
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('*** delete_ae_obj_v3 ***');
|
||||
console.log('*** delete_ae_obj ***');
|
||||
console.log('Endpoint:', endpoint);
|
||||
console.log('Params:', query_params);
|
||||
}
|
||||
@@ -278,7 +294,7 @@ interface DeleteNestedAeObjV3Params {
|
||||
/**
|
||||
* Delete a single nested object by ID (V3)
|
||||
*/
|
||||
export async function delete_nested_ae_obj_v3({
|
||||
export async function delete_nested_ae_obj({
|
||||
api_cfg,
|
||||
parent_type,
|
||||
parent_id,
|
||||
@@ -301,7 +317,7 @@ export async function delete_nested_ae_obj_v3({
|
||||
const query_params = { ...params, method };
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('*** delete_nested_ae_obj_v3 ***');
|
||||
console.log('*** delete_nested_ae_obj ***');
|
||||
console.log('Endpoint:', endpoint);
|
||||
console.log('Params:', query_params);
|
||||
}
|
||||
@@ -10,7 +10,10 @@ interface SearchAeObjV3Params {
|
||||
view?: string;
|
||||
for_obj_type?: string;
|
||||
for_obj_id?: string;
|
||||
order_by_li?: Record<string, 'ASC' | 'DESC'> | Record<string, 'ASC' | 'DESC'>[] | null;
|
||||
order_by_li?:
|
||||
| Record<string, 'ASC' | 'DESC'>
|
||||
| Record<string, 'ASC' | 'DESC'>[]
|
||||
| null;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
delay_ms?: number;
|
||||
@@ -19,7 +22,7 @@ interface SearchAeObjV3Params {
|
||||
log_lvl?: number;
|
||||
}
|
||||
|
||||
export async function search_ae_obj_v3({
|
||||
export async function search_ae_obj({
|
||||
api_cfg,
|
||||
obj_type,
|
||||
search_query,
|
||||
@@ -55,13 +58,16 @@ export async function search_ae_obj_v3({
|
||||
|
||||
// Serialize any complex objects in the query params (e.g. ft_qry, lk_qry)
|
||||
for (const key in query_params) {
|
||||
if (typeof query_params[key] === 'object' && query_params[key] !== null) {
|
||||
if (
|
||||
typeof query_params[key] === 'object' &&
|
||||
query_params[key] !== null
|
||||
) {
|
||||
query_params[key] = JSON.stringify(query_params[key]);
|
||||
}
|
||||
}
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('*** search_ae_obj_v3 ***');
|
||||
console.log('*** search_ae_obj ***');
|
||||
console.log('Endpoint:', endpoint);
|
||||
console.log('Params:', query_params);
|
||||
console.log('Search Query:', search_query);
|
||||
@@ -76,4 +82,4 @@ export async function search_ae_obj_v3({
|
||||
data: search_query,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,9 @@ export const post_object = async function post_object({
|
||||
retry_count?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** post_object() *** Endpoint: ${endpoint} Task ID: ${task_id}`);
|
||||
console.log(
|
||||
`*** post_object() *** Endpoint: ${endpoint} Task ID: ${task_id}`
|
||||
);
|
||||
console.log('Params:', params);
|
||||
if (log_lvl > 1) {
|
||||
console.log('Data:', data);
|
||||
@@ -65,7 +67,9 @@ export const post_object = async function post_object({
|
||||
// Construct the URL with query parameters
|
||||
const url = new URL(endpoint, api_cfg['base_url']);
|
||||
if (params) {
|
||||
Object.keys(params).forEach((key) => url.searchParams.append(key, params[key]));
|
||||
Object.keys(params).forEach((key) =>
|
||||
url.searchParams.append(key, params[key])
|
||||
);
|
||||
}
|
||||
|
||||
// Clean and merge headers
|
||||
@@ -95,14 +99,19 @@ export const post_object = async function post_object({
|
||||
}
|
||||
|
||||
// Handle "Bootstrap Paradox" for unauthenticated requests
|
||||
const bypass_val = merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
|
||||
const is_valid_bypass = bypass_val === 'bypass' ||
|
||||
bypass_val === 'Nothing to See Here' ||
|
||||
params['key'] ||
|
||||
bypass_val === 'direct-download';
|
||||
const bypass_val =
|
||||
merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
|
||||
const is_valid_bypass =
|
||||
bypass_val === 'bypass' ||
|
||||
bypass_val === 'Nothing to See Here' ||
|
||||
params['key'] ||
|
||||
bypass_val === 'direct-download';
|
||||
|
||||
if (is_valid_bypass) {
|
||||
if (log_lvl > 1) console.log('api_post_object: Valid bypass detected. Stripping account ID context.');
|
||||
if (log_lvl > 1)
|
||||
console.log(
|
||||
'api_post_object: Valid bypass detected. Stripping account ID context.'
|
||||
);
|
||||
delete merged_headers['x-account-id'];
|
||||
delete merged_headers['x_account_id'];
|
||||
} else {
|
||||
@@ -124,11 +133,12 @@ export const post_object = async function post_object({
|
||||
}
|
||||
|
||||
// Auto-inject Authorization header if JWT is present but header is missing
|
||||
let jwt = headers_cleaned['jwt'] ||
|
||||
headers_cleaned['JWT'] ||
|
||||
api_cfg['jwt'] ||
|
||||
api_cfg['headers']?.['jwt'] ||
|
||||
api_cfg['headers']?.['JWT'];
|
||||
let jwt =
|
||||
headers_cleaned['jwt'] ||
|
||||
headers_cleaned['JWT'] ||
|
||||
api_cfg['jwt'] ||
|
||||
api_cfg['headers']?.['jwt'] ||
|
||||
api_cfg['headers']?.['JWT'];
|
||||
|
||||
// Final Fallback: Direct check of primary ae_loc key
|
||||
if (!jwt && typeof localStorage !== 'undefined') {
|
||||
@@ -143,7 +153,11 @@ export const post_object = async function post_object({
|
||||
}
|
||||
}
|
||||
|
||||
if (jwt && !headers_cleaned['Authorization'] && !headers_cleaned['authorization']) {
|
||||
if (
|
||||
jwt &&
|
||||
!headers_cleaned['Authorization'] &&
|
||||
!headers_cleaned['authorization']
|
||||
) {
|
||||
headers_cleaned['Authorization'] = `Bearer ${jwt}`;
|
||||
}
|
||||
|
||||
@@ -186,13 +200,21 @@ export const post_object = async function post_object({
|
||||
console.log('Fetch Options:', fetchOptions);
|
||||
}
|
||||
|
||||
const response = await fetch_method(url.toString(), fetchOptions).catch(function (
|
||||
error: any
|
||||
) {
|
||||
const response = await fetch_method(
|
||||
url.toString(),
|
||||
fetchOptions
|
||||
).catch(function (error: any) {
|
||||
// SILENCE NOISE: Aborted requests shouldn't spam logs at log_lvl 0
|
||||
if (error.name === 'AbortError' || error.message?.includes('aborted') || error.name === 'TypeError') {
|
||||
if (
|
||||
error.name === 'AbortError' ||
|
||||
error.message?.includes('aborted') ||
|
||||
error.name === 'TypeError'
|
||||
) {
|
||||
if (log_lvl > 1) {
|
||||
console.log('API POST: Request was aborted or terminated by browser. Expected during navigation.', error);
|
||||
console.log(
|
||||
'API POST: Request was aborted or terminated by browser. Expected during navigation.',
|
||||
error
|
||||
);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
@@ -206,9 +228,17 @@ export const post_object = async function post_object({
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
// Check if we should stop due to abort or network failure
|
||||
if (response instanceof Error || (response && (response.name === 'TypeError' || response.name === 'AbortError'))) {
|
||||
if (
|
||||
response instanceof Error ||
|
||||
(response &&
|
||||
(response.name === 'TypeError' ||
|
||||
response.name === 'AbortError'))
|
||||
) {
|
||||
if (response.name === 'AbortError') return false;
|
||||
if (log_lvl > 1) console.log('API POST Object: Detected NetworkError or TypeError. Failing fast.');
|
||||
if (log_lvl > 1)
|
||||
console.log(
|
||||
'API POST Object: Detected NetworkError or TypeError. Failing fast.'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -219,30 +249,53 @@ export const post_object = async function post_object({
|
||||
}
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`Response: status=${response.status} attempt=${attempt}`);
|
||||
console.log(
|
||||
`Response: status=${response.status} attempt=${attempt}`
|
||||
);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 404) {
|
||||
if (log_lvl) {
|
||||
console.log('The response was a 404 not found "error". Returning null.');
|
||||
console.log(
|
||||
'The response was a 404 not found "error". Returning null.'
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// FAIL FAST (Section 2D): Do not retry on Auth or Client errors (400, 401, 403, 422)
|
||||
if (response.status === 400 || response.status === 401 || response.status === 403 || response.status === 422) {
|
||||
if (log_lvl) console.error(`API Client Failure (${response.status}). Failing fast.`);
|
||||
if (
|
||||
response.status === 400 ||
|
||||
response.status === 401 ||
|
||||
response.status === 403 ||
|
||||
response.status === 422
|
||||
) {
|
||||
if (log_lvl)
|
||||
console.error(
|
||||
`API Client Failure (${response.status}). Failing fast.`
|
||||
);
|
||||
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
console.warn(`AUTH DIAGNOSTICS (POST): Headers sent for ${endpoint}:`, {
|
||||
has_auth: !!headers_cleaned['Authorization'],
|
||||
has_api_key: !!headers_cleaned['x-aether-api-key'],
|
||||
has_account_id: !!headers_cleaned['x-account-id'],
|
||||
jwt_preview: jwt ? `${jwt.slice(0, 8)}...` : 'MISSING'
|
||||
});
|
||||
console.warn(
|
||||
`AUTH DIAGNOSTICS (POST): Headers sent for ${endpoint}:`,
|
||||
{
|
||||
has_auth: !!headers_cleaned['Authorization'],
|
||||
has_api_key:
|
||||
!!headers_cleaned['x-aether-api-key'],
|
||||
has_account_id:
|
||||
!!headers_cleaned['x-account-id'],
|
||||
jwt_preview: jwt
|
||||
? `${jwt.slice(0, 8)}...`
|
||||
: 'MISSING'
|
||||
}
|
||||
);
|
||||
// Signal the root layout to show the session-expired banner.
|
||||
if (browser) ae_auth_error.set({ type: 'expired', ts: Date.now() });
|
||||
if (browser)
|
||||
ae_auth_error.set({
|
||||
type: 'expired',
|
||||
ts: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
// Structured Error Handling (V3): Attempt to get rich error metadata
|
||||
@@ -253,7 +306,11 @@ export const post_object = async function post_object({
|
||||
// Not JSON
|
||||
}
|
||||
|
||||
if (log_lvl) console.log('The response was not ok. Structured Error Check:', error_json);
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
'The response was not ok. Structured Error Check:',
|
||||
error_json
|
||||
);
|
||||
|
||||
if (error_json?.meta?.details) {
|
||||
return error_json;
|
||||
@@ -267,7 +324,10 @@ export const post_object = async function post_object({
|
||||
status_code: response.status,
|
||||
details: {
|
||||
category: 'validation',
|
||||
message: typeof error_json.detail === 'string' ? error_json.detail : JSON.stringify(error_json.detail),
|
||||
message:
|
||||
typeof error_json.detail === 'string'
|
||||
? error_json.detail
|
||||
: JSON.stringify(error_json.detail),
|
||||
raw: error_json.detail
|
||||
}
|
||||
}
|
||||
@@ -311,7 +371,11 @@ export const post_object = async function post_object({
|
||||
|
||||
// Return the response data or metadata
|
||||
// Robustly handle V3 response envelopes
|
||||
return return_meta ? json : (json.data !== undefined ? json.data : json);
|
||||
return return_meta
|
||||
? json
|
||||
: json.data !== undefined
|
||||
? json.data
|
||||
: json;
|
||||
} else {
|
||||
const blob = await response.blob();
|
||||
|
||||
|
||||
@@ -36,11 +36,13 @@ export async function load_ae_obj_id__archive({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Archive | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_id__archive() *** archive_id=${archive_id}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_id__archive() *** archive_id=${archive_id}`
|
||||
);
|
||||
}
|
||||
|
||||
ae_promises.load__archive_obj = await api
|
||||
.get_ae_obj_v3({
|
||||
.get_ae_obj({
|
||||
api_cfg: api_cfg,
|
||||
obj_type: 'archive',
|
||||
obj_id: archive_id,
|
||||
@@ -52,10 +54,11 @@ export async function load_ae_obj_id__archive({
|
||||
.then(async function (archive_obj_get_result) {
|
||||
if (archive_obj_get_result) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__archive_props({
|
||||
obj_li: [archive_obj_get_result],
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__archive_props({
|
||||
obj_li: [archive_obj_get_result],
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_archives,
|
||||
table_name: 'archive',
|
||||
@@ -76,19 +79,21 @@ export async function load_ae_obj_id__archive({
|
||||
|
||||
if (inc_content_li && ae_promises.load__archive_obj) {
|
||||
// Load the contents for the archive
|
||||
const load_archive_content_obj_li = await load_ae_obj_li__archive_content({
|
||||
api_cfg: api_cfg,
|
||||
for_obj_type: 'archive',
|
||||
for_obj_id: archive_id,
|
||||
enabled: enabled,
|
||||
hidden: hidden,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
params: params,
|
||||
try_cache: try_cache,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
ae_promises.load__archive_obj.archive_content_li = load_archive_content_obj_li;
|
||||
const load_archive_content_obj_li =
|
||||
await load_ae_obj_li__archive_content({
|
||||
api_cfg: api_cfg,
|
||||
for_obj_type: 'archive',
|
||||
for_obj_id: archive_id,
|
||||
enabled: enabled,
|
||||
hidden: hidden,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
params: params,
|
||||
try_cache: try_cache,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
ae_promises.load__archive_obj.archive_content_li =
|
||||
load_archive_content_obj_li;
|
||||
}
|
||||
|
||||
return ae_promises.load__archive_obj;
|
||||
@@ -125,7 +130,9 @@ export async function load_ae_obj_li__archive({
|
||||
view?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
order_by_li?: Record<string, 'ASC' | 'DESC'> | Record<string, 'ASC' | 'DESC'>[];
|
||||
order_by_li?:
|
||||
| Record<string, 'ASC' | 'DESC'>
|
||||
| Record<string, 'ASC' | 'DESC'>[];
|
||||
params?: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
@@ -135,15 +142,17 @@ export async function load_ae_obj_li__archive({
|
||||
`*** load_ae_obj_li__archive() *** for_obj_type=${for_obj_type} for_obj_id=${for_obj_id}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// DEBUG: Trace massive content loads
|
||||
if (inc_content_li) {
|
||||
console.warn(`load_ae_obj_li__archive: Loading content for ALL archives in list! Limit: ${limit}`);
|
||||
// console.trace();
|
||||
console.warn(
|
||||
`load_ae_obj_li__archive: Loading content for ALL archives in list! Limit: ${limit}`
|
||||
);
|
||||
// console.trace();
|
||||
}
|
||||
|
||||
ae_promises.load__archive_obj_li = await api
|
||||
.get_ae_obj_li_v3({
|
||||
.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'archive',
|
||||
for_obj_type,
|
||||
@@ -159,10 +168,11 @@ export async function load_ae_obj_li__archive({
|
||||
.then(async function (archive_obj_li_get_result) {
|
||||
if (archive_obj_li_get_result) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__archive_props({
|
||||
obj_li: archive_obj_li_get_result,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__archive_props({
|
||||
obj_li: archive_obj_li_get_result,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_archives,
|
||||
table_name: 'archive',
|
||||
@@ -220,10 +230,12 @@ export async function create_ae_obj__archive({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Archive | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** create_ae_obj__archive() *** account_id=${account_id}`);
|
||||
console.log(
|
||||
`*** create_ae_obj__archive() *** account_id=${account_id}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await api.create_ae_obj_v3({
|
||||
const result = await api.create_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'archive',
|
||||
fields: {
|
||||
@@ -268,10 +280,12 @@ export async function delete_ae_obj_id__archive({
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** delete_ae_obj_id__archive() *** archive_id=${archive_id}`);
|
||||
console.log(
|
||||
`*** delete_ae_obj_id__archive() *** archive_id=${archive_id}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await api.delete_ae_obj_v3({
|
||||
const result = await api.delete_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'archive',
|
||||
obj_id: archive_id,
|
||||
@@ -304,10 +318,13 @@ export async function update_ae_obj__archive({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Archive | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** update_ae_obj__archive() *** archive_id=${archive_id}`, data_kv);
|
||||
console.log(
|
||||
`*** update_ae_obj__archive() *** archive_id=${archive_id}`,
|
||||
data_kv
|
||||
);
|
||||
}
|
||||
|
||||
const result = await api.update_ae_obj_v3({
|
||||
const result = await api.update_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'archive',
|
||||
obj_id: archive_id,
|
||||
@@ -366,14 +383,18 @@ export async function qry__archive({
|
||||
const search_query: any = { and: [] };
|
||||
|
||||
if (account_id) {
|
||||
search_query.and.push({ field: 'account_id_random', op: 'eq', value: account_id });
|
||||
search_query.and.push({
|
||||
field: 'account_id_random',
|
||||
op: 'eq',
|
||||
value: account_id
|
||||
});
|
||||
}
|
||||
|
||||
if (qry_str) {
|
||||
search_query.q = qry_str;
|
||||
}
|
||||
|
||||
ae_promises.load__archive_obj_li = await api.search_ae_obj_v3({
|
||||
ae_promises.load__archive_obj_li = await api.search_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'archive',
|
||||
search_query,
|
||||
@@ -452,11 +473,15 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on;
|
||||
const name = processed_obj.name ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
@@ -489,4 +514,4 @@ export async function process_ae_obj__archive_props({
|
||||
return obj;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export async function load_ae_obj_id__archive_content({
|
||||
}
|
||||
|
||||
ae_promises.load__archive_content_obj = await api
|
||||
.get_ae_obj_v3({
|
||||
.get_ae_obj({
|
||||
api_cfg: api_cfg,
|
||||
obj_type: 'archive_content',
|
||||
obj_id: archive_content_id,
|
||||
@@ -41,10 +41,11 @@ export async function load_ae_obj_id__archive_content({
|
||||
.then(async function (archive_content_obj_get_result) {
|
||||
if (archive_content_obj_get_result) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__archive_content_props({
|
||||
obj_li: [archive_content_obj_get_result],
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__archive_content_props({
|
||||
obj_li: [archive_content_obj_get_result],
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_archives,
|
||||
table_name: 'content',
|
||||
@@ -96,7 +97,9 @@ export async function load_ae_obj_li__archive_content({
|
||||
view?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
order_by_li?: Record<string, 'ASC' | 'DESC'> | Record<string, 'ASC' | 'DESC'>[];
|
||||
order_by_li?:
|
||||
| Record<string, 'ASC' | 'DESC'>
|
||||
| Record<string, 'ASC' | 'DESC'>[];
|
||||
params?: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
@@ -108,7 +111,7 @@ export async function load_ae_obj_li__archive_content({
|
||||
}
|
||||
|
||||
ae_promises.load__archive_content_obj_li = await api
|
||||
.get_ae_obj_li_v3({
|
||||
.get_ae_obj_li({
|
||||
api_cfg: api_cfg,
|
||||
obj_type: 'archive_content',
|
||||
for_obj_type,
|
||||
@@ -124,10 +127,11 @@ export async function load_ae_obj_li__archive_content({
|
||||
.then(async function (archive_content_obj_li_get_result) {
|
||||
if (archive_content_obj_li_get_result) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__archive_content_props({
|
||||
obj_li: archive_content_obj_li_get_result,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__archive_content_props({
|
||||
obj_li: archive_content_obj_li_get_result,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_archives,
|
||||
table_name: 'content',
|
||||
@@ -162,15 +166,19 @@ export async function create_ae_obj__archive_content({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_ArchiveContent | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** create_ae_obj__archive_content() *** archive_id=${archive_id}`);
|
||||
console.log(
|
||||
`*** create_ae_obj__archive_content() *** archive_id=${archive_id}`
|
||||
);
|
||||
}
|
||||
|
||||
if (!archive_id) {
|
||||
console.log(`ERROR: Archives - Content - archive_id required to create`);
|
||||
console.log(
|
||||
`ERROR: Archives - Content - archive_id required to create`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const result = await api.create_nested_obj_v3({
|
||||
const result = await api.create_nested_obj({
|
||||
api_cfg,
|
||||
parent_type: 'archive',
|
||||
parent_id: archive_id,
|
||||
@@ -219,7 +227,7 @@ export async function delete_ae_obj_id__archive_content({
|
||||
);
|
||||
}
|
||||
|
||||
const result = await api.delete_ae_obj_v3({
|
||||
const result = await api.delete_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'archive_content',
|
||||
obj_id: archive_content_id,
|
||||
@@ -258,7 +266,7 @@ export async function update_ae_obj__archive_content({
|
||||
);
|
||||
}
|
||||
|
||||
const result = await api.update_ae_obj_v3({
|
||||
const result = await api.update_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'archive_content',
|
||||
obj_id: archive_content_id,
|
||||
@@ -357,11 +365,15 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on;
|
||||
const name = processed_obj.name ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
@@ -399,4 +411,4 @@ export async function process_ae_obj__archive_content_props({
|
||||
return obj;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,14 @@ import type { key_val } from '$lib/stores/ae_stores';
|
||||
*/
|
||||
export interface Archive {
|
||||
id: string;
|
||||
// id_random: string;
|
||||
// id_random: string; // NO LONGER USE "_random"
|
||||
archive_id: string;
|
||||
// archive_id_random: string;
|
||||
// archive_id_random: string; // NO LONGER USE "_random"
|
||||
|
||||
code?: null | string;
|
||||
|
||||
account_id: string;
|
||||
// account_id_random: string;
|
||||
// account_id_random: string; // NO LONGER USE "_random"
|
||||
|
||||
// archive_type: string;
|
||||
|
||||
@@ -80,12 +80,12 @@ export interface Archive {
|
||||
*/
|
||||
export interface Archive_Content {
|
||||
id: string;
|
||||
// id_random: string;
|
||||
// id_random: string; // NO LONGER USE "_random"
|
||||
archive_content_id: string;
|
||||
// archive_content_id_random: string;
|
||||
// archive_content_id_random: string; // NO LONGER USE "_random"
|
||||
|
||||
archive_id: string;
|
||||
// archive_id_random: string;
|
||||
// archive_id_random: string; // NO LONGER USE "_random"
|
||||
|
||||
archive_content_type: string;
|
||||
|
||||
@@ -169,10 +169,6 @@ export class MySubClassedDexie extends Dexie {
|
||||
enable, hide, priority, sort, group, notes, created_on, updated_on, [group+priority+sort+updated_on]`
|
||||
});
|
||||
|
||||
// file_path,
|
||||
// filename, file_extension,
|
||||
// original_datetime, original_timezone, original_location, original_url, original_url_text,
|
||||
// enable_for_public,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,188 +1,191 @@
|
||||
<script lang="ts">
|
||||
// Imports
|
||||
// Import components and elements
|
||||
// import Element_input_files_tbl from '$lib/element_input_files_tbl.svelte';
|
||||
// Imports
|
||||
// Import components and elements
|
||||
// import Element_input_files_tbl from '$lib/element_input_files_tbl.svelte';
|
||||
|
||||
// Import storage, functions, and libraries
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
// Import storage, functions, and libraries
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
|
||||
import { api } from '$lib/api/api';
|
||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||
import {
|
||||
ae_snip,
|
||||
ae_loc,
|
||||
ae_sess,
|
||||
ae_api,
|
||||
ae_trig,
|
||||
slct,
|
||||
slct_trigger
|
||||
} from '$lib/stores/ae_stores';
|
||||
import { api } from '$lib/api/api';
|
||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||
import {
|
||||
ae_snip,
|
||||
ae_loc,
|
||||
ae_sess,
|
||||
ae_api,
|
||||
ae_trig,
|
||||
slct,
|
||||
slct_trigger
|
||||
} from '$lib/stores/ae_stores';
|
||||
|
||||
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';
|
||||
import { Check, Download, LoaderCircle, MinusCircle, Scissors } from '@lucide/svelte';
|
||||
// Exports
|
||||
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';
|
||||
import {
|
||||
Check,
|
||||
Download,
|
||||
LoaderCircle,
|
||||
MinusCircle,
|
||||
Scissors
|
||||
} from '@lucide/svelte';
|
||||
// Exports
|
||||
|
||||
// export let input_name = 'file_list';
|
||||
// export let multiple: boolean = true;
|
||||
// export let required: boolean = true;
|
||||
// export let input_name = 'file_list';
|
||||
// export let multiple: boolean = true;
|
||||
// export let required: boolean = true;
|
||||
|
||||
// export let input_class_li: string[] = ['file_drop_area'];
|
||||
// export let input_class_li: string[] = ['file_drop_area'];
|
||||
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
// Expecting these for link_to_type: 'event', 'event_location', 'archive_content', etc
|
||||
link_to_type: string;
|
||||
link_to_id: string;
|
||||
// export let accept: string = 'audio/*, image/*, video/*, .bak, .cfg, .css, .csv, .doc, .docx, .gz, .htm, .html, .ini, .iso, .j2, .json, .key, .keynote, .md, .pdf, .ppt, .pptx, .rar, .rtf, .sql, .svelte, ttf, .txt, .xls, .xlsx, .xz, .zip, .bin, .dmg, .exe, .js, .msi, .php, .py, .sh';
|
||||
class_li_default?: string;
|
||||
class_li?: string;
|
||||
// export let table_class_li: string[] = ['table', 'table-sm', 'table-striped', 'table-hover' , 'text-sm'];
|
||||
clip_complete?: boolean;
|
||||
// export let upload_complete: boolean = false;
|
||||
submit_status?: null | string;
|
||||
// hosted_file_id_li?: string[];
|
||||
// hosted_file_obj_li?: any[];
|
||||
hosted_file_obj_kv?: key_val;
|
||||
video_clip_file_kv?: key_val;
|
||||
}
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
// Expecting these for link_to_type: 'event', 'event_location', 'archive_content', etc
|
||||
link_to_type: string;
|
||||
link_to_id: string;
|
||||
// export let accept: string = 'audio/*, image/*, video/*, .bak, .cfg, .css, .csv, .doc, .docx, .gz, .htm, .html, .ini, .iso, .j2, .json, .key, .keynote, .md, .pdf, .ppt, .pptx, .rar, .rtf, .sql, .svelte, ttf, .txt, .xls, .xlsx, .xz, .zip, .bin, .dmg, .exe, .js, .msi, .php, .py, .sh';
|
||||
class_li_default?: string;
|
||||
class_li?: string;
|
||||
// export let table_class_li: string[] = ['table', 'table-sm', 'table-striped', 'table-hover' , 'text-sm'];
|
||||
clip_complete?: boolean;
|
||||
// export let upload_complete: boolean = false;
|
||||
submit_status?: null | string;
|
||||
// hosted_file_id_li?: string[];
|
||||
// hosted_file_obj_li?: any[];
|
||||
hosted_file_obj_kv?: key_val;
|
||||
video_clip_file_kv?: key_val;
|
||||
}
|
||||
|
||||
let {
|
||||
log_lvl = $bindable(0),
|
||||
link_to_type = $bindable(),
|
||||
link_to_id = $bindable(),
|
||||
class_li_default = 'flex flex-col gap-1 items-center justify-center w-full max-w-2xl mx-auto my-1',
|
||||
class_li = $bindable(''),
|
||||
clip_complete = $bindable(false),
|
||||
submit_status = $bindable(null),
|
||||
// hosted_file_id_li = [],
|
||||
// hosted_file_obj_li = [],
|
||||
hosted_file_obj_kv = $bindable({}),
|
||||
video_clip_file_kv = $bindable({})
|
||||
}: Props = $props();
|
||||
let {
|
||||
log_lvl = $bindable(0),
|
||||
link_to_type = $bindable(),
|
||||
link_to_id = $bindable(),
|
||||
class_li_default = 'flex flex-col gap-1 items-center justify-center w-full max-w-2xl mx-auto my-1',
|
||||
class_li = $bindable(''),
|
||||
clip_complete = $bindable(false),
|
||||
submit_status = $bindable(null),
|
||||
// hosted_file_id_li = [],
|
||||
// hosted_file_obj_li = [],
|
||||
hosted_file_obj_kv = $bindable({}),
|
||||
video_clip_file_kv = $bindable({})
|
||||
}: Props = $props();
|
||||
|
||||
// Local Variables
|
||||
let task_id = link_to_id;
|
||||
// let input_file_list: any = null;
|
||||
let ae_promises: key_val = $state({});
|
||||
// let ae_promises_clipping: key_val = {};
|
||||
// let ae_triggers: key_val = {};
|
||||
// Local Variables
|
||||
let task_id = link_to_id;
|
||||
// let input_file_list: any = null;
|
||||
let ae_promises: key_val = $state({});
|
||||
// let ae_promises_clipping: key_val = {};
|
||||
// let ae_triggers: key_val = {};
|
||||
|
||||
// let input_element_id = 'ae_comp__hosted_files_upload__input';
|
||||
// let input_element_id = 'ae_comp__hosted_files_upload__input';
|
||||
|
||||
// let form_kv: key_val = {
|
||||
// start_time: null,
|
||||
// end_time: null,
|
||||
// reencode: null,
|
||||
// video_file: null,
|
||||
// };
|
||||
// let download_clip_src: string;
|
||||
// let download_clip_filename: string;
|
||||
// let form_kv: key_val = {
|
||||
// start_time: null,
|
||||
// end_time: null,
|
||||
// reencode: null,
|
||||
// video_file: null,
|
||||
// };
|
||||
// let download_clip_src: string;
|
||||
// let download_clip_filename: string;
|
||||
|
||||
$ae_sess.files.obj = {
|
||||
obj: null
|
||||
$ae_sess.files.obj = {
|
||||
obj: null
|
||||
};
|
||||
|
||||
// *** Functions and Logic
|
||||
function prevent_default<T extends Event>(fn: (event: T) => void) {
|
||||
return function (event: T) {
|
||||
event.preventDefault();
|
||||
fn(event);
|
||||
};
|
||||
}
|
||||
|
||||
function handle_clip_video(event: Event) {
|
||||
console.log('*** handle_clip_video() ***');
|
||||
|
||||
submit_status = 'clipping';
|
||||
clip_complete = false;
|
||||
|
||||
const form = event.target as HTMLFormElement;
|
||||
const formData = new FormData(form);
|
||||
|
||||
let hosted_file_id = formData.get('hosted_file_id') as string;
|
||||
let start_time = formData.get('start_time') as string;
|
||||
let end_time = formData.get('end_time') as string;
|
||||
let reencode = formData.get('reencode') as string;
|
||||
let scale_down = formData.get('scale_down') as string;
|
||||
let new_filename = formData.get('new_filename') as string;
|
||||
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id] = {};
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id].submit_status = 'clipping';
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id].clip_complete = false;
|
||||
|
||||
// $ae_sess.files.disable_submit__hosted_file_obj = true;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id] = {};
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].submit_status = 'clipping';
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].start_time = start_time;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].end_time = end_time;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].reencode = reencode;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].scale_down = scale_down;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].new_filename = new_filename;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].clip_complete = false;
|
||||
|
||||
let endpoint = `/v3/action/hosted_file/${hosted_file_id}/clip_video`;
|
||||
|
||||
let params = {
|
||||
link_to_type: link_to_type,
|
||||
link_to_id: link_to_id,
|
||||
filename_no_ext: new_filename.replace('.mp4', ''),
|
||||
from_type: 'mp4', // Video file type being converted
|
||||
to_type: 'mp4', // Video file type to convert to
|
||||
start_time: start_time,
|
||||
end_time: end_time,
|
||||
reencode: reencode,
|
||||
scale_down: scale_down
|
||||
};
|
||||
|
||||
// *** Functions and Logic
|
||||
function prevent_default<T extends Event>(fn: (event: T) => void) {
|
||||
return function (event: T) {
|
||||
event.preventDefault();
|
||||
fn(event);
|
||||
};
|
||||
}
|
||||
ae_promises[hosted_file_id] = {};
|
||||
// .convert__hosted_file_obj
|
||||
ae_promises[hosted_file_id] = api
|
||||
.get_object({
|
||||
api_cfg: $ae_api,
|
||||
endpoint: endpoint,
|
||||
params: params,
|
||||
timeout: 300000, // 5 minutes
|
||||
// return_blob: true,
|
||||
// filename: event.target.new_filename.value,
|
||||
// auto_download: false,
|
||||
task_id: task_id,
|
||||
log_lvl: log_lvl
|
||||
})
|
||||
.then(function (result) {
|
||||
console.log(result);
|
||||
|
||||
function handle_clip_video(event: Event) {
|
||||
console.log('*** handle_clip_video() ***');
|
||||
video_clip_file_kv[result.hosted_file_id] = {};
|
||||
video_clip_file_kv[result.hosted_file_id] = result;
|
||||
|
||||
submit_status = 'clipping';
|
||||
clip_complete = false;
|
||||
// $ae_loc.files.video_clip_file_kv[result.hosted_file_id] = {};
|
||||
// $ae_loc.files.video_clip_file_kv[result.hosted_file_id] = result;
|
||||
|
||||
const form = event.target as HTMLFormElement;
|
||||
const formData = new FormData(form);
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id].submit_status =
|
||||
'clipped';
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id].clip_complete =
|
||||
true;
|
||||
|
||||
let hosted_file_id = formData.get('hosted_file_id') as string;
|
||||
let start_time = formData.get('start_time') as string;
|
||||
let end_time = formData.get('end_time') as string;
|
||||
let reencode = formData.get('reencode') as string;
|
||||
let scale_down = formData.get('scale_down') as string;
|
||||
let new_filename = formData.get('new_filename') as string;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].submit_status =
|
||||
'clipped';
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].clip_complete =
|
||||
true;
|
||||
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id] = {};
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id].submit_status =
|
||||
'clipping';
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id].clip_complete = false;
|
||||
submit_status = 'clipped';
|
||||
clip_complete = true;
|
||||
|
||||
// $ae_sess.files.disable_submit__hosted_file_obj = true;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id] = {};
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].submit_status =
|
||||
'clipping';
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].start_time = start_time;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].end_time = end_time;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].reencode = reencode;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].scale_down = scale_down;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].new_filename =
|
||||
new_filename;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].clip_complete = false;
|
||||
// let file_blob = new Blob([result.data]);
|
||||
// // console.log(file_blob);
|
||||
// let file_obj_url = window.URL.createObjectURL(file_blob); // The img src
|
||||
// // const url = window.URL.createObjectURL(new Blob([result.data]));
|
||||
// download_clip_src = file_obj_url;
|
||||
// // download_filename = file_obj_url;
|
||||
|
||||
let endpoint = `/hosted_file/${hosted_file_id}/clip_video`;
|
||||
|
||||
let params = {
|
||||
link_to_type: link_to_type,
|
||||
link_to_id: link_to_id,
|
||||
filename_no_ext: new_filename.replace('.mp4', ''),
|
||||
from_type: 'mp4', // Video file type being converted
|
||||
to_type: 'mp4', // Video file type to convert to
|
||||
start_time: start_time,
|
||||
end_time: end_time,
|
||||
reencode: reencode,
|
||||
scale_down: scale_down
|
||||
};
|
||||
|
||||
ae_promises[hosted_file_id] = {};
|
||||
// .convert__hosted_file_obj
|
||||
ae_promises[hosted_file_id] = api
|
||||
.get_object({
|
||||
api_cfg: $ae_api,
|
||||
endpoint: endpoint,
|
||||
params: params,
|
||||
timeout: 300000, // 5 minutes
|
||||
// return_blob: true,
|
||||
// filename: event.target.new_filename.value,
|
||||
// auto_download: false,
|
||||
task_id: task_id,
|
||||
log_lvl: log_lvl
|
||||
})
|
||||
.then(function (result) {
|
||||
console.log(result);
|
||||
|
||||
video_clip_file_kv[result.hosted_file_id] = {};
|
||||
video_clip_file_kv[result.hosted_file_id] = result;
|
||||
|
||||
// $ae_loc.files.video_clip_file_kv[result.hosted_file_id] = {};
|
||||
// $ae_loc.files.video_clip_file_kv[result.hosted_file_id] = result;
|
||||
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id].submit_status =
|
||||
'clipped';
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id].clip_complete =
|
||||
true;
|
||||
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].submit_status =
|
||||
'clipped';
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].clip_complete =
|
||||
true;
|
||||
|
||||
submit_status = 'clipped';
|
||||
clip_complete = true;
|
||||
|
||||
// let file_blob = new Blob([result.data]);
|
||||
// // console.log(file_blob);
|
||||
// let file_obj_url = window.URL.createObjectURL(file_blob); // The img src
|
||||
// // const url = window.URL.createObjectURL(new Blob([result.data]));
|
||||
// download_clip_src = file_obj_url;
|
||||
// // download_filename = file_obj_url;
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<section class="{class_li_default} {class_li}">
|
||||
@@ -191,11 +194,11 @@
|
||||
</h3>
|
||||
|
||||
{#each Object.entries(hosted_file_obj_kv) as [hosted_file_id, hosted_file_obj] (hosted_file_id)}
|
||||
<div class="border border-surface-500/20 rounded-lg p-2 m-2 preset-tonal-surface">
|
||||
<div
|
||||
class="border-surface-500/20 preset-tonal-surface m-2 rounded-lg border p-2">
|
||||
<!-- Download Button (Standardized) -->
|
||||
<div
|
||||
class="flex flex-row flex-wrap gap-1 justify-center items-center w-full"
|
||||
>
|
||||
class="flex w-full flex-row flex-wrap items-center justify-center gap-1">
|
||||
<!-- Remove from uploaded file kv list -->
|
||||
<button
|
||||
type="button"
|
||||
@@ -219,8 +222,7 @@
|
||||
);
|
||||
}}
|
||||
class="btn btn-sm preset-tonal-warning hover:preset-filled-warning-500"
|
||||
title={`Remove this file from list of videos:\n${hosted_file_obj.filename}\n[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}... Hosted ID: ${hosted_file_obj.hosted_file_id}`}
|
||||
>
|
||||
title={`Remove this file from list of videos:\n${hosted_file_obj.filename}\n[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}... Hosted ID: ${hosted_file_obj.hosted_file_id}`}>
|
||||
<MinusCircle size="1em" class="m-1" />
|
||||
<span class="">Remove</span>
|
||||
</button>
|
||||
@@ -234,60 +236,49 @@
|
||||
variant="tonal"
|
||||
classes="novi_btn btn-sm lg:btn-md min-w-72 lg:min-w-96 !justify-start"
|
||||
show_divider={true}
|
||||
max_filename={30}
|
||||
/>
|
||||
max_filename={30} />
|
||||
</div>
|
||||
<span
|
||||
>{ae_util.shorten_filename({
|
||||
filename: hosted_file_obj?.filename,
|
||||
max_length: 30
|
||||
})}</span
|
||||
>
|
||||
})}</span>
|
||||
<span>
|
||||
<span class="text-sm font-bold"> File ID: </span>
|
||||
{hosted_file_obj.hosted_file_id}</span
|
||||
>
|
||||
{hosted_file_obj.hosted_file_id}</span>
|
||||
<span>
|
||||
<span class="text-sm font-bold"> Type: </span>
|
||||
{hosted_file_obj.extension}</span
|
||||
>
|
||||
{hosted_file_obj.extension}</span>
|
||||
<!-- <span>{hosted_file_obj.filename}</span> -->
|
||||
</div>
|
||||
|
||||
<form
|
||||
onsubmit={prevent_default(handle_clip_video)}
|
||||
class="{class_li_default} {class_li}"
|
||||
>
|
||||
class="{class_li_default} {class_li}">
|
||||
<!-- {$ae_sess?.files[hosted_file_obj?.hosted_file_id ?? 'obj'].submit_status ?? 'not set'} -->
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="hosted_file_id"
|
||||
value={hosted_file_obj.hosted_file_id}
|
||||
/>
|
||||
value={hosted_file_obj.hosted_file_id} />
|
||||
|
||||
<div
|
||||
class="flex flex-row gap-1 justify-center items-center w-full"
|
||||
>
|
||||
<span class="text-xs font-bold w-32">New Filename:</span>
|
||||
class="flex w-full flex-row items-center justify-center gap-1">
|
||||
<span class="w-32 text-xs font-bold">New Filename:</span>
|
||||
<input
|
||||
type="text"
|
||||
class="input w-full text-sm variant-filled-surface"
|
||||
class="input variant-filled-surface w-full text-sm"
|
||||
name="new_filename"
|
||||
value={hosted_file_obj.filename}
|
||||
/>
|
||||
value={hosted_file_obj.filename} />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="max-w-(--breakpoint-sm) flex flex-row gap-1 justify-center items-center w-full"
|
||||
>
|
||||
class="flex w-full max-w-(--breakpoint-sm) flex-row items-center justify-center gap-1">
|
||||
<label
|
||||
class="label w-48"
|
||||
title="The start time of the clip. This is the time in the video where the clip will start. You may need to subtract a few seconds to get the exact start time."
|
||||
>
|
||||
title="The start time of the clip. This is the time in the video where the clip will start. You may need to subtract a few seconds to get the exact start time.">
|
||||
<span class="text-xs font-bold"
|
||||
>Start time (HH:MM:SS)</span
|
||||
>
|
||||
>Start time (HH:MM:SS)</span>
|
||||
<input
|
||||
type="text"
|
||||
name="start_time"
|
||||
@@ -300,17 +291,14 @@
|
||||
].start_time
|
||||
: '00:00:00'}
|
||||
placeholder="HH:MM:SS (00:01:30)"
|
||||
class="input w-32 variant-filled-surface"
|
||||
/>
|
||||
class="input variant-filled-surface w-32" />
|
||||
</label>
|
||||
|
||||
<label
|
||||
class="label w-48"
|
||||
title="The end time of the clip. This is the time in the video where the clip will end. You may need to add a few seconds to get the exact end time."
|
||||
>
|
||||
title="The end time of the clip. This is the time in the video where the clip will end. You may need to add a few seconds to get the exact end time.">
|
||||
<span class="text-xs font-bold"
|
||||
>End time (HH:MM:SS)</span
|
||||
>
|
||||
>End time (HH:MM:SS)</span>
|
||||
<input
|
||||
type="text"
|
||||
name="end_time"
|
||||
@@ -323,14 +311,12 @@
|
||||
].end_time
|
||||
: '00:45:59'}
|
||||
placeholder="HH:MM:SS (01:05:25)"
|
||||
class="input w-32 variant-filled-surface"
|
||||
/>
|
||||
class="input variant-filled-surface w-32" />
|
||||
</label>
|
||||
|
||||
<span
|
||||
class="flex flex-col gap-1 items-center justify-center"
|
||||
title="Re-encode the video file? This does cause some minor quality loss. Re-encoding is useful if the audio or video seems to be chopped off at the beginning or end of the clip. It can also help with partially corrupted files."
|
||||
>
|
||||
class="flex flex-col items-center justify-center gap-1"
|
||||
title="Re-encode the video file? This does cause some minor quality loss. Re-encoding is useful if the audio or video seems to be chopped off at the beginning or end of the clip. It can also help with partially corrupted files.">
|
||||
<span class="text-xs font-bold"> Re-encode? </span>
|
||||
<label class="inline-block">
|
||||
<input
|
||||
@@ -338,8 +324,7 @@
|
||||
name="reencode"
|
||||
value="true"
|
||||
class="radio"
|
||||
checked
|
||||
/>
|
||||
checked />
|
||||
True
|
||||
</label>
|
||||
<label class="inline-block">
|
||||
@@ -347,16 +332,14 @@
|
||||
type="radio"
|
||||
name="reencode"
|
||||
value="false"
|
||||
class="radio"
|
||||
/>
|
||||
class="radio" />
|
||||
False
|
||||
</label>
|
||||
</span>
|
||||
|
||||
<span
|
||||
class="flex flex-col gap-1 items-center justify-center"
|
||||
title="Scale the video file down to 1920x1080? This does cause some minor quality loss. Re-encoding is useful if the audio or video seems to be chopped off at the beginning or end of the clip. It can also help with partially corrupted files."
|
||||
>
|
||||
class="flex flex-col items-center justify-center gap-1"
|
||||
title="Scale the video file down to 1920x1080? This does cause some minor quality loss. Re-encoding is useful if the audio or video seems to be chopped off at the beginning or end of the clip. It can also help with partially corrupted files.">
|
||||
<span class="text-xs font-bold"> Scale down? </span>
|
||||
<label class="inline-block">
|
||||
<input
|
||||
@@ -364,8 +347,7 @@
|
||||
name="scale_down"
|
||||
value="true"
|
||||
class="radio"
|
||||
checked
|
||||
/>
|
||||
checked />
|
||||
True
|
||||
</label>
|
||||
<label class="inline-block">
|
||||
@@ -373,8 +355,7 @@
|
||||
type="radio"
|
||||
name="scale_down"
|
||||
value="false"
|
||||
class="radio"
|
||||
/>
|
||||
class="radio" />
|
||||
False
|
||||
</label>
|
||||
</span>
|
||||
@@ -382,9 +363,8 @@
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-lg btn-primary preset-tonal-primary border border-primary-500 hover:preset-filled-primary-500 transition-colors"
|
||||
disabled={submit_status == 'clipping'}
|
||||
>
|
||||
class="btn btn-lg btn-primary preset-tonal-primary border-primary-500 hover:preset-filled-primary-500 border transition-colors"
|
||||
disabled={submit_status == 'clipping'}>
|
||||
<!-- {#await ae_promises[hosted_file_id]} -->
|
||||
{#if $ae_loc.files.processed_file_kv[hosted_file_id] && $ae_loc.files.processed_file_kv[hosted_file_id].submit_status == 'clipping'}
|
||||
<LoaderCircle size="1em" class="m-1 animate-spin" />
|
||||
@@ -407,8 +387,7 @@
|
||||
{#await ae_promises[hosted_file_id]}
|
||||
<LoaderCircle size="1em" class="m-1 animate-spin" />
|
||||
<span class="highlight"
|
||||
>Processing... This may take a few minutes.</span
|
||||
>
|
||||
>Processing... This may take a few minutes.</span>
|
||||
{:then}
|
||||
{#if ae_promises[hosted_file_id]}
|
||||
<Download size="1em" /> Ready to download below!
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
<script lang="ts">
|
||||
// Imports
|
||||
// Import components and elements
|
||||
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';
|
||||
// Imports
|
||||
// Import components and elements
|
||||
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';
|
||||
|
||||
// Import storage, functions, and libraries
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
// Import storage, functions, and libraries
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
|
||||
import { api } from '$lib/api/api';
|
||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||
import {
|
||||
ae_snip,
|
||||
ae_loc,
|
||||
ae_sess,
|
||||
ae_api,
|
||||
ae_trig,
|
||||
slct,
|
||||
slct_trigger
|
||||
} from '$lib/stores/ae_stores';
|
||||
import { api } from '$lib/api/api';
|
||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||
import {
|
||||
ae_snip,
|
||||
ae_loc,
|
||||
ae_sess,
|
||||
ae_api,
|
||||
ae_trig,
|
||||
slct,
|
||||
slct_trigger
|
||||
} from '$lib/stores/ae_stores';
|
||||
|
||||
// Exports
|
||||
// Exports
|
||||
|
||||
// export let hosted_file_id_li: string[] = [];
|
||||
// export let hosted_file_obj_li: any[] = [];
|
||||
// export let hosted_file_id_li: string[] = [];
|
||||
// export let hosted_file_obj_li: any[] = [];
|
||||
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
// export let hosted_file_obj_kv: key_val = {};
|
||||
video_clip_file_kv?: key_val;
|
||||
class_li_default?: string;
|
||||
class_li?: string;
|
||||
link_to_type: string;
|
||||
link_to_id: string;
|
||||
}
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
// export let hosted_file_obj_kv: key_val = {};
|
||||
video_clip_file_kv?: key_val;
|
||||
class_li_default?: string;
|
||||
class_li?: string;
|
||||
link_to_type: string;
|
||||
link_to_id: string;
|
||||
}
|
||||
|
||||
let {
|
||||
log_lvl = 0,
|
||||
video_clip_file_kv = $bindable({}),
|
||||
class_li_default = 'flex flex-row flex-wrap gap-2 items-center justify-center w-full max-w-2xl p-2 mx-auto my-1 border border-surface-500/20 rounded-lg preset-tonal-surface',
|
||||
class_li = '',
|
||||
link_to_type,
|
||||
link_to_id
|
||||
}: Props = $props();
|
||||
let {
|
||||
log_lvl = 0,
|
||||
video_clip_file_kv = $bindable({}),
|
||||
class_li_default = 'flex flex-row flex-wrap gap-2 items-center justify-center w-full max-w-2xl p-2 mx-auto my-1 border border-surface-500/20 rounded-lg preset-tonal-surface',
|
||||
class_li = '',
|
||||
link_to_type,
|
||||
link_to_id
|
||||
}: Props = $props();
|
||||
|
||||
let ae_promises: key_val = $state({});
|
||||
let ae_promises: key_val = $state({});
|
||||
</script>
|
||||
|
||||
<h3 class="h3">{Object.entries(video_clip_file_kv).length}× files clipped</h3>
|
||||
@@ -54,7 +54,6 @@
|
||||
max_filename={30}
|
||||
classes="btn btn-sm lg:btn-md preset-tonal-primary hover:preset-filled-primary-500 min-w-72 lg:min-w-96"
|
||||
linked_to_type={link_to_type}
|
||||
linked_to_id={link_to_id}
|
||||
/>
|
||||
linked_to_id={link_to_id} />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -1,243 +1,275 @@
|
||||
<script lang="ts">
|
||||
// *** Import Svelte specific
|
||||
import * as Lucide from 'lucide-svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
// *** Import Svelte specific
|
||||
import * as Lucide from 'lucide-svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
// *** Import Aether specific variables and functions
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||
import { download_ae_obj_id__hosted_file } from '$lib/ae_core/core__hosted_files';
|
||||
import {
|
||||
ae_loc,
|
||||
ae_sess,
|
||||
ae_api
|
||||
} from '$lib/stores/ae_stores';
|
||||
// *** Import Aether specific variables and functions
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||
import { download_ae_obj_id__hosted_file } from '$lib/ae_core/core__hosted_files';
|
||||
import { ae_loc, ae_sess, ae_api } from '$lib/stores/ae_stores';
|
||||
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
hosted_file_id: null | string;
|
||||
hosted_file_obj: null | key_val;
|
||||
filename?: null | string;
|
||||
max_filename?: number;
|
||||
auto_download?: boolean;
|
||||
linked_to_type?: null | string;
|
||||
linked_to_id?: null | string;
|
||||
download_complete?: null | boolean;
|
||||
download_percent?: number;
|
||||
download_status_msg?: string;
|
||||
variant?: 'tonal' | 'filled' | 'outline' | 'ghost';
|
||||
color?: 'primary' | 'secondary' | 'tertiary' | 'success' | 'warning' | 'error' | 'surface';
|
||||
show_divider?: boolean;
|
||||
show_direct_download?: boolean;
|
||||
require_auth?: boolean;
|
||||
classes?: string;
|
||||
click?: () => void | Promise<any>;
|
||||
label?: import('svelte').Snippet;
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
hosted_file_id: null | string;
|
||||
hosted_file_obj: null | key_val;
|
||||
filename?: null | string;
|
||||
max_filename?: number;
|
||||
auto_download?: boolean;
|
||||
linked_to_type?: null | string;
|
||||
linked_to_id?: null | string;
|
||||
download_complete?: null | boolean;
|
||||
download_percent?: number;
|
||||
download_status_msg?: string;
|
||||
variant?: 'tonal' | 'filled' | 'outline' | 'ghost';
|
||||
color?:
|
||||
| 'primary'
|
||||
| 'secondary'
|
||||
| 'tertiary'
|
||||
| 'success'
|
||||
| 'warning'
|
||||
| 'error'
|
||||
| 'surface';
|
||||
show_divider?: boolean;
|
||||
show_direct_download?: boolean;
|
||||
require_auth?: boolean;
|
||||
classes?: string;
|
||||
click?: () => void | Promise<any>;
|
||||
label?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let {
|
||||
log_lvl = 0,
|
||||
hosted_file_id,
|
||||
hosted_file_obj,
|
||||
filename = $bindable(null),
|
||||
max_filename = $bindable(30),
|
||||
auto_download = true,
|
||||
linked_to_type = $bindable(null),
|
||||
linked_to_id = $bindable(null),
|
||||
download_complete = $bindable(),
|
||||
download_percent = $bindable(),
|
||||
download_status_msg = $bindable('Not started'),
|
||||
variant = 'tonal',
|
||||
color = 'primary',
|
||||
show_divider = true,
|
||||
show_direct_download = false,
|
||||
require_auth = true,
|
||||
classes = '',
|
||||
click,
|
||||
label
|
||||
}: Props = $props();
|
||||
|
||||
// Map variant/color to classes using literal strings so Tailwind can find them
|
||||
const color_map: Record<string, Record<string, string>> = {
|
||||
primary: {
|
||||
tonal: 'preset-tonal-primary border border-primary-500/30 hover:preset-filled-primary-500',
|
||||
filled: 'preset-filled-primary-500 hover:preset-filled-primary-600',
|
||||
outline: 'border border-primary-500 hover:preset-tonal-primary',
|
||||
ghost: 'hover:preset-tonal-primary'
|
||||
},
|
||||
secondary: {
|
||||
tonal: 'preset-tonal-secondary border border-secondary-500/30 hover:preset-filled-secondary-500',
|
||||
filled: 'preset-filled-secondary-500 hover:preset-filled-secondary-600',
|
||||
outline: 'border border-secondary-500 hover:preset-tonal-secondary',
|
||||
ghost: 'hover:preset-tonal-secondary'
|
||||
},
|
||||
tertiary: {
|
||||
tonal: 'preset-tonal-tertiary border border-tertiary-500/30 hover:preset-filled-tertiary-500',
|
||||
filled: 'preset-filled-tertiary-500 hover:preset-filled-tertiary-600',
|
||||
outline: 'border border-tertiary-500 hover:preset-tonal-tertiary',
|
||||
ghost: 'hover:preset-tonal-tertiary'
|
||||
},
|
||||
success: {
|
||||
tonal: 'preset-tonal-success border border-success-500/30 hover:preset-filled-success-500',
|
||||
filled: 'preset-filled-success-500 hover:preset-filled-success-600',
|
||||
outline: 'border border-success-500 hover:preset-tonal-success',
|
||||
ghost: 'hover:preset-tonal-success'
|
||||
},
|
||||
warning: {
|
||||
tonal: 'preset-tonal-warning border border-warning-500/30 hover:preset-filled-warning-500',
|
||||
filled: 'preset-filled-warning-500 hover:preset-filled-warning-600',
|
||||
outline: 'border border-warning-500 hover:preset-tonal-warning',
|
||||
ghost: 'hover:preset-tonal-warning'
|
||||
},
|
||||
error: {
|
||||
tonal: 'preset-tonal-error border border-error-500/30 hover:preset-filled-error-500',
|
||||
filled: 'preset-filled-error-500 hover:preset-filled-error-600',
|
||||
outline: 'border border-error-500 hover:preset-tonal-error',
|
||||
ghost: 'hover:preset-tonal-error'
|
||||
},
|
||||
surface: {
|
||||
tonal: 'preset-tonal-surface border border-surface-500/30 hover:preset-filled-surface-500',
|
||||
filled: 'preset-filled-surface-500 hover:preset-filled-surface-600',
|
||||
outline: 'border border-surface-500 hover:preset-tonal-surface',
|
||||
ghost: 'hover:preset-tonal-surface'
|
||||
}
|
||||
};
|
||||
|
||||
let variant_classes = $derived.by(() => {
|
||||
const base =
|
||||
'btn btn-sm lg:btn-md min-w-48 transition-all overflow-hidden px-3';
|
||||
const style = color_map[color]?.[variant] || color_map.primary.tonal;
|
||||
return `${base} ${style} ${classes}`.trim();
|
||||
});
|
||||
|
||||
let show_filename_view = $state(true);
|
||||
let status_interval: any;
|
||||
|
||||
$effect(() => {
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`ae_comp__hosted_files_download_button.svelte hosted_file_id=${hosted_file_id}`,
|
||||
hosted_file_obj
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
let ae_promises: key_val = $state({});
|
||||
|
||||
$effect(() => {
|
||||
const file_id =
|
||||
hosted_file_obj?.id ||
|
||||
hosted_file_obj?.hosted_file_id ||
|
||||
hosted_file_id;
|
||||
if (file_id && $ae_sess?.api_download_kv[file_id]?.percent_completed) {
|
||||
download_percent = $ae_sess.api_download_kv[file_id].percent_completed;
|
||||
}
|
||||
});
|
||||
|
||||
// Reactive timer to alternate views during active download
|
||||
$effect(() => {
|
||||
const file_id =
|
||||
hosted_file_obj?.id ||
|
||||
hosted_file_obj?.hosted_file_id ||
|
||||
hosted_file_id;
|
||||
const is_actively_downloading =
|
||||
ae_promises[file_id] && download_complete === undefined;
|
||||
|
||||
if (is_actively_downloading) {
|
||||
if (!status_interval) {
|
||||
status_interval = setInterval(() => {
|
||||
show_filename_view = !show_filename_view;
|
||||
}, 3000);
|
||||
}
|
||||
} else {
|
||||
if (status_interval) {
|
||||
clearInterval(status_interval);
|
||||
status_interval = null;
|
||||
}
|
||||
show_filename_view = true; // Default view when not downloading
|
||||
}
|
||||
|
||||
let {
|
||||
log_lvl = 0,
|
||||
hosted_file_id,
|
||||
hosted_file_obj,
|
||||
filename = $bindable(null),
|
||||
max_filename = $bindable(30),
|
||||
auto_download = true,
|
||||
linked_to_type = $bindable(null),
|
||||
linked_to_id = $bindable(null),
|
||||
download_complete = $bindable(),
|
||||
download_percent = $bindable(),
|
||||
download_status_msg = $bindable('Not started'),
|
||||
variant = 'tonal',
|
||||
color = 'primary',
|
||||
show_divider = true,
|
||||
show_direct_download = false,
|
||||
require_auth = true,
|
||||
classes = '',
|
||||
click,
|
||||
label
|
||||
}: Props = $props();
|
||||
|
||||
// Map variant/color to classes using literal strings so Tailwind can find them
|
||||
const color_map: Record<string, Record<string, string>> = {
|
||||
primary: {
|
||||
tonal: 'preset-tonal-primary border border-primary-500/30 hover:preset-filled-primary-500',
|
||||
filled: 'preset-filled-primary-500 hover:preset-filled-primary-600',
|
||||
outline: 'border border-primary-500 hover:preset-tonal-primary',
|
||||
ghost: 'hover:preset-tonal-primary'
|
||||
},
|
||||
secondary: {
|
||||
tonal: 'preset-tonal-secondary border border-secondary-500/30 hover:preset-filled-secondary-500',
|
||||
filled: 'preset-filled-secondary-500 hover:preset-filled-secondary-600',
|
||||
outline: 'border border-secondary-500 hover:preset-tonal-secondary',
|
||||
ghost: 'hover:preset-tonal-secondary'
|
||||
},
|
||||
tertiary: {
|
||||
tonal: 'preset-tonal-tertiary border border-tertiary-500/30 hover:preset-filled-tertiary-500',
|
||||
filled: 'preset-filled-tertiary-500 hover:preset-filled-tertiary-600',
|
||||
outline: 'border border-tertiary-500 hover:preset-tonal-tertiary',
|
||||
ghost: 'hover:preset-tonal-tertiary'
|
||||
},
|
||||
success: {
|
||||
tonal: 'preset-tonal-success border border-success-500/30 hover:preset-filled-success-500',
|
||||
filled: 'preset-filled-success-500 hover:preset-filled-success-600',
|
||||
outline: 'border border-success-500 hover:preset-tonal-success',
|
||||
ghost: 'hover:preset-tonal-success'
|
||||
},
|
||||
warning: {
|
||||
tonal: 'preset-tonal-warning border border-warning-500/30 hover:preset-filled-warning-500',
|
||||
filled: 'preset-filled-warning-500 hover:preset-filled-warning-600',
|
||||
outline: 'border border-warning-500 hover:preset-tonal-warning',
|
||||
ghost: 'hover:preset-tonal-warning'
|
||||
},
|
||||
error: {
|
||||
tonal: 'preset-tonal-error border border-error-500/30 hover:preset-filled-error-500',
|
||||
filled: 'preset-filled-error-500 hover:preset-filled-error-600',
|
||||
outline: 'border border-error-500 hover:preset-tonal-error',
|
||||
ghost: 'hover:preset-tonal-error'
|
||||
},
|
||||
surface: {
|
||||
tonal: 'preset-tonal-surface border border-surface-500/30 hover:preset-filled-surface-500',
|
||||
filled: 'preset-filled-surface-500 hover:preset-filled-surface-600',
|
||||
outline: 'border border-surface-500 hover:preset-tonal-surface',
|
||||
ghost: 'hover:preset-tonal-surface'
|
||||
return () => {
|
||||
if (status_interval) {
|
||||
clearInterval(status_interval);
|
||||
status_interval = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
let variant_classes = $derived.by(() => {
|
||||
const base = 'btn btn-sm lg:btn-md min-w-48 transition-all overflow-hidden px-3';
|
||||
const style = color_map[color]?.[variant] || color_map.primary.tonal;
|
||||
return `${base} ${style} ${classes}`.trim();
|
||||
});
|
||||
|
||||
let show_filename_view = $state(true);
|
||||
let status_interval: any;
|
||||
|
||||
$effect(() => {
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`ae_comp__hosted_files_download_button.svelte hosted_file_id=${hosted_file_id}`,
|
||||
hosted_file_obj
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
let ae_promises: key_val = $state({});
|
||||
|
||||
$effect(() => {
|
||||
const file_id = hosted_file_obj?.id || hosted_file_obj?.hosted_file_id || hosted_file_id;
|
||||
if (file_id && $ae_sess?.api_download_kv[file_id]?.percent_completed) {
|
||||
download_percent =
|
||||
$ae_sess.api_download_kv[file_id].percent_completed;
|
||||
}
|
||||
});
|
||||
|
||||
// Reactive timer to alternate views during active download
|
||||
$effect(() => {
|
||||
const file_id = hosted_file_obj?.id || hosted_file_obj?.hosted_file_id || hosted_file_id;
|
||||
const is_actively_downloading = ae_promises[file_id] && download_complete === undefined;
|
||||
|
||||
if (is_actively_downloading) {
|
||||
if (!status_interval) {
|
||||
status_interval = setInterval(() => {
|
||||
show_filename_view = !show_filename_view;
|
||||
}, 3000);
|
||||
}
|
||||
} else {
|
||||
if (status_interval) {
|
||||
clearInterval(status_interval);
|
||||
status_interval = null;
|
||||
}
|
||||
show_filename_view = true; // Default view when not downloading
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (status_interval) {
|
||||
clearInterval(status_interval);
|
||||
status_interval = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
let final_filename = $derived(filename ?? hosted_file_obj?.filename ?? 'unknown');
|
||||
let shortened_filename = $derived(ae_util.shorten_filename({
|
||||
let final_filename = $derived(
|
||||
filename ?? hosted_file_obj?.filename ?? 'unknown'
|
||||
);
|
||||
let shortened_filename = $derived(
|
||||
ae_util.shorten_filename({
|
||||
filename: final_filename,
|
||||
max_length: max_filename
|
||||
}));
|
||||
})
|
||||
);
|
||||
|
||||
let direct_download_url = $derived.by(() => {
|
||||
if (!show_direct_download || !hosted_file_obj) return '';
|
||||
// IMPORTANT: For Direct Link Mode, we MUST use the V3 Action endpoint to support Random String IDs.
|
||||
// Legacy endpoints often expect integer IDs and will return 404 for string IDs.
|
||||
const file_id = hosted_file_obj.event_file_id || hosted_file_obj.hosted_file_id || hosted_file_id;
|
||||
const obj_type_path = hosted_file_obj.event_file_id ? 'event_file' : 'hosted_file';
|
||||
return `${$ae_api.base_url}/v3/action/${obj_type_path}/${file_id}/download?filename=${ae_util.clean_filename(final_filename)}&key=${$ae_api.account_id}`;
|
||||
});
|
||||
let direct_download_url = $derived.by(() => {
|
||||
if (!show_direct_download || !hosted_file_obj) return '';
|
||||
// IMPORTANT: For Direct Link Mode, we MUST use the V3 Action endpoint to support Random String IDs.
|
||||
// Legacy endpoints often expect integer IDs and will return 404 for string IDs.
|
||||
const file_id =
|
||||
hosted_file_obj.event_file_id ||
|
||||
hosted_file_obj.hosted_file_id ||
|
||||
hosted_file_id;
|
||||
const obj_type_path = hosted_file_obj.event_file_id
|
||||
? 'event_file'
|
||||
: 'hosted_file';
|
||||
return `${$ae_api.base_url}/v3/action/${obj_type_path}/${file_id}/download?filename=${ae_util.clean_filename(final_filename)}&key=${$ae_api.account_id}`;
|
||||
});
|
||||
|
||||
async function handle_click() {
|
||||
const file_id = hosted_file_obj?.id || hosted_file_obj?.hosted_file_id || hosted_file_id;
|
||||
download_complete = undefined;
|
||||
download_status_msg = 'Downloading...';
|
||||
async function handle_click() {
|
||||
const file_id =
|
||||
hosted_file_obj?.id ||
|
||||
hosted_file_obj?.hosted_file_id ||
|
||||
hosted_file_id;
|
||||
download_complete = undefined;
|
||||
download_status_msg = 'Downloading...';
|
||||
|
||||
if (click) {
|
||||
const result = click();
|
||||
// If the override returns a promise, track it so the UI shows progress
|
||||
if (result instanceof Promise) {
|
||||
ae_promises[file_id] = result;
|
||||
}
|
||||
return;
|
||||
if (click) {
|
||||
const result = click();
|
||||
// If the override returns a promise, track it so the UI shows progress
|
||||
if (result instanceof Promise) {
|
||||
ae_promises[file_id] = result;
|
||||
}
|
||||
|
||||
ae_promises[file_id] = download_ae_obj_id__hosted_file({
|
||||
api_cfg: $ae_api,
|
||||
hosted_file_id: file_id,
|
||||
return_file: true,
|
||||
filename: final_filename,
|
||||
auto_download: auto_download,
|
||||
log_lvl: log_lvl
|
||||
})
|
||||
.then((result) => {
|
||||
if (result === null) {
|
||||
console.log('File not found (404)');
|
||||
download_complete = null;
|
||||
download_status_msg = 'File not found';
|
||||
} else if (result === false) {
|
||||
console.log(
|
||||
'Possible error with API server (check network and server status)'
|
||||
);
|
||||
download_complete = false;
|
||||
download_status_msg = 'Failed to download';
|
||||
} else {
|
||||
// console.log('File found and downloaded');
|
||||
download_complete = true;
|
||||
download_status_msg = 'File downloaded';
|
||||
}
|
||||
return result;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
ae_promises[file_id] = download_ae_obj_id__hosted_file({
|
||||
api_cfg: $ae_api,
|
||||
hosted_file_id: file_id,
|
||||
return_file: true,
|
||||
filename: final_filename,
|
||||
auto_download: auto_download,
|
||||
log_lvl: log_lvl
|
||||
}).then((result) => {
|
||||
if (result === null) {
|
||||
console.log('File not found (404)');
|
||||
download_complete = null;
|
||||
download_status_msg = 'File not found';
|
||||
} else if (result === false) {
|
||||
console.log(
|
||||
'Possible error with API server (check network and server status)'
|
||||
);
|
||||
download_complete = false;
|
||||
download_status_msg = 'Failed to download';
|
||||
} else {
|
||||
// console.log('File found and downloaded');
|
||||
download_complete = true;
|
||||
download_status_msg = 'File downloaded';
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{#snippet content()}
|
||||
{@const file_id = hosted_file_obj?.id || hosted_file_obj?.hosted_file_id || hosted_file_id}
|
||||
{@const file_id =
|
||||
hosted_file_obj?.id ||
|
||||
hosted_file_obj?.hosted_file_id ||
|
||||
hosted_file_id}
|
||||
{#await ae_promises[file_id]}
|
||||
<div class="flex items-center w-full min-h-[1.5rem]">
|
||||
<div class="flex min-h-[1.5rem] w-full items-center">
|
||||
<div
|
||||
class="flex items-center pr-2 shrink-0 {show_divider ? 'border-r border-surface-500/30 mr-2' : ''}"
|
||||
>
|
||||
class="flex shrink-0 items-center pr-2 {show_divider
|
||||
? 'border-surface-500/30 mr-2 border-r'
|
||||
: ''}">
|
||||
<Lucide.LoaderCircle class="animate-spin" size={18} />
|
||||
</div>
|
||||
<div class="grow relative text-left h-full">
|
||||
<div class="relative h-full grow text-left">
|
||||
{#if show_filename_view}
|
||||
<div in:fade={{ duration: 250 }} out:fade={{ duration: 250 }} class="flex items-center h-full">
|
||||
<div
|
||||
in:fade={{ duration: 250 }}
|
||||
out:fade={{ duration: 250 }}
|
||||
class="flex h-full items-center">
|
||||
<span class="truncate">
|
||||
{shortened_filename}
|
||||
</span>
|
||||
</div>
|
||||
{:else}
|
||||
<div in:fade={{ duration: 250 }} out:fade={{ duration: 250 }} class="absolute inset-0 flex items-center h-full">
|
||||
<div
|
||||
in:fade={{ duration: 250 }}
|
||||
out:fade={{ duration: 250 }}
|
||||
class="absolute inset-0 flex h-full items-center">
|
||||
<span class="font-bold whitespace-nowrap">
|
||||
Downloading:
|
||||
{#if $ae_sess.api_download_kv[file_id]}
|
||||
{$ae_sess.api_download_kv[file_id].percent_completed}%
|
||||
{$ae_sess.api_download_kv[file_id]
|
||||
.percent_completed}%
|
||||
{:else}
|
||||
...
|
||||
{/if}
|
||||
@@ -250,18 +282,22 @@
|
||||
{#if label}
|
||||
{@render label()}
|
||||
{:else}
|
||||
{@const IconComp = ae_util.file_extension_icon_lucide(hosted_file_obj?.extension)}
|
||||
<div class="flex items-center w-full">
|
||||
{@const IconComp = ae_util.file_extension_icon_lucide(
|
||||
hosted_file_obj?.extension
|
||||
)}
|
||||
<div class="flex w-full items-center">
|
||||
<div
|
||||
class="flex items-center pr-2 shrink-0 {show_divider ? 'border-r border-surface-500/30 mr-2' : ''}"
|
||||
>
|
||||
class="flex shrink-0 items-center pr-2 {show_divider
|
||||
? 'border-surface-500/30 mr-2 border-r'
|
||||
: ''}">
|
||||
<IconComp size={18} />
|
||||
</div>
|
||||
<span class="grow truncate text-left">
|
||||
{shortened_filename}
|
||||
</span>
|
||||
{#if hosted_file_obj?.file_purpose || hosted_file_obj?.group}
|
||||
<span class="badge preset-tonal-success ml-2 text-[10px] uppercase font-bold shrink-0">
|
||||
<span
|
||||
class="badge preset-tonal-success ml-2 shrink-0 text-[10px] font-bold uppercase">
|
||||
{hosted_file_obj.file_purpose || hosted_file_obj.group}
|
||||
</span>
|
||||
{/if}
|
||||
@@ -270,22 +306,25 @@
|
||||
{/await}
|
||||
|
||||
{#if download_complete === null}
|
||||
<span class="text-red-800 dark:text-red-200 ml-2 whitespace-nowrap">File not found</span>
|
||||
<span class="ml-2 whitespace-nowrap text-red-800 dark:text-red-200"
|
||||
>File not found</span>
|
||||
{:else if download_complete === false}
|
||||
<span class="text-red-800 dark:text-red-200 ml-2 whitespace-nowrap text-xs">Failed!</span>
|
||||
<span
|
||||
class="ml-2 text-xs whitespace-nowrap text-red-800 dark:text-red-200"
|
||||
>Failed!</span>
|
||||
{/if}
|
||||
{/snippet}
|
||||
|
||||
{#if hosted_file_id && hosted_file_obj}
|
||||
{@const file_id = hosted_file_obj.id || hosted_file_obj.hosted_file_id || hosted_file_id}
|
||||
{@const file_id =
|
||||
hosted_file_obj.id || hosted_file_obj.hosted_file_id || hosted_file_id}
|
||||
|
||||
{#if show_direct_download}
|
||||
<a
|
||||
href={direct_download_url}
|
||||
download={ae_util.clean_filename(final_filename)}
|
||||
class={variant_classes}
|
||||
title={`Direct download (V3 Action):\n${final_filename}\n[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}...\nHosted ID: ${file_id}`}
|
||||
>
|
||||
title={`Direct download (V3 Action):\n${final_filename}\n[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}...\nHosted ID: ${file_id}`}>
|
||||
{@render content()}
|
||||
</a>
|
||||
{:else}
|
||||
@@ -294,20 +333,24 @@
|
||||
disabled={require_auth && !$ae_loc.authenticated_access}
|
||||
class={variant_classes}
|
||||
onclick={handle_click}
|
||||
title={`Download this file:\n${final_filename}\n[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}...\nHosted ID: ${file_id}\n Linked to: ${linked_to_type} ID: ${linked_to_id}`}
|
||||
>
|
||||
title={`Download this file:\n${final_filename}\n[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}...\nHosted ID: ${file_id}\n Linked to: ${linked_to_type} ID: ${linked_to_id}`}>
|
||||
{@render content()}
|
||||
</button>
|
||||
{/if}
|
||||
{:else}
|
||||
<button type="button" disabled class={variant_classes} title="No file selected">
|
||||
<div class="flex items-center w-full">
|
||||
<button
|
||||
type="button"
|
||||
disabled
|
||||
class={variant_classes}
|
||||
title="No file selected">
|
||||
<div class="flex w-full items-center">
|
||||
<div
|
||||
class="flex items-center pr-2 shrink-0 {show_divider ? 'border-r border-surface-500/30 mr-2' : ''}"
|
||||
>
|
||||
class="flex shrink-0 items-center pr-2 {show_divider
|
||||
? 'border-surface-500/30 mr-2 border-r'
|
||||
: ''}">
|
||||
<Lucide.FileX size={18} />
|
||||
</div>
|
||||
<span class="grow text-left"> No file info </span>
|
||||
</div>
|
||||
</button>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -1,281 +1,286 @@
|
||||
<script lang="ts">
|
||||
// untrack import removed — task_id sync now uses direct $effect (no untrack needed)
|
||||
// Imports
|
||||
// Import components and elements
|
||||
import * as Lucide from 'lucide-svelte';
|
||||
import Element_input_files_tbl from '$lib/elements/element_input_files_tbl.svelte';
|
||||
// untrack import removed — task_id sync now uses direct $effect (no untrack needed)
|
||||
// Imports
|
||||
// Import components and elements
|
||||
import * as Lucide from 'lucide-svelte';
|
||||
import Element_input_files_tbl from '$lib/elements/element_input_files_tbl.svelte';
|
||||
|
||||
// Import storage, functions, and libraries
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
// Import storage, functions, and libraries
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
|
||||
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 { api } from '$lib/api/api';
|
||||
import {
|
||||
ae_snip,
|
||||
ae_loc,
|
||||
ae_sess,
|
||||
ae_api,
|
||||
ae_trig,
|
||||
slct,
|
||||
slct_trigger
|
||||
} from '$lib/stores/ae_stores';
|
||||
|
||||
// Exports
|
||||
// Exports
|
||||
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
// Expecting these for link_to_type: 'event', 'event_location', 'archive_content', etc
|
||||
link_to_type: string;
|
||||
link_to_id: string;
|
||||
input_name?: string;
|
||||
multiple?: boolean;
|
||||
required?: boolean;
|
||||
accept?: string;
|
||||
class_li_default?: string;
|
||||
class_li?: string;
|
||||
input_class_li?: string[];
|
||||
table_class_li?: string[];
|
||||
upload_complete?: boolean;
|
||||
submit_status?: null | string;
|
||||
hosted_file_id_li?: string[];
|
||||
hosted_file_obj_li?: any[];
|
||||
hosted_file_obj_kv?: key_val;
|
||||
label?: import('svelte').Snippet;
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
// Expecting these for link_to_type: 'event', 'event_location', 'archive_content', etc
|
||||
link_to_type: string;
|
||||
link_to_id: string;
|
||||
input_name?: string;
|
||||
multiple?: boolean;
|
||||
required?: boolean;
|
||||
accept?: string;
|
||||
class_li_default?: string;
|
||||
class_li?: string;
|
||||
input_class_li?: string[];
|
||||
table_class_li?: string[];
|
||||
upload_complete?: boolean;
|
||||
submit_status?: null | string;
|
||||
hosted_file_id_li?: string[];
|
||||
hosted_file_obj_li?: any[];
|
||||
hosted_file_obj_kv?: key_val;
|
||||
label?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let {
|
||||
log_lvl = 0,
|
||||
link_to_type,
|
||||
link_to_id,
|
||||
input_name = 'file_list',
|
||||
multiple = true,
|
||||
required = true,
|
||||
accept = 'audio/*, image/*, video/*, .bak, .cfg, .css, .csv, .doc, .docx, .gz, .htm, .html, .ini, .iso, .j2, .json, .key, .keynote, .md, .pdf, .ppt, .pptx, .rar, .rtf, .sql, .svelte, ttf, .txt, .xls, .xlsx, .xz, .zip, .bin, .dmg, .exe, .js, .msi, .php, .py, .sh',
|
||||
class_li_default = 'flex flex-col gap-1 items-center justify-center w-full max-w-2xl mx-auto my-1',
|
||||
class_li = '',
|
||||
input_class_li = ['file_drop_area'],
|
||||
table_class_li = ['table', 'table-sm', 'table-striped', '', 'text-sm'],
|
||||
upload_complete = $bindable(false),
|
||||
submit_status = $bindable(null),
|
||||
hosted_file_id_li = $bindable([]),
|
||||
hosted_file_obj_li = $bindable([]),
|
||||
hosted_file_obj_kv = $bindable({}),
|
||||
label
|
||||
}: Props = $props();
|
||||
|
||||
// Local Variables
|
||||
let task_id: string = $state('');
|
||||
let input_file_list: any = $state(null);
|
||||
let ae_promises: key_val = $state({}); // Promise<any>;
|
||||
let ae_triggers: key_val = {};
|
||||
|
||||
let input_element_id = 'ae_comp__hosted_files_upload__input';
|
||||
|
||||
$effect(() => {
|
||||
if (log_lvl) {
|
||||
console.log(`*** ae_comp__hosted_files_upload.svelte ***`);
|
||||
console.log(`link_to_type: ${link_to_type} link_to_id: ${link_to_id}`);
|
||||
}
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
// Sync task_id with link_to_id prop so it resets when navigating to a different object.
|
||||
task_id = link_to_id;
|
||||
});
|
||||
|
||||
// *** Functions and Logic
|
||||
async function handle_submit_form_files(event: SubmitEvent) {
|
||||
console.log('*** handle_submit_form() ***');
|
||||
event.preventDefault();
|
||||
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
|
||||
let {
|
||||
log_lvl = 0,
|
||||
link_to_type,
|
||||
link_to_id,
|
||||
input_name = 'file_list',
|
||||
multiple = true,
|
||||
required = true,
|
||||
accept = 'audio/*, image/*, video/*, .bak, .cfg, .css, .csv, .doc, .docx, .gz, .htm, .html, .ini, .iso, .j2, .json, .key, .keynote, .md, .pdf, .ppt, .pptx, .rar, .rtf, .sql, .svelte, ttf, .txt, .xls, .xlsx, .xz, .zip, .bin, .dmg, .exe, .js, .msi, .php, .py, .sh',
|
||||
class_li_default = 'flex flex-col gap-1 items-center justify-center w-full max-w-2xl mx-auto my-1',
|
||||
class_li = '',
|
||||
input_class_li = ['file_drop_area'],
|
||||
table_class_li = ['table', 'table-sm', 'table-striped', '', 'text-sm'],
|
||||
upload_complete = $bindable(false),
|
||||
submit_status = $bindable(null),
|
||||
hosted_file_id_li = $bindable([]),
|
||||
hosted_file_obj_li = $bindable([]),
|
||||
hosted_file_obj_kv = $bindable({}),
|
||||
label
|
||||
}: Props = $props();
|
||||
$ae_sess.files.disable_submit__hosted_file_obj = true;
|
||||
$ae_sess.files.submit_status = 'saving';
|
||||
submit_status = 'saving';
|
||||
upload_complete = false;
|
||||
|
||||
// Local Variables
|
||||
let task_id: string = $state('');
|
||||
let input_file_list: any = $state(null);
|
||||
let ae_promises: key_val = $state({}); // Promise<any>;
|
||||
let ae_triggers: key_val = {};
|
||||
hosted_file_id_li = [];
|
||||
hosted_file_obj_li = [];
|
||||
hosted_file_obj_kv = {};
|
||||
|
||||
let input_element_id = 'ae_comp__hosted_files_upload__input';
|
||||
let hosted_file_results;
|
||||
|
||||
$effect(() => {
|
||||
if (log_lvl) {
|
||||
console.log(`*** ae_comp__hosted_files_upload.svelte ***`);
|
||||
console.log(`link_to_type: ${link_to_type} link_to_id: ${link_to_id}`);
|
||||
}
|
||||
});
|
||||
const target = event.currentTarget as HTMLFormElement;
|
||||
const file_input = target
|
||||
? (target[input_element_id] as HTMLInputElement)
|
||||
: null;
|
||||
|
||||
$effect(() => {
|
||||
// Sync task_id with link_to_id prop so it resets when navigating to a different object.
|
||||
task_id = link_to_id;
|
||||
});
|
||||
if (
|
||||
target &&
|
||||
file_input &&
|
||||
file_input.files &&
|
||||
file_input.files.length > 0
|
||||
) {
|
||||
task_id = link_to_id; // Ideally this should be the file hash, but we may be uploading multiple files at once. This should be done with a loop instead?
|
||||
|
||||
// *** Functions and Logic
|
||||
async function handle_submit_form_files(event: SubmitEvent) {
|
||||
console.log('*** handle_submit_form() ***');
|
||||
event.preventDefault();
|
||||
// Loop through each file and upload them individually in event.target[input_element_id].files
|
||||
// The task_id should be the file hash.
|
||||
// processed_file_list[i] has the file hash_sha256, hash_sha256_match, warnings, uploaded, uploaded_bytes, filename, and file_size_bytes.
|
||||
for (let i = 0; i < file_input.files.length; i++) {
|
||||
let tmp_file = file_input.files[i];
|
||||
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
task_id = $ae_sess.files.processed_file_list[i].hash_sha256;
|
||||
|
||||
$ae_sess.files.disable_submit__hosted_file_obj = true;
|
||||
$ae_sess.files.submit_status = 'saving';
|
||||
submit_status = 'saving';
|
||||
upload_complete = false;
|
||||
|
||||
hosted_file_id_li = [];
|
||||
hosted_file_obj_li = [];
|
||||
hosted_file_obj_kv = {};
|
||||
|
||||
let hosted_file_results;
|
||||
|
||||
const target = event.currentTarget as HTMLFormElement;
|
||||
const file_input = target ? (target[input_element_id] as HTMLInputElement) : null;
|
||||
|
||||
if (
|
||||
target &&
|
||||
file_input &&
|
||||
file_input.files &&
|
||||
file_input.files.length > 0
|
||||
) {
|
||||
task_id = link_to_id; // Ideally this should be the file hash, but we may be uploading multiple files at once. This should be done with a loop instead?
|
||||
|
||||
// Loop through each file and upload them individually in event.target[input_element_id].files
|
||||
// The task_id should be the file hash.
|
||||
// processed_file_list[i] has the file hash_sha256, hash_sha256_match, warnings, uploaded, uploaded_bytes, filename, and file_size_bytes.
|
||||
for (let i = 0; i < file_input.files.length; i++) {
|
||||
let tmp_file = file_input.files[i];
|
||||
|
||||
task_id = $ae_sess.files.processed_file_list[i].hash_sha256;
|
||||
|
||||
// hosted_file_results = await handle_input_upload_files([tmp_file], task_id);
|
||||
hosted_file_results = await handle_input_upload_files({
|
||||
input_upload_files: [tmp_file],
|
||||
task_id: task_id
|
||||
});
|
||||
|
||||
if (hosted_file_results) {
|
||||
console.log(`hosted_file_results:`, hosted_file_results);
|
||||
} else {
|
||||
console.log(`hosted_file_results:`, hosted_file_results);
|
||||
}
|
||||
}
|
||||
// hosted_file_results = await handle_input_upload_files(event.target[input_element_id].files, task_id);
|
||||
$ae_sess.files.processed_file_list = [];
|
||||
$ae_sess = $ae_sess; // Is this needed? 2025-03-17
|
||||
target.reset();
|
||||
// await tick();
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`hosted_file_id_li: ${hosted_file_id_li}`, hosted_file_id_li);
|
||||
} else if (log_lvl > 1) {
|
||||
console.log('hosted_file_results:', hosted_file_results);
|
||||
}
|
||||
}
|
||||
|
||||
$ae_sess.files.disable_submit__hosted_file_obj = false;
|
||||
$ae_sess.files.submit_status = 'saved';
|
||||
submit_status = 'saved';
|
||||
upload_complete = true;
|
||||
}
|
||||
|
||||
async function handle_input_upload_files({
|
||||
input_upload_files,
|
||||
task_id
|
||||
}: {
|
||||
input_upload_files: any[];
|
||||
task_id: string;
|
||||
}) {
|
||||
console.log('*** handle_input_upload_files() ***');
|
||||
|
||||
const form_data = new FormData();
|
||||
|
||||
form_data.append('account_id', $ae_loc.account_id);
|
||||
form_data.append('link_to_type', link_to_type);
|
||||
form_data.append('link_to_id', link_to_id);
|
||||
|
||||
for (let i = 0; i < input_upload_files.length; i++) {
|
||||
form_data.append(`file_list`, input_upload_files[i]);
|
||||
}
|
||||
|
||||
// hash_sha256, uploaded, uploaded_bytes
|
||||
// $ae_sess.files.processed_file_list[i] = {
|
||||
// ...$ae_sess.files.processed_file_list[i],
|
||||
// uploaded: $ae_sess.api_upload_kv[link_to_id].percent_completed,
|
||||
// uploaded_bytes: $ae_sess.api_upload_kv[link_to_id].uploaded_bytes,
|
||||
// };
|
||||
|
||||
let params = null;
|
||||
|
||||
let endpoint = '/hosted_file/upload_files';
|
||||
|
||||
console.log(form_data);
|
||||
|
||||
params = null;
|
||||
|
||||
// Uncomment and the post_promise is not seen by the "await" below
|
||||
// post_promise = await api.post_object({api_cfg: $cfg.api, endpoint: endpoint, params: params, data:form_data});
|
||||
// Uncomment so that the post_promise is not seen by the "await" below
|
||||
ae_promises.upload__hosted_file_obj = api
|
||||
.post_object({
|
||||
api_cfg: $ae_api,
|
||||
endpoint: endpoint,
|
||||
// params: params,
|
||||
form_data: form_data,
|
||||
task_id: task_id,
|
||||
log_lvl: log_lvl
|
||||
// retry_count: 1,
|
||||
})
|
||||
.then(async function (result) {
|
||||
// WARNING!!!! ONLY ONE FILE IS EXPECTED TO BE UPLOADED AT A TIME!!!
|
||||
// NOTE: The /hosted_file/upload_files endpoint will always return a list of successful files uploaded. In this case we are only uploading one file and expecting a list of one item.
|
||||
let x = 0;
|
||||
console.log(result[x]);
|
||||
let hosted_file_obj = result[x];
|
||||
|
||||
let hosted_file_id = hosted_file_obj.hosted_file_id;
|
||||
|
||||
hosted_file_id_li.push(hosted_file_id);
|
||||
hosted_file_obj_li.push(hosted_file_obj);
|
||||
|
||||
let hosted_file_data: key_val = {};
|
||||
hosted_file_data['id'] = hosted_file_id; // Same as the hosted_file_id
|
||||
hosted_file_data['hosted_file_id'] = hosted_file_id;
|
||||
hosted_file_data['for_type'] = link_to_type;
|
||||
hosted_file_data['for_id'] = link_to_id;
|
||||
hosted_file_data['hash_sha256'] = hosted_file_obj.hash_sha256;
|
||||
hosted_file_data['filename'] = hosted_file_obj.filename;
|
||||
hosted_file_data['extension'] = hosted_file_obj.extension;
|
||||
hosted_file_data['content_type'] = hosted_file_obj.content_type;
|
||||
hosted_file_data['size'] = hosted_file_obj.size;
|
||||
hosted_file_data['enable'] = true;
|
||||
hosted_file_data['created_on'] = hosted_file_obj.created_on;
|
||||
hosted_file_data['updated_on'] = hosted_file_obj.updated_on;
|
||||
console.log(hosted_file_data);
|
||||
|
||||
hosted_file_obj_kv[hosted_file_id] = hosted_file_data;
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`hosted_file_data:`, hosted_file_data);
|
||||
}
|
||||
|
||||
return hosted_file_data;
|
||||
|
||||
// $ae_sess.files.new_upload_list[i].uploaded_bytes = 10; // fake 10 bytes at least...
|
||||
|
||||
// let event_file_id = await events_func.create_hosted_file_obj_from_hosted_file_async({
|
||||
// api_cfg: $ae_api,
|
||||
// hosted_file_id: hosted_file_id,
|
||||
// data: event_file_data,
|
||||
// log_lvl: log_lvl
|
||||
// })
|
||||
// .then(function (create_result) {
|
||||
// console.log(create_result); // NOTE: This should be the event_file_id string
|
||||
// // let event_file_id = create_result;
|
||||
// return create_result;
|
||||
// });
|
||||
|
||||
// return event_file_id;
|
||||
})
|
||||
// .then(function (hosted_file_data) {
|
||||
// return hosted_file_data;
|
||||
// })
|
||||
.catch(function (error: any) {
|
||||
console.log('Something went wrong.');
|
||||
console.log(error);
|
||||
return false;
|
||||
})
|
||||
.finally(function () {
|
||||
$slct_trigger = 'load__hosted_file_obj_li';
|
||||
// hosted_file_results = await handle_input_upload_files([tmp_file], task_id);
|
||||
hosted_file_results = await handle_input_upload_files({
|
||||
input_upload_files: [tmp_file],
|
||||
task_id: task_id
|
||||
});
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`Waiting for upload__hosted_file_obj promise...`);
|
||||
if (hosted_file_results) {
|
||||
console.log(`hosted_file_results:`, hosted_file_results);
|
||||
} else {
|
||||
console.log(`hosted_file_results:`, hosted_file_results);
|
||||
}
|
||||
}
|
||||
let hosted_file_result = ae_promises.upload__hosted_file_obj;
|
||||
// hosted_file_results = await handle_input_upload_files(event.target[input_element_id].files, task_id);
|
||||
$ae_sess.files.processed_file_list = [];
|
||||
$ae_sess = $ae_sess; // Is this needed? 2025-03-17
|
||||
target.reset();
|
||||
// await tick();
|
||||
|
||||
return hosted_file_result;
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`hosted_file_id_li: ${hosted_file_id_li}`,
|
||||
hosted_file_id_li
|
||||
);
|
||||
} else if (log_lvl > 1) {
|
||||
console.log('hosted_file_results:', hosted_file_results);
|
||||
}
|
||||
}
|
||||
|
||||
$ae_sess.files.disable_submit__hosted_file_obj = false;
|
||||
$ae_sess.files.submit_status = 'saved';
|
||||
submit_status = 'saved';
|
||||
upload_complete = true;
|
||||
}
|
||||
|
||||
async function handle_input_upload_files({
|
||||
input_upload_files,
|
||||
task_id
|
||||
}: {
|
||||
input_upload_files: any[];
|
||||
task_id: string;
|
||||
}) {
|
||||
console.log('*** handle_input_upload_files() ***');
|
||||
|
||||
const form_data = new FormData();
|
||||
|
||||
form_data.append('account_id', $ae_loc.account_id);
|
||||
form_data.append('link_to_type', link_to_type);
|
||||
form_data.append('link_to_id', link_to_id);
|
||||
|
||||
for (let i = 0; i < input_upload_files.length; i++) {
|
||||
form_data.append(`file_list`, input_upload_files[i]);
|
||||
}
|
||||
|
||||
// hash_sha256, uploaded, uploaded_bytes
|
||||
// $ae_sess.files.processed_file_list[i] = {
|
||||
// ...$ae_sess.files.processed_file_list[i],
|
||||
// uploaded: $ae_sess.api_upload_kv[link_to_id].percent_completed,
|
||||
// uploaded_bytes: $ae_sess.api_upload_kv[link_to_id].uploaded_bytes,
|
||||
// };
|
||||
|
||||
let params = null;
|
||||
|
||||
let endpoint = '/v3/action/hosted_file/upload';
|
||||
|
||||
console.log(form_data);
|
||||
|
||||
params = null;
|
||||
|
||||
// Uncomment and the post_promise is not seen by the "await" below
|
||||
// post_promise = await api.post_object({api_cfg: $cfg.api, endpoint: endpoint, params: params, data:form_data});
|
||||
// Uncomment so that the post_promise is not seen by the "await" below
|
||||
ae_promises.upload__hosted_file_obj = api
|
||||
.post_object({
|
||||
api_cfg: $ae_api,
|
||||
endpoint: endpoint,
|
||||
// params: params,
|
||||
form_data: form_data,
|
||||
task_id: task_id,
|
||||
log_lvl: log_lvl
|
||||
// retry_count: 1,
|
||||
})
|
||||
.then(async function (result) {
|
||||
// WARNING!!!! ONLY ONE FILE IS EXPECTED TO BE UPLOADED AT A TIME!!!
|
||||
// NOTE: The upload endpoint always returns a list of successfully uploaded files. In this case we are only uploading one file and expecting a list of one item.
|
||||
let x = 0;
|
||||
console.log(result[x]);
|
||||
let hosted_file_obj = result[x];
|
||||
|
||||
let hosted_file_id = hosted_file_obj.hosted_file_id;
|
||||
|
||||
hosted_file_id_li.push(hosted_file_id);
|
||||
hosted_file_obj_li.push(hosted_file_obj);
|
||||
|
||||
let hosted_file_data: key_val = {};
|
||||
hosted_file_data['id'] = hosted_file_id; // Same as the hosted_file_id
|
||||
hosted_file_data['hosted_file_id'] = hosted_file_id;
|
||||
hosted_file_data['for_type'] = link_to_type;
|
||||
hosted_file_data['for_id'] = link_to_id;
|
||||
hosted_file_data['hash_sha256'] = hosted_file_obj.hash_sha256;
|
||||
hosted_file_data['filename'] = hosted_file_obj.filename;
|
||||
hosted_file_data['extension'] = hosted_file_obj.extension;
|
||||
hosted_file_data['content_type'] = hosted_file_obj.content_type;
|
||||
hosted_file_data['size'] = hosted_file_obj.size;
|
||||
hosted_file_data['enable'] = true;
|
||||
hosted_file_data['created_on'] = hosted_file_obj.created_on;
|
||||
hosted_file_data['updated_on'] = hosted_file_obj.updated_on;
|
||||
console.log(hosted_file_data);
|
||||
|
||||
hosted_file_obj_kv[hosted_file_id] = hosted_file_data;
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`hosted_file_data:`, hosted_file_data);
|
||||
}
|
||||
|
||||
return hosted_file_data;
|
||||
|
||||
// $ae_sess.files.new_upload_list[i].uploaded_bytes = 10; // fake 10 bytes at least...
|
||||
|
||||
// let event_file_id = await events_func.create_hosted_file_obj_from_hosted_file_async({
|
||||
// api_cfg: $ae_api,
|
||||
// hosted_file_id: hosted_file_id,
|
||||
// data: event_file_data,
|
||||
// log_lvl: log_lvl
|
||||
// })
|
||||
// .then(function (create_result) {
|
||||
// console.log(create_result); // NOTE: This should be the event_file_id string
|
||||
// // let event_file_id = create_result;
|
||||
// return create_result;
|
||||
// });
|
||||
|
||||
// return event_file_id;
|
||||
})
|
||||
// .then(function (hosted_file_data) {
|
||||
// return hosted_file_data;
|
||||
// })
|
||||
.catch(function (error: any) {
|
||||
console.log('Something went wrong.');
|
||||
console.log(error);
|
||||
return false;
|
||||
})
|
||||
.finally(function () {
|
||||
$slct_trigger = 'load__hosted_file_obj_li';
|
||||
});
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`Waiting for upload__hosted_file_obj promise...`);
|
||||
}
|
||||
let hosted_file_result = ae_promises.upload__hosted_file_obj;
|
||||
|
||||
return hosted_file_result;
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- class:hidden={!$ae_loc.trusted_access} -->
|
||||
<form onsubmit={handle_submit_form_files} class="{class_li_default} {class_li}">
|
||||
{#await ae_promises.upload__hosted_file_obj}
|
||||
<div class="text-lg flex flex-row gap-1 items-center justify-center">
|
||||
<Lucide.LoaderCircle class="animate-spin m-1" />
|
||||
<div class="flex flex-row items-center justify-center gap-1 text-lg">
|
||||
<Lucide.LoaderCircle class="m-1 animate-spin" />
|
||||
<span class="">
|
||||
Uploading
|
||||
{#if $ae_sess.api_upload_kv[task_id]}
|
||||
@@ -288,14 +293,14 @@
|
||||
<label
|
||||
for="ae_comp__hosted_files_upload__input"
|
||||
class="svelte_input_file_label text-center"
|
||||
class:hidden={$ae_sess.files.disable_submit__hosted_file_obj}
|
||||
>
|
||||
class:hidden={$ae_sess.files.disable_submit__hosted_file_obj}>
|
||||
{#if label}{@render label()}{:else}
|
||||
<div class="flex items-center justify-center gap-2 mb-2">
|
||||
<div class="mb-2 flex items-center justify-center gap-2">
|
||||
<Lucide.Upload class="text-primary-500" />
|
||||
<strong class="preset-tonal-primary px-3 py-1 rounded-full">Select Files</strong>
|
||||
<strong class="preset-tonal-primary rounded-full px-3 py-1"
|
||||
>Select Files</strong>
|
||||
</div>
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400 italic">
|
||||
<span class="text-sm text-gray-600 italic dark:text-gray-400">
|
||||
<strong>Supported formats</strong><br />
|
||||
(PowerPoint, Keynote, PDF, Media, etc)
|
||||
</span>
|
||||
@@ -313,33 +318,30 @@
|
||||
class="
|
||||
svelte_input_file_element
|
||||
file-dropzone-input
|
||||
px-1
|
||||
block w-full text-lg
|
||||
preset-filled-surface-50-950
|
||||
text-surface-900 dark:text-surface-100
|
||||
border border-surface-300 dark:border-surface-700 rounded-lg
|
||||
cursor-pointer
|
||||
focus:outline-hidden focus:ring-2 focus:ring-primary-500
|
||||
text-surface-900 dark:text-surface-100 border-surface-300
|
||||
dark:border-surface-700
|
||||
focus:ring-primary-500 block
|
||||
w-full cursor-pointer rounded-lg border
|
||||
px-1
|
||||
text-lg focus:ring-2 focus:outline-hidden
|
||||
{input_class_li.join(' ')}
|
||||
"
|
||||
class:hidden={$ae_sess.files.disable_submit__hosted_file_obj}
|
||||
/>
|
||||
class:hidden={$ae_sess.files.disable_submit__hosted_file_obj} />
|
||||
|
||||
<Element_input_files_tbl
|
||||
bind:input_file_list
|
||||
bind:file_list_status={$ae_sess.files.status__file_list}
|
||||
bind:processed_file_list={$ae_sess.files.processed_file_list}
|
||||
{table_class_li}
|
||||
/>
|
||||
{table_class_li} />
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-lg btn-primary preset-tonal-primary border border-primary-500 hover:preset-tonal-success hover:border-success-500 w-54"
|
||||
class="btn btn-lg btn-primary preset-tonal-primary border-primary-500 hover:preset-tonal-success hover:border-success-500 w-54 border"
|
||||
disabled={$ae_sess.files.disable_submit__hosted_file_obj ||
|
||||
$ae_sess.files.status__file_list != 'ready'}
|
||||
>
|
||||
$ae_sess.files.status__file_list != 'ready'}>
|
||||
{#await ae_promises.upload__hosted_file_obj}
|
||||
<Lucide.LoaderCircle class="animate-spin m-1" />
|
||||
<Lucide.LoaderCircle class="m-1 animate-spin" />
|
||||
<span class="">
|
||||
{#if $ae_sess.api_upload_kv[task_id]}
|
||||
{$ae_sess.api_upload_kv[task_id].percent_completed}%
|
||||
@@ -350,9 +352,12 @@
|
||||
{:then}
|
||||
<Lucide.UploadCloud class="m-1" size={20} />
|
||||
<span class="text-sm"> Upload </span>
|
||||
<span class="grow font-bold ml-2">
|
||||
<span class="ml-2 grow font-bold">
|
||||
{#if $ae_sess.files.processed_file_list?.length > 0}
|
||||
{$ae_sess.files.processed_file_list.length} { $ae_sess.files.processed_file_list.length === 1 ? 'file' : 'files' }
|
||||
{$ae_sess.files.processed_file_list.length}
|
||||
{$ae_sess.files.processed_file_list.length === 1
|
||||
? 'file'
|
||||
: 'files'}
|
||||
{:else}
|
||||
<span class="text-xs"> 0 </span>
|
||||
{/if}
|
||||
|
||||
@@ -1,235 +1,358 @@
|
||||
<script lang="ts">
|
||||
import { untrack } from 'svelte';
|
||||
/**
|
||||
* AE_Comp_Site_Config_Editor.svelte
|
||||
* Specialized UI for managing site.cfg_json settings.
|
||||
* Supports General, AI, Performance, and IDAA-specific configurations.
|
||||
*/
|
||||
import { Modal } from 'flowbite-svelte';
|
||||
import { Brain, CodeXml, ExternalLink, Globe, Mail, Minus, Palette, Plus, Save, ShieldCheck, Timer } from '@lucide/svelte';
|
||||
import AE_Comp_Editor_CodeMirror from '$lib/elements/AE_Comp_Editor_CodeMirror.svelte';
|
||||
import { ae_loc } from '$lib/stores/ae_stores';
|
||||
import { untrack } from 'svelte';
|
||||
/**
|
||||
* AE_Comp_Site_Config_Editor.svelte
|
||||
* Specialized UI for managing site.cfg_json settings.
|
||||
* Supports General, AI, Performance, and IDAA-specific configurations.
|
||||
*/
|
||||
import { Modal } from 'flowbite-svelte';
|
||||
import {
|
||||
Brain,
|
||||
CodeXml,
|
||||
ExternalLink,
|
||||
Globe,
|
||||
Mail,
|
||||
Minus,
|
||||
Palette,
|
||||
Plus,
|
||||
Save,
|
||||
ShieldCheck,
|
||||
Timer
|
||||
} from '@lucide/svelte';
|
||||
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte';
|
||||
import { ae_loc } from '$lib/stores/ae_stores';
|
||||
|
||||
interface Props {
|
||||
cfg_json: any;
|
||||
on_save?: () => void;
|
||||
}
|
||||
interface Props {
|
||||
cfg_json: any;
|
||||
on_save?: () => void;
|
||||
}
|
||||
|
||||
let { cfg_json = $bindable({}), on_save }: Props = $props();
|
||||
let { cfg_json = $bindable({}), on_save }: Props = $props();
|
||||
|
||||
// Ensure we have a valid object (handle strings/nulls)
|
||||
$effect(() => {
|
||||
if (typeof cfg_json === 'string') {
|
||||
try {
|
||||
cfg_json = JSON.parse(cfg_json);
|
||||
} catch (e) {
|
||||
cfg_json = {};
|
||||
}
|
||||
// Ensure we have a valid object (handle strings/nulls)
|
||||
$effect(() => {
|
||||
if (typeof cfg_json === 'string') {
|
||||
try {
|
||||
cfg_json = JSON.parse(cfg_json);
|
||||
} catch (e) {
|
||||
cfg_json = {};
|
||||
}
|
||||
if (!cfg_json) cfg_json = {};
|
||||
});
|
||||
|
||||
// Internal State
|
||||
let active_tab: 'visuals' | 'email' | 'ai' | 'refresh' | 'idaa' | 'raw' = $state('visuals');
|
||||
let raw_json_str = $state('');
|
||||
|
||||
// Ensure we have a valid object
|
||||
}
|
||||
if (!cfg_json) cfg_json = {};
|
||||
});
|
||||
|
||||
function add_to_list(key: string) {
|
||||
if (!cfg_json[key]) cfg_json[key] = [];
|
||||
const val = prompt('Enter Novi UUID:');
|
||||
if (val) cfg_json[key].push(val);
|
||||
// Internal State
|
||||
let active_tab: 'visuals' | 'email' | 'ai' | 'refresh' | 'idaa' | 'raw' =
|
||||
$state('visuals');
|
||||
let raw_json_str = $state('');
|
||||
|
||||
// Ensure we have a valid object
|
||||
if (!cfg_json) cfg_json = {};
|
||||
|
||||
function add_to_list(key: string) {
|
||||
if (!cfg_json[key]) cfg_json[key] = [];
|
||||
const val = prompt('Enter Novi UUID:');
|
||||
if (val) cfg_json[key].push(val);
|
||||
}
|
||||
|
||||
function remove_from_list(key: string, index: number) {
|
||||
cfg_json[key].splice(index, 1);
|
||||
}
|
||||
|
||||
// Sync Raw JSON string when entering the tab
|
||||
$effect(() => {
|
||||
if (active_tab === 'raw') {
|
||||
untrack(() => {
|
||||
raw_json_str = JSON.stringify(cfg_json, null, 2);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function remove_from_list(key: string, index: number) {
|
||||
cfg_json[key].splice(index, 1);
|
||||
// Update cfg_json when raw string changes
|
||||
$effect(() => {
|
||||
if (active_tab === 'raw' && raw_json_str) {
|
||||
try {
|
||||
const parsed = JSON.parse(raw_json_str);
|
||||
cfg_json = parsed;
|
||||
} catch (e) {
|
||||
// Ignore invalid JSON while typing
|
||||
}
|
||||
}
|
||||
|
||||
// Sync Raw JSON string when entering the tab
|
||||
$effect(() => {
|
||||
if (active_tab === 'raw') {
|
||||
untrack(() => {
|
||||
raw_json_str = JSON.stringify(cfg_json, null, 2);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Update cfg_json when raw string changes
|
||||
$effect(() => {
|
||||
if (active_tab === 'raw' && raw_json_str) {
|
||||
try {
|
||||
const parsed = JSON.parse(raw_json_str);
|
||||
cfg_json = parsed;
|
||||
} catch (e) {
|
||||
// Ignore invalid JSON while typing
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="ae-site-config-editor flex flex-col h-full space-y-4">
|
||||
<div class="ae-site-config-editor flex h-full flex-col space-y-4">
|
||||
<!-- Tab Navigation -->
|
||||
<div class="flex flex-wrap gap-1 p-1 bg-surface-500/10 rounded-lg max-w-fit">
|
||||
<button class="btn btn-sm transition-all {active_tab === 'visuals' ? 'variant-filled-primary' : 'variant-soft-surface'}" onclick={() => active_tab = 'visuals'}>
|
||||
<div
|
||||
class="bg-surface-500/10 flex max-w-fit flex-wrap gap-1 rounded-lg p-1">
|
||||
<button
|
||||
class="btn btn-sm transition-all {active_tab === 'visuals'
|
||||
? 'variant-filled-primary'
|
||||
: 'variant-soft-surface'}"
|
||||
onclick={() => (active_tab = 'visuals')}>
|
||||
<Palette size="1.1em" class="mr-1" /> Visuals
|
||||
</button>
|
||||
<button class="btn btn-sm transition-all {active_tab === 'email' ? 'variant-filled-primary' : 'variant-soft-surface'}" onclick={() => active_tab = 'email'}>
|
||||
<button
|
||||
class="btn btn-sm transition-all {active_tab === 'email'
|
||||
? 'variant-filled-primary'
|
||||
: 'variant-soft-surface'}"
|
||||
onclick={() => (active_tab = 'email')}>
|
||||
<Mail size="1.1em" class="mr-1" /> Email
|
||||
</button>
|
||||
<button class="btn btn-sm transition-all {active_tab === 'ai' ? 'variant-filled-primary' : 'variant-soft-surface'}" onclick={() => active_tab = 'ai'}>
|
||||
<button
|
||||
class="btn btn-sm transition-all {active_tab === 'ai'
|
||||
? 'variant-filled-primary'
|
||||
: 'variant-soft-surface'}"
|
||||
onclick={() => (active_tab = 'ai')}>
|
||||
<Brain size="1.1em" class="mr-1" /> AI/LLM
|
||||
</button>
|
||||
<button class="btn btn-sm transition-all {active_tab === 'refresh' ? 'variant-filled-primary' : 'variant-soft-surface'}" onclick={() => active_tab = 'refresh'}>
|
||||
<button
|
||||
class="btn btn-sm transition-all {active_tab === 'refresh'
|
||||
? 'variant-filled-primary'
|
||||
: 'variant-soft-surface'}"
|
||||
onclick={() => (active_tab = 'refresh')}>
|
||||
<Timer size="1.1em" class="mr-1" /> Refresh
|
||||
</button>
|
||||
<button class="btn btn-sm transition-all {active_tab === 'idaa' ? 'variant-filled-primary' : 'variant-soft-surface'}" onclick={() => active_tab = 'idaa'}>
|
||||
<button
|
||||
class="btn btn-sm transition-all {active_tab === 'idaa'
|
||||
? 'variant-filled-primary'
|
||||
: 'variant-soft-surface'}"
|
||||
onclick={() => (active_tab = 'idaa')}>
|
||||
<ShieldCheck size="1.1em" class="mr-1" /> IDAA
|
||||
</button>
|
||||
<button class="btn btn-sm transition-all {active_tab === 'raw' ? 'variant-filled-primary' : 'variant-soft-surface'}" onclick={() => active_tab = 'raw'}>
|
||||
<button
|
||||
class="btn btn-sm transition-all {active_tab === 'raw'
|
||||
? 'variant-filled-primary'
|
||||
: 'variant-soft-surface'}"
|
||||
onclick={() => (active_tab = 'raw')}>
|
||||
<CodeXml size="1.1em" class="mr-1" /> Raw JSON
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Scrollable Content Area -->
|
||||
<div class="grow overflow-y-auto p-1 pr-2 space-y-6 max-h-[60vh]">
|
||||
|
||||
<div class="max-h-[60vh] grow space-y-6 overflow-y-auto p-1 pr-2">
|
||||
{#if active_tab === 'visuals'}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 animate-in fade-in duration-200">
|
||||
<div
|
||||
class="animate-in fade-in grid grid-cols-1 gap-4 duration-200 md:grid-cols-2">
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Theme Name</span>
|
||||
<input type="text" bind:value={cfg_json.theme_name} class="input variant-form-material" placeholder="e.g. AE_OSIT_default" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Theme Name</span>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={cfg_json.theme_name}
|
||||
class="input variant-form-material"
|
||||
placeholder="e.g. AE_OSIT_default" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Theme Mode</span>
|
||||
<select bind:value={cfg_json.theme_mode} class="select variant-form-material">
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Theme Mode</span>
|
||||
<select
|
||||
bind:value={cfg_json.theme_mode}
|
||||
class="select variant-form-material">
|
||||
<option value="light">Light</option>
|
||||
<option value="dark">Dark</option>
|
||||
<option value="auto">Auto (System)</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="label md:col-span-2">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Header Image Path (URL)</span>
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Header Image Path (URL)</span>
|
||||
<div class="flex gap-2">
|
||||
<input type="text" bind:value={cfg_json.header_image_path} class="input variant-form-material grow" placeholder="https://..." />
|
||||
<input
|
||||
type="text"
|
||||
bind:value={cfg_json.header_image_path}
|
||||
class="input variant-form-material grow"
|
||||
placeholder="https://..." />
|
||||
{#if cfg_json.header_image_path}
|
||||
<a href={cfg_json.header_image_path} target="_blank" rel="noopener noreferrer" class="btn-icon variant-soft-surface"><ExternalLink size="1.2em" /></a>
|
||||
<a
|
||||
href={cfg_json.header_image_path}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="btn-icon variant-soft-surface"
|
||||
><ExternalLink size="1.2em" /></a>
|
||||
{/if}
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{:else if active_tab === 'email'}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 animate-in fade-in duration-200">
|
||||
<section class="space-y-4 border-r border-surface-500/10 pr-4">
|
||||
<h4 class="text-sm font-black text-primary-500">Admin Contact</h4>
|
||||
<div
|
||||
class="animate-in fade-in grid grid-cols-1 gap-4 duration-200 md:grid-cols-2">
|
||||
<section class="border-surface-500/10 space-y-4 border-r pr-4">
|
||||
<h4 class="text-primary-500 text-sm font-black">
|
||||
Admin Contact
|
||||
</h4>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Admin Name</span>
|
||||
<input type="text" bind:value={cfg_json.admin_name} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Admin Name</span>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={cfg_json.admin_name}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Admin Email</span>
|
||||
<input type="email" bind:value={cfg_json.admin_email} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Admin Email</span>
|
||||
<input
|
||||
type="email"
|
||||
bind:value={cfg_json.admin_email}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
</section>
|
||||
<section class="space-y-4">
|
||||
<h4 class="text-sm font-black text-secondary-500">System (No-Reply)</h4>
|
||||
<h4 class="text-secondary-500 text-sm font-black">
|
||||
System (No-Reply)
|
||||
</h4>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">No-Reply Name</span>
|
||||
<input type="text" bind:value={cfg_json.noreply_name} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>No-Reply Name</span>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={cfg_json.noreply_name}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">No-Reply Email</span>
|
||||
<input type="email" bind:value={cfg_json.noreply_email} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>No-Reply Email</span>
|
||||
<input
|
||||
type="email"
|
||||
bind:value={cfg_json.noreply_email}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{:else if active_tab === 'ai'}
|
||||
<div class="space-y-4 animate-in fade-in duration-200">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="animate-in fade-in space-y-4 duration-200">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">LLM API Base URL</span>
|
||||
<input type="text" bind:value={cfg_json.llm__api_base_url} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>LLM API Base URL</span>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={cfg_json.llm__api_base_url}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">LLM Model</span>
|
||||
<input type="text" bind:value={cfg_json.llm__api_model} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>LLM Model</span>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={cfg_json.llm__api_model}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
</div>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">API Token</span>
|
||||
<input type="password" bind:value={cfg_json.llm__api_token} class="input variant-form-material font-mono" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>API Token</span>
|
||||
<input
|
||||
type="password"
|
||||
bind:value={cfg_json.llm__api_token}
|
||||
class="input variant-form-material font-mono" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">System Prompt</span>
|
||||
<textarea bind:value={cfg_json.llm__system_prompt} class="textarea variant-form-material h-24 text-sm"></textarea>
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>System Prompt</span>
|
||||
<textarea
|
||||
bind:value={cfg_json.llm__system_prompt}
|
||||
class="textarea variant-form-material h-24 text-sm"
|
||||
></textarea>
|
||||
</label>
|
||||
<label class="flex items-center space-x-2">
|
||||
<input type="checkbox" bind:checked={cfg_json.llm__api_dangerous_browser} class="checkbox" />
|
||||
<span class="text-xs font-bold uppercase opacity-50">Allow Browser Fetch (Dangerously)</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={cfg_json.llm__api_dangerous_browser}
|
||||
class="checkbox" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Allow Browser Fetch (Dangerously)</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{:else if active_tab === 'refresh'}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 animate-in fade-in duration-200">
|
||||
<div
|
||||
class="animate-in fade-in grid grid-cols-1 gap-4 duration-200 md:grid-cols-2">
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Default (Minutes)</span>
|
||||
<input type="number" bind:value={cfg_json.default_refresh_minutes} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Default (Minutes)</span>
|
||||
<input
|
||||
type="number"
|
||||
bind:value={cfg_json.default_refresh_minutes}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Authenticated (Minutes)</span>
|
||||
<input type="number" bind:value={cfg_json.authenticated_refresh_time} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Authenticated (Minutes)</span>
|
||||
<input
|
||||
type="number"
|
||||
bind:value={cfg_json.authenticated_refresh_time}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Trusted (Minutes)</span>
|
||||
<input type="number" bind:value={cfg_json.trusted_refresh_minutes} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Trusted (Minutes)</span>
|
||||
<input
|
||||
type="number"
|
||||
bind:value={cfg_json.trusted_refresh_minutes}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Manager (Minutes)</span>
|
||||
<input type="number" bind:value={cfg_json.manager_refresh_minutes} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Manager (Minutes)</span>
|
||||
<input
|
||||
type="number"
|
||||
bind:value={cfg_json.manager_refresh_minutes}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{:else if active_tab === 'idaa'}
|
||||
<div class="space-y-6 animate-in fade-in duration-200">
|
||||
<div class="animate-in fade-in space-y-6 duration-200">
|
||||
<!-- Novi API -->
|
||||
<section class="space-y-4 p-4 bg-surface-500/5 rounded-xl border border-surface-500/10">
|
||||
<h4 class="text-sm font-black flex items-center gap-2">
|
||||
<section
|
||||
class="bg-surface-500/5 border-surface-500/10 space-y-4 rounded-xl border p-4">
|
||||
<h4 class="flex items-center gap-2 text-sm font-black">
|
||||
<Globe size="1.1em" class="text-primary-500" /> Novi API Connection
|
||||
</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Root URL</span>
|
||||
<input type="text" bind:value={cfg_json.novi_api_root_url} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Root URL</span>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={cfg_json.novi_api_root_url}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">API Key</span>
|
||||
<input type="password" bind:value={cfg_json.novi_idaa_api_key} class="input variant-form-material font-mono" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>API Key</span>
|
||||
<input
|
||||
type="password"
|
||||
bind:value={cfg_json.novi_idaa_api_key}
|
||||
class="input variant-form-material font-mono" />
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- UUID Lists -->
|
||||
<section class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{#each [
|
||||
{ key: 'novi_admin_li', label: 'Novi Admins', color: 'text-error-500' },
|
||||
{ key: 'novi_trusted_li', label: 'Novi Trusted', color: 'text-warning-500' },
|
||||
{ key: 'novi_jitsi_mod_li', label: 'Jitsi Moderators', color: 'text-primary-500' },
|
||||
{ key: 'novi_idaa_group_guid_li', label: 'Member Group GUIDs', color: 'text-secondary-500' }
|
||||
] as list (list.key)}
|
||||
<div class="space-y-2 p-3 bg-surface-500/5 rounded-lg">
|
||||
<header class="flex justify-between items-center">
|
||||
<span class="text-[10px] font-black uppercase tracking-wider {list.color}">{list.label}</span>
|
||||
<button class="btn btn-icon btn-icon-sm variant-soft-primary" onclick={() => add_to_list(list.key)}>
|
||||
<section class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
{#each [{ key: 'novi_admin_li', label: 'Novi Admins', color: 'text-error-500' }, { key: 'novi_trusted_li', label: 'Novi Trusted', color: 'text-warning-500' }, { key: 'novi_jitsi_mod_li', label: 'Jitsi Moderators', color: 'text-primary-500' }, { key: 'novi_idaa_group_guid_li', label: 'Member Group GUIDs', color: 'text-secondary-500' }] as list (list.key)}
|
||||
<div class="bg-surface-500/5 space-y-2 rounded-lg p-3">
|
||||
<header class="flex items-center justify-between">
|
||||
<span
|
||||
class="text-[10px] font-black tracking-wider uppercase {list.color}"
|
||||
>{list.label}</span>
|
||||
<button
|
||||
class="btn btn-icon btn-icon-sm variant-soft-primary"
|
||||
onclick={() => add_to_list(list.key)}>
|
||||
<Plus size="12" />
|
||||
</button>
|
||||
</header>
|
||||
<div class="space-y-1">
|
||||
{#each cfg_json[list.key] ?? [] as uuid, i (uuid)}
|
||||
<div class="flex gap-1 items-center bg-surface-500/10 p-1 rounded font-mono text-[10px]">
|
||||
<span class="grow truncate">{uuid}</span>
|
||||
<button class="text-error-500 hover:scale-110 transition-transform" onclick={() => remove_from_list(list.key, i)}>
|
||||
<div
|
||||
class="bg-surface-500/10 flex items-center gap-1 rounded p-1 font-mono text-[10px]">
|
||||
<span class="grow truncate"
|
||||
>{uuid}</span>
|
||||
<button
|
||||
class="text-error-500 transition-transform hover:scale-110"
|
||||
onclick={() =>
|
||||
remove_from_list(list.key, i)}>
|
||||
<Minus size="12" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -240,62 +363,96 @@
|
||||
</section>
|
||||
|
||||
<!-- Notifications -->
|
||||
<section class="grid grid-cols-1 md:grid-cols-2 gap-4 p-4 bg-surface-500/5 rounded-xl">
|
||||
<section
|
||||
class="bg-surface-500/5 grid grid-cols-1 gap-4 rounded-xl p-4 md:grid-cols-2">
|
||||
<div class="space-y-2">
|
||||
<h4 class="text-[10px] font-black uppercase opacity-50">Bulletin Board</h4>
|
||||
<h4 class="text-[10px] font-black uppercase opacity-50">
|
||||
Bulletin Board
|
||||
</h4>
|
||||
<label class="flex items-center space-x-2 text-xs">
|
||||
<input type="checkbox" bind:checked={cfg_json.bb_send_staff_new_email} class="checkbox checkbox-sm" />
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={cfg_json.bb_send_staff_new_email}
|
||||
class="checkbox checkbox-sm" />
|
||||
<span>Notify Staff (New)</span>
|
||||
</label>
|
||||
<label class="flex items-center space-x-2 text-xs">
|
||||
<input type="checkbox" bind:checked={cfg_json.bb_send_staff_update_email} class="checkbox checkbox-sm" />
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={
|
||||
cfg_json.bb_send_staff_update_email
|
||||
}
|
||||
class="checkbox checkbox-sm" />
|
||||
<span>Notify Staff (Update)</span>
|
||||
</label>
|
||||
<label class="flex items-center space-x-2 text-xs">
|
||||
<input type="checkbox" bind:checked={cfg_json.bb_send_poster_email} class="checkbox checkbox-sm" />
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={cfg_json.bb_send_poster_email}
|
||||
class="checkbox checkbox-sm" />
|
||||
<span>Notify Poster</span>
|
||||
</label>
|
||||
<label class="flex items-center space-x-2 text-xs">
|
||||
<input type="checkbox" bind:checked={cfg_json.bb_send_commenter_email} class="checkbox checkbox-sm" />
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={cfg_json.bb_send_commenter_email}
|
||||
class="checkbox checkbox-sm" />
|
||||
<span>Notify Commenters</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h4 class="text-[10px] font-black uppercase opacity-50">Recovery Meetings</h4>
|
||||
<h4 class="text-[10px] font-black uppercase opacity-50">
|
||||
Recovery Meetings
|
||||
</h4>
|
||||
<label class="flex items-center space-x-2 text-xs">
|
||||
<input type="checkbox" bind:checked={cfg_json.recovery_mtg_send_staff_new_email} class="checkbox checkbox-sm" />
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={
|
||||
cfg_json.recovery_mtg_send_staff_new_email
|
||||
}
|
||||
class="checkbox checkbox-sm" />
|
||||
<span>Notify Staff (New)</span>
|
||||
</label>
|
||||
<label class="flex items-center space-x-2 text-xs">
|
||||
<input type="checkbox" bind:checked={cfg_json.recovery_mtg_send_staff_update_email} class="checkbox checkbox-sm" />
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={
|
||||
cfg_json.recovery_mtg_send_staff_update_email
|
||||
}
|
||||
class="checkbox checkbox-sm" />
|
||||
<span>Notify Staff (Update)</span>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{:else if active_tab === 'raw'}
|
||||
<div class="h-[50vh] animate-in fade-in duration-200">
|
||||
<div class="animate-in fade-in h-[50vh] duration-200">
|
||||
<AE_Comp_Editor_CodeMirror
|
||||
content={raw_json_str}
|
||||
bind:new_content={raw_json_str}
|
||||
language="json"
|
||||
theme_mode={$ae_loc.theme_mode}
|
||||
class_li="h-full border border-surface-500/20 rounded-lg shadow-inner"
|
||||
/>
|
||||
class_li="h-full border border-surface-500/20 rounded-lg shadow-inner" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Action Bar -->
|
||||
<div class="flex justify-between items-center pt-4 border-t border-surface-500/10">
|
||||
<div
|
||||
class="border-surface-500/10 flex items-center justify-between border-t pt-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="flex items-center space-x-2 cursor-pointer">
|
||||
<input type="checkbox" bind:checked={cfg_json.test} class="checkbox" />
|
||||
<span class="text-xs font-bold uppercase text-warning-500">Test Mode</span>
|
||||
<label class="flex cursor-pointer items-center space-x-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={cfg_json.test}
|
||||
class="checkbox" />
|
||||
<span class="text-warning-500 text-xs font-bold uppercase"
|
||||
>Test Mode</span>
|
||||
</label>
|
||||
</div>
|
||||
<button class="btn btn-sm variant-filled-primary font-bold shadow-lg" onclick={on_save}>
|
||||
<button
|
||||
class="btn btn-sm variant-filled-primary font-bold shadow-lg"
|
||||
onclick={on_save}>
|
||||
<Save size="1.1em" class="mr-2" /> Save Config
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -24,11 +24,13 @@ export async function load_ae_obj_id__account({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Account | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_id__account() *** account_id=${account_id}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_id__account() *** account_id=${account_id}`
|
||||
);
|
||||
}
|
||||
|
||||
ae_promises.load__account_obj = await api
|
||||
.get_ae_obj_v3({
|
||||
.get_ae_obj({
|
||||
api_cfg: api_cfg,
|
||||
obj_type: 'account',
|
||||
obj_id: account_id,
|
||||
@@ -39,10 +41,11 @@ export async function load_ae_obj_id__account({
|
||||
.then(async function (account_obj_get_result) {
|
||||
if (account_obj_get_result) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__account_props({
|
||||
obj_li: [account_obj_get_result],
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__account_props({
|
||||
obj_li: [account_obj_get_result],
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'account',
|
||||
@@ -100,7 +103,7 @@ export async function load_ae_obj_li__account({
|
||||
}
|
||||
|
||||
ae_promises.load__account_obj_li = await api
|
||||
.get_ae_obj_li_v3({
|
||||
.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'account',
|
||||
enabled,
|
||||
@@ -114,10 +117,11 @@ export async function load_ae_obj_li__account({
|
||||
.then(async function (account_obj_li_get_result) {
|
||||
if (account_obj_li_get_result) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__account_props({
|
||||
obj_li: account_obj_li_get_result,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__account_props({
|
||||
obj_li: account_obj_li_get_result,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'account',
|
||||
@@ -154,7 +158,7 @@ export async function create_ae_obj__account({
|
||||
}
|
||||
|
||||
ae_promises.create__account = await api
|
||||
.create_ae_obj_v3({
|
||||
.create_ae_obj({
|
||||
api_cfg: api_cfg,
|
||||
obj_type: 'account',
|
||||
fields: data_kv,
|
||||
@@ -164,10 +168,11 @@ export async function create_ae_obj__account({
|
||||
.then(async function (account_obj_create_result) {
|
||||
if (account_obj_create_result) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__account_props({
|
||||
obj_li: [account_obj_create_result],
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__account_props({
|
||||
obj_li: [account_obj_create_result],
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'account',
|
||||
@@ -206,10 +211,13 @@ export async function update_ae_obj__account({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Account | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** update_ae_obj__account() *** account_id=${account_id}`, data_kv);
|
||||
console.log(
|
||||
`*** update_ae_obj__account() *** account_id=${account_id}`,
|
||||
data_kv
|
||||
);
|
||||
}
|
||||
|
||||
const result = await api.update_ae_obj_v3({
|
||||
const result = await api.update_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'account',
|
||||
obj_id: account_id,
|
||||
@@ -256,11 +264,13 @@ export async function delete_ae_obj_id__account({
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** delete_ae_obj_id__account() *** account_id=${account_id}`);
|
||||
console.log(
|
||||
`*** delete_ae_obj_id__account() *** account_id=${account_id}`
|
||||
);
|
||||
}
|
||||
|
||||
ae_promises.delete__account_obj = await api
|
||||
.delete_ae_obj_v3({
|
||||
.delete_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'account',
|
||||
obj_id: account_id,
|
||||
@@ -337,11 +347,15 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on;
|
||||
const name = processed_obj.name ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
|
||||
@@ -19,10 +19,12 @@ export async function load_ae_obj_id__activity_log({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_ActivityLog | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_id__activity_log() *** activity_log_id=${activity_log_id}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_id__activity_log() *** activity_log_id=${activity_log_id}`
|
||||
);
|
||||
}
|
||||
|
||||
ae_promises.load__activity_log_obj = await api.get_ae_obj_v3({
|
||||
ae_promises.load__activity_log_obj = await api.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'activity_log',
|
||||
obj_id: activity_log_id,
|
||||
@@ -61,10 +63,12 @@ export async function load_ae_obj_li__activity_log({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_ActivityLog[]> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_li__activity_log() *** for_obj_id=${for_obj_id}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_li__activity_log() *** for_obj_id=${for_obj_id}`
|
||||
);
|
||||
}
|
||||
|
||||
ae_promises.load__activity_log_obj_li = await api.get_ae_obj_li_v3({
|
||||
ae_promises.load__activity_log_obj_li = await api.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'activity_log',
|
||||
for_obj_type,
|
||||
@@ -96,15 +100,19 @@ export async function create_ae_obj__activity_log({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_ActivityLog | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** create_ae_obj__activity_log() *** account_id=${account_id}`);
|
||||
console.log(
|
||||
`*** create_ae_obj__activity_log() *** account_id=${account_id}`
|
||||
);
|
||||
}
|
||||
|
||||
if (!account_id) {
|
||||
console.log(`ERROR: Core - Activity Log - account_id required to create`);
|
||||
console.log(
|
||||
`ERROR: Core - Activity Log - account_id required to create`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
ae_promises.create__activity_log = await api.create_ae_obj_v3({
|
||||
ae_promises.create__activity_log = await api.create_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'activity_log',
|
||||
fields: {
|
||||
@@ -133,10 +141,12 @@ export async function update_ae_obj__activity_log({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_ActivityLog | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** update_ae_obj__activity_log() *** activity_log_id=${activity_log_id}`);
|
||||
console.log(
|
||||
`*** update_ae_obj__activity_log() *** activity_log_id=${activity_log_id}`
|
||||
);
|
||||
}
|
||||
|
||||
ae_promises.update__activity_log_obj = await api.update_ae_obj_v3({
|
||||
ae_promises.update__activity_log_obj = await api.update_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'activity_log',
|
||||
obj_id: activity_log_id,
|
||||
@@ -151,7 +161,6 @@ export async function update_ae_obj__activity_log({
|
||||
// Updated 2026-01-07
|
||||
|
||||
export async function qry__activity_log({
|
||||
|
||||
api_cfg,
|
||||
|
||||
account_id,
|
||||
@@ -173,9 +182,7 @@ export async function qry__activity_log({
|
||||
order_by_li = { created_on: 'DESC' },
|
||||
|
||||
log_lvl = 0
|
||||
|
||||
}: {
|
||||
|
||||
api_cfg: any;
|
||||
|
||||
account_id: string;
|
||||
@@ -197,49 +204,36 @@ export async function qry__activity_log({
|
||||
order_by_li?: Record<string, 'ASC' | 'DESC'>;
|
||||
|
||||
log_lvl?: number;
|
||||
|
||||
}): Promise<ae_ActivityLog[]> {
|
||||
|
||||
const search_query: any = {};
|
||||
|
||||
const filters: any[] = [];
|
||||
|
||||
|
||||
|
||||
if (account_id) {
|
||||
|
||||
filters.push({ field: 'account_id_random', op: 'eq', value: account_id });
|
||||
|
||||
filters.push({
|
||||
field: 'account_id_random',
|
||||
op: 'eq',
|
||||
value: account_id
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (qry_person_id) {
|
||||
|
||||
filters.push({ field: 'person_id_random', op: 'eq', value: qry_person_id });
|
||||
|
||||
filters.push({
|
||||
field: 'person_id_random',
|
||||
op: 'eq',
|
||||
value: qry_person_id
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (filters.length > 0) {
|
||||
|
||||
search_query.and = filters;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (qry_str) {
|
||||
|
||||
search_query.q = qry_str;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
ae_promises.load__activity_log_obj_li = await api.search_ae_obj_v3({
|
||||
|
||||
ae_promises.load__activity_log_obj_li = await api.search_ae_obj({
|
||||
api_cfg,
|
||||
|
||||
obj_type: 'activity_log',
|
||||
@@ -259,13 +253,9 @@ export async function qry__activity_log({
|
||||
order_by_li,
|
||||
|
||||
log_lvl
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
return ae_promises.load__activity_log_obj_li;
|
||||
|
||||
}
|
||||
|
||||
// Updated 2026-02-16
|
||||
@@ -338,14 +328,21 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
const group = processed_obj.group ?? '0';
|
||||
const priority = processed_obj.priority ? 1 : 0;
|
||||
const sort = processed_obj.sort ?? '0';
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on ?? new Date(0).toISOString();
|
||||
const updated =
|
||||
processed_obj.updated_on ??
|
||||
processed_obj.created_on ??
|
||||
new Date(0).toISOString();
|
||||
const name = processed_obj.name ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
@@ -367,5 +364,3 @@ export async function process_ae_obj__activity_log_props({
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -23,29 +23,34 @@ export async function load_ae_obj_id__address({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Address | null> {
|
||||
ae_promises.load__address_obj = await api.get_ae_obj_v3({
|
||||
api_cfg,
|
||||
obj_type: 'address',
|
||||
obj_id: address_id,
|
||||
view,
|
||||
params,
|
||||
log_lvl
|
||||
}).then(async (result) => {
|
||||
if (result) {
|
||||
if (try_cache) {
|
||||
const processed = await process_ae_obj__address_props({ obj_li: [result], log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'address',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
ae_promises.load__address_obj = await api
|
||||
.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'address',
|
||||
obj_id: address_id,
|
||||
view,
|
||||
params,
|
||||
log_lvl
|
||||
})
|
||||
.then(async (result) => {
|
||||
if (result) {
|
||||
if (try_cache) {
|
||||
const processed = await process_ae_obj__address_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'address',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
return null;
|
||||
});
|
||||
return ae_promises.load__address_obj;
|
||||
}
|
||||
|
||||
@@ -77,34 +82,39 @@ export async function load_ae_obj_li__address({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Address[]> {
|
||||
ae_promises.load__address_obj_li = await api.get_ae_obj_li_v3({
|
||||
api_cfg,
|
||||
obj_type: 'address',
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
log_lvl
|
||||
}).then(async (result) => {
|
||||
if (result && Array.isArray(result)) {
|
||||
if (try_cache) {
|
||||
const processed = await process_ae_obj__address_props({ obj_li: result, log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'address',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
ae_promises.load__address_obj_li = await api
|
||||
.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'address',
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
log_lvl
|
||||
})
|
||||
.then(async (result) => {
|
||||
if (result && Array.isArray(result)) {
|
||||
if (try_cache) {
|
||||
const processed = await process_ae_obj__address_props({
|
||||
obj_li: result,
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'address',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
return [];
|
||||
});
|
||||
return ae_promises.load__address_obj_li;
|
||||
}
|
||||
|
||||
@@ -124,7 +134,7 @@ export async function create_ae_obj__address({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Address | null> {
|
||||
const result = await api.create_ae_obj_v3({
|
||||
const result = await api.create_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'address',
|
||||
fields: {
|
||||
@@ -136,7 +146,10 @@ export async function create_ae_obj__address({
|
||||
});
|
||||
|
||||
if (result && try_cache) {
|
||||
const processed = await process_ae_obj__address_props({ obj_li: [result], log_lvl });
|
||||
const processed = await process_ae_obj__address_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'address',
|
||||
@@ -164,7 +177,7 @@ export async function update_ae_obj__address({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Address | null> {
|
||||
const result = await api.update_ae_obj_v3({
|
||||
const result = await api.update_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'address',
|
||||
obj_id: address_id,
|
||||
@@ -174,7 +187,10 @@ export async function update_ae_obj__address({
|
||||
});
|
||||
|
||||
if (result && try_cache) {
|
||||
const processed = await process_ae_obj__address_props({ obj_li: [result], log_lvl });
|
||||
const processed = await process_ae_obj__address_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'address',
|
||||
@@ -202,7 +218,7 @@ export async function delete_ae_obj_id__address({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
const result = await api.delete_ae_obj_v3({
|
||||
const result = await api.delete_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'address',
|
||||
obj_id: address_id,
|
||||
@@ -269,7 +285,9 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
}
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
@@ -290,4 +308,4 @@ export async function process_ae_obj__address_props({
|
||||
obj_type: 'address',
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,29 +23,34 @@ export async function load_ae_obj_id__contact({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Contact | null> {
|
||||
ae_promises.load__contact_obj = await api.get_ae_obj_v3({
|
||||
api_cfg,
|
||||
obj_type: 'contact',
|
||||
obj_id: contact_id,
|
||||
view,
|
||||
params,
|
||||
log_lvl
|
||||
}).then(async (result) => {
|
||||
if (result) {
|
||||
if (try_cache) {
|
||||
const processed = await process_ae_obj__contact_props({ obj_li: [result], log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'contact',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
ae_promises.load__contact_obj = await api
|
||||
.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'contact',
|
||||
obj_id: contact_id,
|
||||
view,
|
||||
params,
|
||||
log_lvl
|
||||
})
|
||||
.then(async (result) => {
|
||||
if (result) {
|
||||
if (try_cache) {
|
||||
const processed = await process_ae_obj__contact_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'contact',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
return null;
|
||||
});
|
||||
return ae_promises.load__contact_obj;
|
||||
}
|
||||
|
||||
@@ -75,34 +80,39 @@ export async function load_ae_obj_li__contact({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Contact[]> {
|
||||
ae_promises.load__contact_obj_li = await api.get_ae_obj_li_v3({
|
||||
api_cfg,
|
||||
obj_type: 'contact',
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
log_lvl
|
||||
}).then(async (result) => {
|
||||
if (result && Array.isArray(result)) {
|
||||
if (try_cache) {
|
||||
const processed = await process_ae_obj__contact_props({ obj_li: result, log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'contact',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
ae_promises.load__contact_obj_li = await api
|
||||
.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'contact',
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
log_lvl
|
||||
})
|
||||
.then(async (result) => {
|
||||
if (result && Array.isArray(result)) {
|
||||
if (try_cache) {
|
||||
const processed = await process_ae_obj__contact_props({
|
||||
obj_li: result,
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'contact',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
return [];
|
||||
});
|
||||
return ae_promises.load__contact_obj_li;
|
||||
}
|
||||
|
||||
@@ -122,7 +132,7 @@ export async function create_ae_obj__contact({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Contact | null> {
|
||||
const result = await api.create_ae_obj_v3({
|
||||
const result = await api.create_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'contact',
|
||||
fields: {
|
||||
@@ -134,7 +144,10 @@ export async function create_ae_obj__contact({
|
||||
});
|
||||
|
||||
if (result && try_cache) {
|
||||
const processed = await process_ae_obj__contact_props({ obj_li: [result], log_lvl });
|
||||
const processed = await process_ae_obj__contact_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'contact',
|
||||
@@ -162,7 +175,7 @@ export async function update_ae_obj__contact({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Contact | null> {
|
||||
const result = await api.update_ae_obj_v3({
|
||||
const result = await api.update_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'contact',
|
||||
obj_id: contact_id,
|
||||
@@ -172,7 +185,10 @@ export async function update_ae_obj__contact({
|
||||
});
|
||||
|
||||
if (result && try_cache) {
|
||||
const processed = await process_ae_obj__contact_props({ obj_li: [result], log_lvl });
|
||||
const processed = await process_ae_obj__contact_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'contact',
|
||||
@@ -200,7 +216,7 @@ export async function delete_ae_obj_id__contact({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
const result = await api.delete_ae_obj_v3({
|
||||
const result = await api.delete_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'contact',
|
||||
obj_id: contact_id,
|
||||
@@ -267,7 +283,9 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
}
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
@@ -288,4 +306,4 @@ export async function process_ae_obj__contact_props({
|
||||
obj_type: 'contact',
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export async function load_ae_obj_id__organization({
|
||||
organization_id: string;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Organization | null> {
|
||||
return await api.get_ae_obj_v3({
|
||||
return await api.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'organization',
|
||||
obj_id: organization_id,
|
||||
|
||||
@@ -28,7 +28,7 @@ export async function load_ae_obj_id__person({
|
||||
}
|
||||
|
||||
ae_promises.load__person_obj = await api
|
||||
.get_ae_obj_v3({
|
||||
.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'person',
|
||||
obj_id: person_id,
|
||||
@@ -39,10 +39,12 @@ export async function load_ae_obj_id__person({
|
||||
.then(async function (result) {
|
||||
if (result) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__person_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj_li = await process_ae_obj__person_props(
|
||||
{
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
}
|
||||
);
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'person',
|
||||
@@ -98,7 +100,9 @@ export async function load_ae_obj_li__person({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Person[]> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_li__person() *** for_obj_id=${for_obj_id}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_li__person() *** for_obj_id=${for_obj_id}`
|
||||
);
|
||||
}
|
||||
|
||||
let promise;
|
||||
@@ -109,11 +113,19 @@ export async function load_ae_obj_li__person({
|
||||
};
|
||||
|
||||
if (qry_user_id) {
|
||||
search_query.and.push({ field: 'user_id_random', op: 'eq', value: qry_user_id });
|
||||
search_query.and.push({
|
||||
field: 'user_id_random',
|
||||
op: 'eq',
|
||||
value: qry_user_id
|
||||
});
|
||||
}
|
||||
|
||||
if (qry_email) {
|
||||
search_query.and.push({ field: 'primary_email', op: 'eq', value: qry_email });
|
||||
search_query.and.push({
|
||||
field: 'primary_email',
|
||||
op: 'eq',
|
||||
value: qry_email
|
||||
});
|
||||
}
|
||||
|
||||
if (for_obj_id) {
|
||||
@@ -136,7 +148,7 @@ export async function load_ae_obj_li__person({
|
||||
search_query.and.push({ field: 'hide', op: 'eq', value: false });
|
||||
}
|
||||
|
||||
promise = api.search_ae_obj_v3({
|
||||
promise = api.search_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'person',
|
||||
search_query,
|
||||
@@ -146,7 +158,7 @@ export async function load_ae_obj_li__person({
|
||||
log_lvl
|
||||
});
|
||||
} else {
|
||||
promise = api.get_ae_obj_li_v3({
|
||||
promise = api.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'person',
|
||||
for_obj_type,
|
||||
@@ -161,26 +173,30 @@ export async function load_ae_obj_li__person({
|
||||
});
|
||||
}
|
||||
|
||||
ae_promises.load__person_obj_li = await promise.then(async function (result_li) {
|
||||
if (result_li) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__person_props({
|
||||
obj_li: result_li,
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'person',
|
||||
obj_li: processed_obj_li,
|
||||
properties_to_save: properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
ae_promises.load__person_obj_li = await promise.then(
|
||||
async function (result_li) {
|
||||
if (result_li) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__person_props(
|
||||
{
|
||||
obj_li: result_li,
|
||||
log_lvl
|
||||
}
|
||||
);
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'person',
|
||||
obj_li: processed_obj_li,
|
||||
properties_to_save: properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return result_li;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
return result_li;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
return ae_promises.load__person_obj_li;
|
||||
}
|
||||
@@ -207,7 +223,7 @@ export async function create_ae_obj__person({
|
||||
if (account_id) fields.account_id_random = account_id;
|
||||
if (user_id) fields.user_id_random = user_id;
|
||||
|
||||
const result = await api.create_ae_obj_v3({
|
||||
const result = await api.create_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'person',
|
||||
fields,
|
||||
@@ -248,7 +264,7 @@ export async function update_ae_obj__person({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Person | null> {
|
||||
const result = await api.update_ae_obj_v3({
|
||||
const result = await api.update_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'person',
|
||||
obj_id: person_id,
|
||||
@@ -290,7 +306,7 @@ export async function delete_ae_obj_id__person({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
const result = await api.delete_ae_obj_v3({
|
||||
const result = await api.delete_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'person',
|
||||
obj_id: person_id,
|
||||
@@ -411,11 +427,15 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on;
|
||||
const name = processed_obj.full_name ?? processed_obj.name ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
import { ae_loc } from '$lib/stores/ae_stores';
|
||||
import { get } from 'svelte/store';
|
||||
import { api } from '$lib/api/api';
|
||||
|
||||
import { db_save_ae_obj_li__ae_obj } from '$lib/ae_core/core__idb_dexie';
|
||||
@@ -11,6 +13,88 @@ const ae_promises: key_val = {};
|
||||
* --- SITE CRUD ---
|
||||
*/
|
||||
|
||||
// Legacy version
|
||||
// export async function lookup_site_domain({
|
||||
// api_cfg,
|
||||
// fqdn,
|
||||
// view = 'default',
|
||||
// log_lvl = 0
|
||||
// }: {
|
||||
// api_cfg: any;
|
||||
// fqdn: string;
|
||||
// view?: string;
|
||||
// log_lvl?: number;
|
||||
// }): Promise<ae_SiteDomain | null> {
|
||||
// if (log_lvl) {
|
||||
// console.log(`*** lookup_site_domain() *** fqdn=${fqdn}`);
|
||||
// }
|
||||
|
||||
// try {
|
||||
// // We use get_ae_obj_id_crud because we are looking up by a unique field (fqdn) rather than ID.
|
||||
// // This is the older method that uses the /crud/site/domain/:id endpoint.
|
||||
// const result = await api.get_ae_obj_id_crud({
|
||||
// api_cfg,
|
||||
// no_account_id: true,
|
||||
// obj_type: 'site_domain',
|
||||
// obj_id: fqdn,
|
||||
// use_alt_table: true,
|
||||
// use_alt_base: true,
|
||||
// log_lvl
|
||||
// });
|
||||
|
||||
// if (result) {
|
||||
// // Standardize and save to cache
|
||||
// const processed_obj_li = await process_ae_obj__site_domain_props({
|
||||
// obj_li: [result],
|
||||
// log_lvl
|
||||
// });
|
||||
// await db_save_ae_obj_li__ae_obj({
|
||||
// db_instance: db_core,
|
||||
// table_name: 'site_domain',
|
||||
// obj_li: processed_obj_li,
|
||||
// properties_to_save: properties_to_save__site_domain,
|
||||
// log_lvl
|
||||
// });
|
||||
// return result;
|
||||
// }
|
||||
// } catch (error: any) {
|
||||
// console.log('Site domain lookup failed (API Error).', error);
|
||||
// }
|
||||
|
||||
// if (log_lvl) console.log('Attempting to load site domain from local cache...');
|
||||
// const cached = await db_core.site_domain.where('fqdn').equals(fqdn).first();
|
||||
|
||||
// if (cached) {
|
||||
// return cached as any;
|
||||
// }
|
||||
|
||||
// // CRITICAL FALLBACK: If both API and Cache fail, return a "Ghost" site domain object
|
||||
// // to prevent the 403 error from blocking the UI. Page components will handle empty data.
|
||||
// console.error('AE_SITE_CRITICAL: Site domain lookup failed API and CACHE. Returning ghost object.');
|
||||
// return {
|
||||
// id: 'ghost',
|
||||
// id_random: 'ghost',
|
||||
// site_domain_id: 'ghost',
|
||||
// site_domain_id_random: 'ghost',
|
||||
// site_id: 'ghost',
|
||||
// site_id_random: 'ghost',
|
||||
// account_id: 'ghost',
|
||||
// account_id_random: 'ghost',
|
||||
// account_code: 'ghost',
|
||||
// account_name: 'Ghost Account (Offline)',
|
||||
// fqdn: fqdn,
|
||||
// enable: '1',
|
||||
// header_image_path: '',
|
||||
// style_href: '',
|
||||
// google_tracking_id: '',
|
||||
// access_code_kv_json: {},
|
||||
// cfg_json: {},
|
||||
// access_key: '',
|
||||
// site_domain_access_key: ''
|
||||
// } as any;
|
||||
// }
|
||||
|
||||
// Updated 2026-01-26 (Cache-First Optimization)
|
||||
export async function lookup_site_domain({
|
||||
api_cfg,
|
||||
fqdn,
|
||||
@@ -23,88 +107,7 @@ export async function lookup_site_domain({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_SiteDomain | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** lookup_site_domain() *** fqdn=${fqdn}`);
|
||||
}
|
||||
|
||||
try {
|
||||
// We use get_ae_obj_id_crud because we are looking up by a unique field (fqdn) rather than ID.
|
||||
// This is the older method that uses the /crud/site/domain/:id endpoint.
|
||||
const result = await api.get_ae_obj_id_crud({
|
||||
api_cfg,
|
||||
no_account_id: true,
|
||||
obj_type: 'site_domain',
|
||||
obj_id: fqdn,
|
||||
use_alt_table: true,
|
||||
use_alt_base: true,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (result) {
|
||||
// Standardize and save to cache
|
||||
const processed_obj_li = await process_ae_obj__site_domain_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'site_domain',
|
||||
obj_li: processed_obj_li,
|
||||
properties_to_save: properties_to_save__site_domain,
|
||||
log_lvl
|
||||
});
|
||||
return result;
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log('Site domain lookup failed (API Error).', error);
|
||||
}
|
||||
|
||||
if (log_lvl) console.log('Attempting to load site domain from local cache...');
|
||||
const cached = await db_core.site_domain.where('fqdn').equals(fqdn).first();
|
||||
|
||||
if (cached) {
|
||||
return cached as any;
|
||||
}
|
||||
|
||||
// CRITICAL FALLBACK: If both API and Cache fail, return a "Ghost" site domain object
|
||||
// to prevent the 403 error from blocking the UI. Page components will handle empty data.
|
||||
console.error('AE_SITE_CRITICAL: Site domain lookup failed API and CACHE. Returning ghost object.');
|
||||
return {
|
||||
id: 'ghost',
|
||||
id_random: 'ghost',
|
||||
site_domain_id: 'ghost',
|
||||
site_domain_id_random: 'ghost',
|
||||
site_id: 'ghost',
|
||||
site_id_random: 'ghost',
|
||||
account_id: 'ghost',
|
||||
account_id_random: 'ghost',
|
||||
account_code: 'ghost',
|
||||
account_name: 'Ghost Account (Offline)',
|
||||
fqdn: fqdn,
|
||||
enable: '1',
|
||||
header_image_path: '',
|
||||
style_href: '',
|
||||
google_tracking_id: '',
|
||||
access_code_kv_json: {},
|
||||
cfg_json: {},
|
||||
access_key: '',
|
||||
site_domain_access_key: ''
|
||||
} as any;
|
||||
}
|
||||
|
||||
// Updated 2026-01-26 (Cache-First Optimization)
|
||||
export async function lookup_site_domain_v3({
|
||||
api_cfg,
|
||||
fqdn,
|
||||
view = 'default',
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
fqdn: string;
|
||||
view?: string;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_SiteDomain | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** lookup_site_domain_v3() *** fqdn=${fqdn} (Cache-First)`);
|
||||
console.log(`*** lookup_site_domain() *** fqdn=${fqdn} (Cache-First)`);
|
||||
}
|
||||
|
||||
// 1. FAST PATH: Check local cache first
|
||||
@@ -112,11 +115,19 @@ export async function lookup_site_domain_v3({
|
||||
try {
|
||||
cached = await db_core.site_domain.where('fqdn').equals(fqdn).first();
|
||||
if (cached) {
|
||||
if (log_lvl) console.log('BOOTSTRAP: Cache hit. Returning cached site domain immediately.');
|
||||
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
'BOOTSTRAP: Cache hit. Returning cached site domain immediately.'
|
||||
);
|
||||
|
||||
// Trigger background refresh to keep cache fresh, but don't await it
|
||||
_refresh_site_domain_v3_background({ api_cfg, fqdn, view, log_lvl: 0 });
|
||||
|
||||
_refresh_site_domain_background({
|
||||
api_cfg,
|
||||
fqdn,
|
||||
view,
|
||||
log_lvl: 0
|
||||
});
|
||||
|
||||
return cached as any;
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -124,26 +135,36 @@ export async function lookup_site_domain_v3({
|
||||
}
|
||||
|
||||
// 2. SLOW PATH: Wait for API if cache is empty
|
||||
return await _refresh_site_domain_v3_background({ api_cfg, fqdn, view, log_lvl });
|
||||
return await _refresh_site_domain_background({
|
||||
api_cfg,
|
||||
fqdn,
|
||||
view,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal helper to perform the actual API fetch and cache update
|
||||
*/
|
||||
async function _refresh_site_domain_v3_background({ api_cfg, fqdn, view, log_lvl }: any) {
|
||||
async function _refresh_site_domain_background({
|
||||
api_cfg,
|
||||
fqdn,
|
||||
view,
|
||||
log_lvl
|
||||
}: any) {
|
||||
try {
|
||||
const guest_api_cfg = { ...api_cfg };
|
||||
guest_api_cfg.headers = { ...api_cfg.headers };
|
||||
|
||||
|
||||
const auth_props = [
|
||||
'x-account-id',
|
||||
'Authorization',
|
||||
'authorization',
|
||||
'jwt',
|
||||
'x-account-id',
|
||||
'Authorization',
|
||||
'authorization',
|
||||
'jwt',
|
||||
'JWT'
|
||||
];
|
||||
|
||||
auth_props.forEach(prop => {
|
||||
|
||||
auth_props.forEach((prop) => {
|
||||
delete guest_api_cfg.headers[prop];
|
||||
delete guest_api_cfg.headers[prop.toLowerCase()];
|
||||
delete guest_api_cfg.headers[prop.replaceAll('-', '_')];
|
||||
@@ -155,7 +176,7 @@ async function _refresh_site_domain_v3_background({ api_cfg, fqdn, view, log_lvl
|
||||
and: [{ field: 'fqdn', op: 'eq', value: fqdn }]
|
||||
};
|
||||
|
||||
const result_li = await api.search_ae_obj_v3({
|
||||
const result_li = await api.search_ae_obj({
|
||||
api_cfg: guest_api_cfg,
|
||||
obj_type: 'site_domain',
|
||||
search_query,
|
||||
@@ -179,6 +200,25 @@ async function _refresh_site_domain_v3_background({ api_cfg, fqdn, view, log_lvl
|
||||
properties_to_save: properties_to_save__site_domain,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
// WHY: The fast-path returns stale Dexie cache, then this background refresh
|
||||
// runs after the page renders. If cfg_json changed server-side (e.g. a Novi
|
||||
// API key was added), the stale cfg is already in $ae_loc. We push the fresh
|
||||
// cfg_json into the store here so any layout tracking it (e.g. IDAA Novi
|
||||
// verification) gets notified and can retry with the correct config.
|
||||
if (result.cfg_json) {
|
||||
const current_cfg = get(ae_loc).site_cfg_json;
|
||||
if (
|
||||
JSON.stringify(current_cfg) !==
|
||||
JSON.stringify(result.cfg_json)
|
||||
) {
|
||||
ae_loc.update((loc) => ({
|
||||
...loc,
|
||||
site_cfg_json: result.cfg_json
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} catch (error: any) {
|
||||
@@ -207,7 +247,7 @@ export async function load_ae_obj_id__site({
|
||||
}
|
||||
|
||||
ae_promises.load__site_obj = await api
|
||||
.get_ae_obj_v3({
|
||||
.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'site',
|
||||
obj_id: site_id,
|
||||
@@ -278,7 +318,7 @@ export async function load_ae_obj_li__site({
|
||||
}
|
||||
|
||||
ae_promises.load__site_obj_li = await api
|
||||
.get_ae_obj_li_v3({
|
||||
.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'site',
|
||||
for_obj_type,
|
||||
@@ -331,7 +371,7 @@ export async function create_ae_obj__site({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Site | null> {
|
||||
const result = await api.create_ae_obj_v3({
|
||||
const result = await api.create_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'site',
|
||||
fields: {
|
||||
@@ -375,7 +415,7 @@ export async function update_ae_obj__site({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Site | null> {
|
||||
const result = await api.update_ae_obj_v3({
|
||||
const result = await api.update_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'site',
|
||||
obj_id: site_id,
|
||||
@@ -417,7 +457,7 @@ export async function delete_ae_obj_id__site({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
const result = await api.delete_ae_obj_v3({
|
||||
const result = await api.delete_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'site',
|
||||
obj_id: site_id,
|
||||
@@ -464,7 +504,7 @@ export async function load_ae_obj_li__site_domain({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_SiteDomain[]> {
|
||||
ae_promises.load__site_domain_li = await api
|
||||
.get_nested_obj_li_v3({
|
||||
.get_nested_obj_li({
|
||||
api_cfg,
|
||||
parent_type: 'site',
|
||||
parent_id: site_id,
|
||||
@@ -480,11 +520,12 @@ export async function load_ae_obj_li__site_domain({
|
||||
.then(async function (domain_li) {
|
||||
if (domain_li) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__site_domain_props({
|
||||
obj_li: domain_li,
|
||||
site_id,
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__site_domain_props({
|
||||
obj_li: domain_li,
|
||||
site_id,
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'site_domain',
|
||||
@@ -518,7 +559,7 @@ export async function create_ae_obj__site_domain({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_SiteDomain | null> {
|
||||
const result = await api.create_nested_obj_v3({
|
||||
const result = await api.create_nested_obj({
|
||||
api_cfg,
|
||||
parent_type: 'site',
|
||||
parent_id: site_id,
|
||||
@@ -564,7 +605,7 @@ export async function update_ae_obj__site_domain({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_SiteDomain | null> {
|
||||
const result = await api.update_nested_obj_v3({
|
||||
const result = await api.update_nested_obj({
|
||||
api_cfg,
|
||||
parent_type: 'site',
|
||||
parent_id: site_id,
|
||||
@@ -611,7 +652,7 @@ export async function delete_ae_obj_id__site_domain({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
const result = await api.delete_nested_ae_obj_v3({
|
||||
const result = await api.delete_nested_ae_obj({
|
||||
api_cfg,
|
||||
parent_type: 'site',
|
||||
parent_id: site_id,
|
||||
@@ -728,11 +769,15 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on;
|
||||
const name = processed_obj.name ?? processed_obj.fqdn ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
|
||||
@@ -28,7 +28,7 @@ export async function load_ae_obj_id__user({
|
||||
}
|
||||
|
||||
ae_promises.load__user_obj = await api
|
||||
.get_ae_obj_v3({
|
||||
.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'user',
|
||||
obj_id: user_id,
|
||||
@@ -93,7 +93,9 @@ export async function load_ae_obj_li__user({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_User[]> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_li__user() *** for_obj_id=${for_obj_id} include_global=${include_global} qry_str=${qry_str}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_li__user() *** for_obj_id=${for_obj_id} include_global=${include_global} qry_str=${qry_str}`
|
||||
);
|
||||
}
|
||||
|
||||
// SCENARIO A: Text Search
|
||||
@@ -107,12 +109,20 @@ export async function load_ae_obj_li__user({
|
||||
]
|
||||
});
|
||||
} else if (for_obj_id) {
|
||||
search_query.and.push({ field: `account_id_random`, op: 'eq', value: for_obj_id });
|
||||
search_query.and.push({
|
||||
field: `account_id_random`,
|
||||
op: 'eq',
|
||||
value: for_obj_id
|
||||
});
|
||||
} else if (include_global) {
|
||||
search_query.and.push({ field: `account_id_random`, op: 'eq', value: null });
|
||||
search_query.and.push({
|
||||
field: `account_id_random`,
|
||||
op: 'eq',
|
||||
value: null
|
||||
});
|
||||
}
|
||||
|
||||
return await api.search_ae_obj_v3({
|
||||
return await api.search_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'user',
|
||||
search_query,
|
||||
@@ -130,13 +140,33 @@ export async function load_ae_obj_li__user({
|
||||
if (for_obj_id && include_global) {
|
||||
if (log_lvl) console.log('Strategy: Multi-call (Account + Global)');
|
||||
const [acct_users, global_users] = await Promise.all([
|
||||
load_ae_obj_li__user({ api_cfg, for_obj_id, include_global: false, enabled, hidden, view, limit, log_lvl }),
|
||||
load_ae_obj_li__user({ api_cfg, for_obj_id: null, include_global: true, enabled, hidden, view, limit, log_lvl })
|
||||
load_ae_obj_li__user({
|
||||
api_cfg,
|
||||
for_obj_id,
|
||||
include_global: false,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
log_lvl
|
||||
}),
|
||||
load_ae_obj_li__user({
|
||||
api_cfg,
|
||||
for_obj_id: null,
|
||||
include_global: true,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
log_lvl
|
||||
})
|
||||
]);
|
||||
|
||||
|
||||
// Merge and unique-ify by ID
|
||||
const merged = [...acct_users, ...global_users];
|
||||
const unique = Array.from(new Map(merged.map(u => [u.user_id_random, u])).values());
|
||||
const unique = Array.from(
|
||||
new Map(merged.map((u) => [u.user_id_random, u])).values()
|
||||
);
|
||||
return unique;
|
||||
}
|
||||
|
||||
@@ -147,7 +177,7 @@ export async function load_ae_obj_li__user({
|
||||
const search_query = {
|
||||
and: [{ field: 'account_id_random', op: 'eq', value: null }]
|
||||
};
|
||||
return await api.search_ae_obj_v3({
|
||||
return await api.search_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'user',
|
||||
search_query,
|
||||
@@ -162,8 +192,9 @@ export async function load_ae_obj_li__user({
|
||||
}
|
||||
|
||||
// SCENARIO D: Account Only or Everything (confirmed working List API)
|
||||
if (log_lvl) console.log(`Strategy: Standard List API (for_obj_id=${for_obj_id})`);
|
||||
return await api.get_ae_obj_li_v3({
|
||||
if (log_lvl)
|
||||
console.log(`Strategy: Standard List API (for_obj_id=${for_obj_id})`);
|
||||
return await api.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'user',
|
||||
for_obj_type: for_obj_id ? for_obj_type : undefined,
|
||||
@@ -197,7 +228,7 @@ export async function create_ae_obj__user({
|
||||
const fields: key_val = { ...data_kv };
|
||||
if (account_id) fields.account_id_random = account_id;
|
||||
|
||||
const result = await api.create_ae_obj_v3({
|
||||
const result = await api.create_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'user',
|
||||
fields,
|
||||
@@ -238,7 +269,7 @@ export async function update_ae_obj__user({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_User | null> {
|
||||
const result = await api.update_ae_obj_v3({
|
||||
const result = await api.update_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'user',
|
||||
obj_id: user_id,
|
||||
@@ -280,7 +311,7 @@ export async function delete_ae_obj_id__user({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
const result = await api.delete_ae_obj_v3({
|
||||
const result = await api.delete_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'user',
|
||||
obj_id: user_id,
|
||||
@@ -383,7 +414,10 @@ export async function auth_ae_obj__username_password({
|
||||
});
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('ae_promises.auth__username_password:', ae_promises.auth__username_password);
|
||||
console.log(
|
||||
'ae_promises.auth__username_password:',
|
||||
ae_promises.auth__username_password
|
||||
);
|
||||
}
|
||||
return ae_promises.auth__username_password;
|
||||
}
|
||||
@@ -450,7 +484,10 @@ export async function auth_ae_obj__user_id_user_auth_key({
|
||||
});
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('ae_promises.auth__user_id_user_key:', ae_promises.auth__user_id_user_key);
|
||||
console.log(
|
||||
'ae_promises.auth__user_id_user_key:',
|
||||
ae_promises.auth__user_id_user_key
|
||||
);
|
||||
}
|
||||
return ae_promises.auth__user_id_user_key;
|
||||
}
|
||||
@@ -525,7 +562,9 @@ export async function qry_ae_obj_li__user_email({
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** qry_ae_obj_li__user_email() *** account_id=${account_id} email=${email}`);
|
||||
console.log(
|
||||
`*** qry_ae_obj_li__user_email() *** account_id=${account_id} email=${email}`
|
||||
);
|
||||
}
|
||||
|
||||
const endpoint = '/user/lookup_email';
|
||||
@@ -540,7 +579,7 @@ export async function qry_ae_obj_li__user_email({
|
||||
|
||||
params['email'] = email; // Required
|
||||
params['null_account_id'] = null_account_id || false;
|
||||
|
||||
|
||||
ae_promises.qry__user_email = await api
|
||||
.get_object({
|
||||
api_cfg: use_api_cfg,
|
||||
@@ -676,11 +715,15 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on;
|
||||
const name = processed_obj.username ?? processed_obj.name ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
@@ -701,4 +744,4 @@ export async function process_ae_obj__user_props({
|
||||
obj_type: 'user',
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,10 @@ import {
|
||||
auth_ae_obj__user_id_change_password
|
||||
} from '$lib/ae_core/ae_core__user';
|
||||
|
||||
import { generate_qr_code, js_generate_qr_code } from '$lib/ae_core/core__qr_code';
|
||||
import {
|
||||
generate_qr_code,
|
||||
js_generate_qr_code
|
||||
} from '$lib/ae_core/core__qr_code';
|
||||
|
||||
import { check_hosted_file_obj_w_hash } from '$lib/ae_core/core__check_hosted_file_obj_w_hash';
|
||||
|
||||
@@ -161,7 +164,9 @@ async function load_ae_obj_code__data_store({
|
||||
}
|
||||
|
||||
if (!get_ds_result.data_store_id_random) {
|
||||
console.log('*ae_func* Something went wrong? No data store ID found.');
|
||||
console.log(
|
||||
'*ae_func* Something went wrong? No data store ID found.'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -240,7 +245,10 @@ async function load_ae_obj_code__data_store({
|
||||
get_ds_result
|
||||
);
|
||||
}
|
||||
localStorage.setItem(`${key_prefix}${code}`, JSON.stringify(get_ds_result));
|
||||
localStorage.setItem(
|
||||
`${key_prefix}${code}`,
|
||||
JSON.stringify(get_ds_result)
|
||||
);
|
||||
} else {
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
@@ -327,102 +335,102 @@ async function update_ae_obj_id_crud({
|
||||
return ae_promises.api_update__ae_obj;
|
||||
}
|
||||
|
||||
// Core - Already imported above
|
||||
// import { load_ae_obj_id__person } from "$lib/ae_core/core__person";
|
||||
// import { load_ae_obj_id__user } from "$lib/ae_core/core__user";
|
||||
// // Core - Already imported above
|
||||
// // import { load_ae_obj_id__person } from "$lib/ae_core/core__person";
|
||||
// // import { load_ae_obj_id__user } from "$lib/ae_core/core__user";
|
||||
|
||||
// Additional Modules
|
||||
import { load_ae_obj_id__archive } from '$lib/ae_archives/ae_archives__archive';
|
||||
import { load_ae_obj_id__archive_content } from '$lib/ae_archives/ae_archives__archive_content';
|
||||
// // Additional Modules
|
||||
// import { load_ae_obj_id__archive } from '$lib/ae_archives/ae_archives__archive';
|
||||
// import { load_ae_obj_id__archive_content } from '$lib/ae_archives/ae_archives__archive_content';
|
||||
|
||||
import { load_ae_obj_id__event } from '$lib/ae_events/ae_events__event';
|
||||
// import { load_ae_obj_id__event_badge } from "$lib/ae_events/ae_events__event_badge";
|
||||
import { load_ae_obj_id__event_exhibit } from '$lib/ae_events/ae_events__exhibit';
|
||||
import { load_ae_obj_id__event_device } from '$lib/ae_events/ae_events__event_device';
|
||||
// import { load_ae_obj_id__event_exhibit } from "$lib/ae_events/ae_events__event_exhibit";
|
||||
import { load_ae_obj_id__event_file } from '$lib/ae_events/ae_events__event_file';
|
||||
import { load_ae_obj_id__event_location } from '$lib/ae_events/ae_events__event_location';
|
||||
import { load_ae_obj_id__event_presentation } from '$lib/ae_events/ae_events__event_presentation';
|
||||
import { load_ae_obj_id__event_presenter } from '$lib/ae_events/ae_events__event_presenter';
|
||||
import { load_ae_obj_id__event_session } from '$lib/ae_events/ae_events__event_session';
|
||||
// import { load_ae_obj_id__event } from '$lib/ae_events/ae_events__event';
|
||||
// // import { load_ae_obj_id__event_badge } from "$lib/ae_events/ae_events__event_badge";
|
||||
// import { load_ae_obj_id__event_exhibit } from '$lib/ae_events/ae_events__exhibit';
|
||||
// import { load_ae_obj_id__event_device } from '$lib/ae_events/ae_events__event_device';
|
||||
// // import { load_ae_obj_id__event_exhibit } from "$lib/ae_events/ae_events__event_exhibit";
|
||||
// import { load_ae_obj_id__event_file } from '$lib/ae_events/ae_events__event_file';
|
||||
// import { load_ae_obj_id__event_location } from '$lib/ae_events/ae_events__event_location';
|
||||
// import { load_ae_obj_id__event_presentation } from '$lib/ae_events/ae_events__event_presentation';
|
||||
// import { load_ae_obj_id__event_presenter } from '$lib/ae_events/ae_events__event_presenter';
|
||||
// import { load_ae_obj_id__event_session } from '$lib/ae_events/ae_events__event_session';
|
||||
|
||||
import { load_ae_obj_id__journal } from '$lib/ae_journals/ae_journals__journal';
|
||||
import { load_ae_obj_id__journal_entry } from '$lib/ae_journals/ae_journals__journal_entry';
|
||||
// import { load_ae_obj_id__journal } from '$lib/ae_journals/ae_journals__journal';
|
||||
// import { load_ae_obj_id__journal_entry } from '$lib/ae_journals/ae_journals__journal_entry';
|
||||
|
||||
import { load_ae_obj_id__post } from '$lib/ae_posts/ae_posts__post';
|
||||
import { load_ae_obj_id__post_comment } from '$lib/ae_posts/ae_posts__post_comment';
|
||||
// import { load_ae_obj_id__post } from '$lib/ae_posts/ae_posts__post';
|
||||
// import { load_ae_obj_id__post_comment } from '$lib/ae_posts/ae_posts__post_comment';
|
||||
|
||||
// Updated 2025-09-30
|
||||
async function update_ae_obj_id_crud_v2({
|
||||
api_cfg,
|
||||
object_type,
|
||||
object_id,
|
||||
object_reload = false,
|
||||
field_name,
|
||||
new_field_value,
|
||||
params = {},
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
object_type: string;
|
||||
object_id: string;
|
||||
object_reload?: boolean;
|
||||
field_name: string;
|
||||
new_field_value: any;
|
||||
params?: any | key_val;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`*** update_ae_obj_id_crud_v2() *** object_type=${object_type}, object_id=${object_id}, object_reload=${object_reload}, field_name=${field_name}, new_field_value=`,
|
||||
new_field_value
|
||||
);
|
||||
}
|
||||
// async function update_ae_obj_id_crud_v2({
|
||||
// api_cfg,
|
||||
// object_type,
|
||||
// object_id,
|
||||
// object_reload = false,
|
||||
// field_name,
|
||||
// new_field_value,
|
||||
// params = {},
|
||||
// log_lvl = 0
|
||||
// }: {
|
||||
// api_cfg: any;
|
||||
// object_type: string;
|
||||
// object_id: string;
|
||||
// object_reload?: boolean;
|
||||
// field_name: string;
|
||||
// new_field_value: any;
|
||||
// params?: any | key_val;
|
||||
// log_lvl?: number;
|
||||
// }) {
|
||||
// if (log_lvl) {
|
||||
// console.log(
|
||||
// `*** update_ae_obj_id_crud_v2() *** object_type=${object_type}, object_id=${object_id}, object_reload=${object_reload}, field_name=${field_name}, new_field_value=`,
|
||||
// new_field_value
|
||||
// );
|
||||
// }
|
||||
|
||||
const results = await api.update_ae_obj_id_crud({
|
||||
api_cfg: api_cfg,
|
||||
obj_type: object_type,
|
||||
obj_id: object_id,
|
||||
field_name: field_name,
|
||||
field_value: new_field_value,
|
||||
key: api_cfg.api_crud_super_key,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
// const results = await api.update_ae_obj_id_crud({
|
||||
// api_cfg: api_cfg,
|
||||
// obj_type: object_type,
|
||||
// obj_id: object_id,
|
||||
// field_name: field_name,
|
||||
// field_value: new_field_value,
|
||||
// key: api_cfg.api_crud_super_key,
|
||||
// log_lvl: log_lvl
|
||||
// });
|
||||
|
||||
if (results) {
|
||||
if (log_lvl) {
|
||||
console.log(`Patched - Field Name: ${field_name} with new Field Value: ${new_field_value}`);
|
||||
}
|
||||
// if (results) {
|
||||
// if (log_lvl) {
|
||||
// console.log(`Patched - Field Name: ${field_name} with new Field Value: ${new_field_value}`);
|
||||
// }
|
||||
|
||||
if (object_reload) {
|
||||
if (log_lvl) {
|
||||
console.log(`Reloading the object after patching...`);
|
||||
}
|
||||
// Trigger reloads based on object type. These are fire-and-forget or awaited internally by the library functions.
|
||||
if (object_type == 'person') load_ae_obj_id__person({ api_cfg, person_id: object_id, log_lvl });
|
||||
if (object_type == 'archive') load_ae_obj_id__archive({ api_cfg, archive_id: object_id, log_lvl });
|
||||
if (object_type == 'archive_content') load_ae_obj_id__archive_content({ api_cfg, archive_content_id: object_id, log_lvl });
|
||||
if (object_type == 'journal') load_ae_obj_id__journal({ api_cfg, journal_id: object_id, log_lvl });
|
||||
if (object_type == 'journal_entry') load_ae_obj_id__journal_entry({ api_cfg, journal_entry_id: object_id, log_lvl });
|
||||
if (object_type == 'event') load_ae_obj_id__event({ api_cfg, event_id: object_id, log_lvl });
|
||||
if (object_type == 'event_exhibit') load_ae_obj_id__event_exhibit({ api_cfg, exhibit_id: object_id, log_lvl });
|
||||
if (object_type == 'event_device') load_ae_obj_id__event_device({ api_cfg, event_device_id: object_id, log_lvl });
|
||||
if (object_type == 'event_file') load_ae_obj_id__event_file({ api_cfg, event_file_id: object_id, log_lvl });
|
||||
if (object_type == 'event_location') load_ae_obj_id__event_location({ api_cfg, event_location_id: object_id, log_lvl });
|
||||
if (object_type == 'event_presentation') load_ae_obj_id__event_presentation({ api_cfg, event_presentation_id: object_id, log_lvl });
|
||||
if (object_type == 'event_presenter') load_ae_obj_id__event_presenter({ api_cfg, event_presenter_id: object_id, log_lvl });
|
||||
if (object_type == 'event_session') load_ae_obj_id__event_session({ api_cfg, event_session_id: object_id, log_lvl });
|
||||
if (object_type == 'post') load_ae_obj_id__post({ api_cfg, post_id: object_id, log_lvl });
|
||||
if (object_type == 'post_comment') load_ae_obj_id__post_comment({ api_cfg, post_comment_id: object_id, log_lvl });
|
||||
}
|
||||
} else {
|
||||
if (log_lvl) {
|
||||
console.log(`PATCH failed for ${object_type} ${object_id}`);
|
||||
}
|
||||
}
|
||||
// if (object_reload) {
|
||||
// if (log_lvl) {
|
||||
// console.log(`Reloading the object after patching...`);
|
||||
// }
|
||||
// // Trigger reloads based on object type. These are fire-and-forget or awaited internally by the library functions.
|
||||
// if (object_type == 'person') load_ae_obj_id__person({ api_cfg, person_id: object_id, log_lvl });
|
||||
// if (object_type == 'archive') load_ae_obj_id__archive({ api_cfg, archive_id: object_id, log_lvl });
|
||||
// if (object_type == 'archive_content') load_ae_obj_id__archive_content({ api_cfg, archive_content_id: object_id, log_lvl });
|
||||
// if (object_type == 'journal') load_ae_obj_id__journal({ api_cfg, journal_id: object_id, log_lvl });
|
||||
// if (object_type == 'journal_entry') load_ae_obj_id__journal_entry({ api_cfg, journal_entry_id: object_id, log_lvl });
|
||||
// if (object_type == 'event') load_ae_obj_id__event({ api_cfg, event_id: object_id, log_lvl });
|
||||
// if (object_type == 'event_exhibit') load_ae_obj_id__event_exhibit({ api_cfg, exhibit_id: object_id, log_lvl });
|
||||
// if (object_type == 'event_device') load_ae_obj_id__event_device({ api_cfg, event_device_id: object_id, log_lvl });
|
||||
// if (object_type == 'event_file') load_ae_obj_id__event_file({ api_cfg, event_file_id: object_id, log_lvl });
|
||||
// if (object_type == 'event_location') load_ae_obj_id__event_location({ api_cfg, event_location_id: object_id, log_lvl });
|
||||
// if (object_type == 'event_presentation') load_ae_obj_id__event_presentation({ api_cfg, event_presentation_id: object_id, log_lvl });
|
||||
// if (object_type == 'event_presenter') load_ae_obj_id__event_presenter({ api_cfg, event_presenter_id: object_id, log_lvl });
|
||||
// if (object_type == 'event_session') load_ae_obj_id__event_session({ api_cfg, event_session_id: object_id, log_lvl });
|
||||
// if (object_type == 'post') load_ae_obj_id__post({ api_cfg, post_id: object_id, log_lvl });
|
||||
// if (object_type == 'post_comment') load_ae_obj_id__post_comment({ api_cfg, post_comment_id: object_id, log_lvl });
|
||||
// }
|
||||
// } else {
|
||||
// if (log_lvl) {
|
||||
// console.log(`PATCH failed for ${object_type} ${object_id}`);
|
||||
// }
|
||||
// }
|
||||
|
||||
return results;
|
||||
}
|
||||
// return results;
|
||||
// }
|
||||
|
||||
async function download_export__obj_type({
|
||||
api_cfg,
|
||||
@@ -491,7 +499,10 @@ async function download_export__obj_type({
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
|
||||
console.log('ae_promises.download__export_file:', ae_promises.download__export_file);
|
||||
console.log(
|
||||
'ae_promises.download__export_file:',
|
||||
ae_promises.download__export_file
|
||||
);
|
||||
return ae_promises.download__export_file;
|
||||
}
|
||||
|
||||
@@ -533,7 +544,7 @@ const export_obj = {
|
||||
auth_ae_obj__user_id_change_password: auth_ae_obj__user_id_change_password,
|
||||
|
||||
update_ae_obj_id_crud: update_ae_obj_id_crud,
|
||||
update_ae_obj_id_crud_v2: update_ae_obj_id_crud_v2,
|
||||
// update_ae_obj_id_crud_v2: update_ae_obj_id_crud_v2,
|
||||
download_export__obj_type: download_export__obj_type,
|
||||
generate_qr_code: generate_qr_code,
|
||||
js_generate_qr_code: js_generate_qr_code
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
export interface Account {
|
||||
id: string;
|
||||
// id_random: string;
|
||||
account_id: string;
|
||||
account_id_random: string;
|
||||
|
||||
code?: string;
|
||||
name: string;
|
||||
short_name?: null | string;
|
||||
description?: null | string;
|
||||
|
||||
enable: null | boolean;
|
||||
enable_from?: null | Date;
|
||||
enable_to?: null | Date;
|
||||
|
||||
hide?: null | boolean;
|
||||
priority?: null | boolean;
|
||||
sort?: null | number;
|
||||
group?: null | string;
|
||||
notes?: null | string;
|
||||
created_on: Date;
|
||||
updated_on?: null | Date;
|
||||
}
|
||||
@@ -13,12 +13,17 @@ export function add_url_params({
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** add_url_params() *** base_url=${base_url} endpoint=${endpoint}`, params);
|
||||
console.log(
|
||||
`*** add_url_params() *** base_url=${base_url} endpoint=${endpoint}`,
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
const url_obj = new URL(endpoint, base_url);
|
||||
|
||||
Object.keys(params).forEach((key) => url_obj.searchParams.append(key, params[key]));
|
||||
Object.keys(params).forEach((key) =>
|
||||
url_obj.searchParams.append(key, params[key])
|
||||
);
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('New URL:', url_obj.toString());
|
||||
@@ -30,7 +35,13 @@ export function add_url_params({
|
||||
|
||||
// This is used to clean the header property names. Not underscores allowed in the header names.
|
||||
// Updated 2025-01-28
|
||||
export function clean_headers({ headers, log_lvl = 0 }: { headers: any; log_lvl?: number }) {
|
||||
export function clean_headers({
|
||||
headers,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
headers: any;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** clean_headers() ***`, headers);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
import { api } from '$lib/api/api';
|
||||
|
||||
// Updated 2024-10-02
|
||||
// Updated 2026-03-25
|
||||
export async function check_hosted_file_obj_w_hash({
|
||||
api_cfg,
|
||||
hosted_file_hash,
|
||||
@@ -19,7 +19,7 @@ export async function check_hosted_file_obj_w_hash({
|
||||
}) {
|
||||
console.log('*** stores_event_api.js: check_hosted_file_obj_w_hash() ***');
|
||||
|
||||
const endpoint = `/hosted_file/hash/${hosted_file_hash}`;
|
||||
const endpoint = `/v3/action/hosted_file/hash/${hosted_file_hash}`;
|
||||
if (check_for_local) {
|
||||
params['check_for_local'] = true;
|
||||
}
|
||||
|
||||
@@ -1,65 +1,72 @@
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
import { api } from '$lib/api/api';
|
||||
import { db_lookups, LOOKUP_TTL_MS } from '$lib/ae_core/db_lookups';
|
||||
|
||||
import { db_core } from '$lib/ae_core/db_core';
|
||||
/**
|
||||
* Country lookup — IDB-backed SWR helper.
|
||||
*
|
||||
* Calling this function triggers a background API refresh if IDB is empty or
|
||||
* older than 24 hours. The function returns immediately without awaiting the
|
||||
* refresh. Components subscribe to db_lookups.lu_country via liveQuery and
|
||||
* receive automatic updates when the refresh completes.
|
||||
*
|
||||
* Updated 2026-03-23 — replaced localStorage pattern with IDB + 24h TTL
|
||||
*/
|
||||
|
||||
const ae_promises: key_val = {};
|
||||
|
||||
// Updated 2024-10-14
|
||||
export async function load_ae_obj_li__country({
|
||||
async function _refresh_lu_country_background({
|
||||
api_cfg,
|
||||
// account_id,
|
||||
enabled = 'enabled',
|
||||
hidden = 'not_hidden',
|
||||
limit = 275, // There are roughly 249 as of 2026-02
|
||||
offset = 0,
|
||||
order_by_li = { sort: 'DESC', english_short_name: 'ASC', alpha_2_code: 'ASC' } as const,
|
||||
params = {},
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
api_cfg: any;
|
||||
// account_id: string,
|
||||
enabled?: 'enabled' | 'all' | 'not_enabled' | undefined;
|
||||
hidden?: 'hidden' | 'all' | 'not_hidden' | undefined;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
order_by_li?: key_val;
|
||||
params?: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_li__country() ***`);
|
||||
}
|
||||
|
||||
const params_json: key_val = {};
|
||||
|
||||
// console.log('params_json:', params_json);
|
||||
|
||||
ae_promises.load__country_li = await api
|
||||
.get_ae_obj_li_for_lu({
|
||||
api_cfg: api_cfg,
|
||||
if (log_lvl) console.log('*** _refresh_lu_country_background() ***');
|
||||
try {
|
||||
const result = await api.get_ae_obj_li_for_lu({
|
||||
api_cfg,
|
||||
for_lu_type: 'country',
|
||||
enabled: enabled,
|
||||
hidden: hidden,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
params: params,
|
||||
log_lvl: log_lvl
|
||||
})
|
||||
.then(function (country_li_get_result) {
|
||||
if (country_li_get_result) {
|
||||
// handle_db_save_ae_obj_li__country({obj_type: 'country', obj_li: country_li_get_result});
|
||||
return country_li_get_result;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
})
|
||||
.catch(function (error: any) {
|
||||
console.log('No results returned or failed.', error);
|
||||
enabled: 'enabled',
|
||||
hidden: 'not_hidden',
|
||||
limit: 275,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
console.log('ae_promises.load__country_li:', ae_promises.load__country_li);
|
||||
return ae_promises.load__country_li;
|
||||
if (result?.length) {
|
||||
await db_lookups.lu_country.clear();
|
||||
await db_lookups.lu_country.bulkPut(result);
|
||||
await db_lookups.lu_cache_meta.put({
|
||||
lu_type: 'country',
|
||||
refreshed_at: Date.now()
|
||||
});
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`lu_country: saved ${result.length} records to IDB`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('lu_country refresh failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function load_ae_obj_li__country({
|
||||
api_cfg,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
api_cfg: any;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) console.log('*** load_ae_obj_li__country() ***');
|
||||
|
||||
const count = await db_lookups.lu_country.count();
|
||||
const meta = await db_lookups.lu_cache_meta.get('country');
|
||||
const is_stale = !meta || Date.now() - meta.refreshed_at > LOOKUP_TTL_MS;
|
||||
|
||||
if (count === 0 || is_stale) {
|
||||
// Fire-and-forget — liveQuery subscribers receive updates when IDB is written
|
||||
_refresh_lu_country_background({ api_cfg, log_lvl });
|
||||
} else if (log_lvl) {
|
||||
console.log(
|
||||
`lu_country: IDB fresh (${count} records), skipping refresh`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +1,71 @@
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
import { api } from '$lib/api/api';
|
||||
import { db_lookups, LOOKUP_TTL_MS } from '$lib/ae_core/db_lookups';
|
||||
|
||||
import { db_core } from '$lib/ae_core/db_core';
|
||||
/**
|
||||
* Country subdivision lookup — IDB-backed SWR helper.
|
||||
*
|
||||
* Calling this function triggers a background API refresh if IDB is empty or
|
||||
* older than 24 hours. Components subscribe to db_lookups.lu_country_subdivision
|
||||
* via liveQuery and receive automatic updates when the refresh completes.
|
||||
*
|
||||
* Updated 2026-03-23 — replaced localStorage pattern with IDB + 24h TTL
|
||||
*/
|
||||
|
||||
const ae_promises: key_val = {};
|
||||
|
||||
// Updated 2024-10-14
|
||||
export async function load_ae_obj_li__country_subdivision({
|
||||
async function _refresh_lu_country_subdivision_background({
|
||||
api_cfg,
|
||||
// account_id,
|
||||
enabled = 'enabled',
|
||||
hidden = 'not_hidden',
|
||||
limit = 3500, // There are roughly 3434 as of 2026-02
|
||||
offset = 0,
|
||||
order_by_li = { sort: 'DESC', name: 'ASC', code: 'ASC' } as const,
|
||||
params = {},
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
api_cfg: any;
|
||||
// account_id: string,
|
||||
enabled?: 'enabled' | 'all' | 'not_enabled' | undefined;
|
||||
hidden?: 'hidden' | 'all' | 'not_hidden' | undefined;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
order_by_li?: key_val;
|
||||
params?: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_li__country_subdivision() ***`);
|
||||
}
|
||||
|
||||
const params_json: key_val = {};
|
||||
|
||||
// console.log('params_json:', params_json);
|
||||
|
||||
ae_promises.load__country_subdivision_li = await api
|
||||
.get_ae_obj_li_for_lu({
|
||||
api_cfg: api_cfg,
|
||||
if (log_lvl)
|
||||
console.log('*** _refresh_lu_country_subdivision_background() ***');
|
||||
try {
|
||||
const result = await api.get_ae_obj_li_for_lu({
|
||||
api_cfg,
|
||||
for_lu_type: 'country_subdivision',
|
||||
enabled: enabled,
|
||||
hidden: hidden,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
params: params,
|
||||
log_lvl: log_lvl
|
||||
})
|
||||
.then(function (country_subdivision_li_get_result) {
|
||||
if (country_subdivision_li_get_result) {
|
||||
// handle_db_save_ae_obj_li__country_subdivision({obj_type: 'country_subdivision', obj_li: country_subdivision_li_get_result});
|
||||
return country_subdivision_li_get_result;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
})
|
||||
.catch(function (error: any) {
|
||||
console.log('No results returned or failed.', error);
|
||||
enabled: 'enabled',
|
||||
hidden: 'not_hidden',
|
||||
limit: 3500,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
console.log(
|
||||
'ae_promises.load__country_subdivision_li:',
|
||||
ae_promises.load__country_subdivision_li
|
||||
);
|
||||
return ae_promises.load__country_subdivision_li;
|
||||
if (result?.length) {
|
||||
await db_lookups.lu_country_subdivision.clear();
|
||||
await db_lookups.lu_country_subdivision.bulkPut(result);
|
||||
await db_lookups.lu_cache_meta.put({
|
||||
lu_type: 'country_subdivision',
|
||||
refreshed_at: Date.now()
|
||||
});
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`lu_country_subdivision: saved ${result.length} records to IDB`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('lu_country_subdivision refresh failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function load_ae_obj_li__country_subdivision({
|
||||
api_cfg,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
api_cfg: any;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) console.log('*** load_ae_obj_li__country_subdivision() ***');
|
||||
|
||||
const count = await db_lookups.lu_country_subdivision.count();
|
||||
const meta = await db_lookups.lu_cache_meta.get('country_subdivision');
|
||||
const is_stale = !meta || Date.now() - meta.refreshed_at > LOOKUP_TTL_MS;
|
||||
|
||||
if (count === 0 || is_stale) {
|
||||
_refresh_lu_country_subdivision_background({ api_cfg, log_lvl });
|
||||
} else if (log_lvl) {
|
||||
console.log(
|
||||
`lu_country_subdivision: IDB fresh (${count} records), skipping refresh`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,351 +0,0 @@
|
||||
import { marked } from 'marked';
|
||||
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
import { api } from '$lib/api/api';
|
||||
|
||||
import { db_save_ae_obj_li__ae_obj } from '$lib/ae_core/core__idb_dexie';
|
||||
|
||||
// Define generic CRUD args
|
||||
export interface GenericCrudArgs {
|
||||
api_cfg: any;
|
||||
obj_type: string;
|
||||
obj_id?: string;
|
||||
for_obj_type?: string;
|
||||
for_obj_id?: string;
|
||||
|
||||
db_instance?: any; // Optional DB instance for caching
|
||||
db_field_li?: string[]; // Optional list of fields to save in DB
|
||||
|
||||
// Flags to include related core object models
|
||||
inc_account_li?: boolean;
|
||||
inc_address_li?: boolean;
|
||||
inc_contact_li?: boolean;
|
||||
inc_person_li?: boolean;
|
||||
inc_site_li?: boolean;
|
||||
inc_site_domain_li?: boolean;
|
||||
inc_user_li?: boolean;
|
||||
|
||||
// Flags to include related other object models
|
||||
inc_archive_li?: boolean;
|
||||
inc_archive_entry_li?: boolean;
|
||||
inc_event_li?: boolean;
|
||||
inc_event_session_li?: boolean;
|
||||
inc_post_li?: boolean;
|
||||
inc_post_comment_li?: boolean;
|
||||
inc_journal_li?: boolean;
|
||||
inc_journal_entry_li?: boolean;
|
||||
|
||||
inc_obj_type_li?: string[]; // Optional list of object types to include
|
||||
|
||||
data_kv?: key_val;
|
||||
enabled?: 'enabled' | 'not_enabled' | 'all';
|
||||
hidden?: 'not_hidden' | 'hidden' | 'all';
|
||||
method?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
order_by_li?: Record<string, 'ASC' | 'DESC'> | Record<string, 'ASC' | 'DESC'>[] | null;
|
||||
params?: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}
|
||||
|
||||
// Generic function: Load single object by ID
|
||||
export async function load_ae_obj_id(args: GenericCrudArgs): Promise<any> {
|
||||
const { api_cfg, obj_type, obj_id, log_lvl = 0 } = args;
|
||||
|
||||
if (!obj_id) {
|
||||
if (log_lvl) console.warn('load_ae_obj_id called without obj_id');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_id() *** obj_type=${obj_type} obj_id=${obj_id}`);
|
||||
}
|
||||
|
||||
const result = await api.get_ae_obj_id_crud({
|
||||
api_cfg,
|
||||
obj_type,
|
||||
obj_id,
|
||||
params: {},
|
||||
log_lvl
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Generic function: Load list of objects
|
||||
export async function load_ae_obj_li(args: GenericCrudArgs): Promise<any> {
|
||||
const {
|
||||
api_cfg,
|
||||
obj_type,
|
||||
for_obj_type = '',
|
||||
for_obj_id,
|
||||
inc_obj_type_li,
|
||||
enabled = 'enabled',
|
||||
hidden = 'not_hidden',
|
||||
limit = 99,
|
||||
offset = 0,
|
||||
order_by_li = {},
|
||||
params = {},
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
} = args;
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`*** load_ae_obj_li() *** obj_type=${obj_type} for_obj_type=${for_obj_type} for_obj_id=${for_obj_id}`
|
||||
);
|
||||
}
|
||||
|
||||
const params_json: key_val = {};
|
||||
|
||||
const result = await api.get_ae_obj_li_for_obj_id_crud_v2({
|
||||
api_cfg,
|
||||
obj_type,
|
||||
for_obj_type,
|
||||
for_obj_id: for_obj_id ?? '',
|
||||
enabled,
|
||||
hidden,
|
||||
order_by_li,
|
||||
limit,
|
||||
offset,
|
||||
params_json: {},
|
||||
params,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Generic function: Create object
|
||||
export async function create_ae_obj(args: GenericCrudArgs): Promise<any> {
|
||||
const { api_cfg, obj_type, data_kv, log_lvl = 0 } = args;
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`*** create_ae_obj() *** obj_type=${obj_type}`, data_kv);
|
||||
}
|
||||
|
||||
const result = await api.create_ae_obj_crud({
|
||||
api_cfg,
|
||||
obj_type,
|
||||
fields: data_kv,
|
||||
key: api_cfg.api_crud_super_key,
|
||||
params: {},
|
||||
return_obj: true,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Generic function: Update object
|
||||
export async function update_ae_obj(args: GenericCrudArgs): Promise<any> {
|
||||
const { api_cfg, obj_type, obj_id, data_kv, log_lvl = 0 } = args;
|
||||
|
||||
if (!obj_id) {
|
||||
if (log_lvl) console.warn('update_ae_obj called without obj_id');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`*** update_ae_obj() *** obj_type=${obj_type} obj_id=${obj_id}`, data_kv);
|
||||
}
|
||||
|
||||
const result = await api.update_ae_obj_id_crud({
|
||||
api_cfg,
|
||||
obj_type,
|
||||
obj_id,
|
||||
fields: data_kv,
|
||||
key: api_cfg.api_crud_super_key,
|
||||
params: {},
|
||||
return_obj: true,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Generic function: Delete object
|
||||
export async function delete_ae_obj_id(args: GenericCrudArgs): Promise<any> {
|
||||
const { api_cfg, obj_type, obj_id, method = 'delete', log_lvl = 0 } = args;
|
||||
|
||||
if (!obj_id) {
|
||||
if (log_lvl) console.warn('delete_ae_obj_id called without obj_id');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`*** delete_ae_obj_id() *** obj_type=${obj_type} obj_id=${obj_id}`);
|
||||
}
|
||||
|
||||
const result = await api.delete_ae_obj_id_crud({
|
||||
api_cfg,
|
||||
obj_type,
|
||||
obj_id,
|
||||
key: api_cfg.api_crud_super_key,
|
||||
params: {},
|
||||
method,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Additional Modules that might be needed for reloads
|
||||
import { load_ae_obj_id__archive } from '$lib/ae_archives/ae_archives__archive';
|
||||
import { load_ae_obj_id__archive_content } from '$lib/ae_archives/ae_archives__archive_content';
|
||||
import { load_ae_obj_id__event } from '$lib/ae_events/ae_events__event';
|
||||
import { load_ae_obj_id__event_device } from '$lib/ae_events/ae_events__event_device';
|
||||
import { load_ae_obj_id__event_file } from '$lib/ae_events/ae_events__event_file';
|
||||
import { load_ae_obj_id__event_location } from '$lib/ae_events/ae_events__event_location';
|
||||
import { load_ae_obj_id__event_presentation } from '$lib/ae_events/ae_events__event_presentation';
|
||||
import { load_ae_obj_id__event_presenter } from '$lib/ae_events/ae_events__event_presenter';
|
||||
import { load_ae_obj_id__event_session } from '$lib/ae_events/ae_events__event_session';
|
||||
import { load_ae_obj_id__journal } from '$lib/ae_journals/ae_journals__journal';
|
||||
import { load_ae_obj_id__journal_entry } from '$lib/ae_journals/ae_journals__journal_entry';
|
||||
import { load_ae_obj_id__post } from '$lib/ae_posts/ae_posts__post';
|
||||
import { load_ae_obj_id__post_comment } from '$lib/ae_posts/ae_posts__post_comment';
|
||||
import { load_ae_obj_id__person } from '$lib/ae_core/ae_core__person';
|
||||
|
||||
export async function update_ae_obj_id_crud_v2({
|
||||
api_cfg,
|
||||
object_type,
|
||||
object_id,
|
||||
object_reload = false,
|
||||
field_name,
|
||||
new_field_value,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
object_type: string;
|
||||
object_id: string;
|
||||
object_reload?: boolean;
|
||||
field_name: string;
|
||||
new_field_value: any;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`*** update_ae_obj_id_crud_v2() *** object_type=${object_type}, object_id=${object_id}, object_reload=${object_reload}, field_name=${field_name}, new_field_value=`,
|
||||
new_field_value
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const results = await api.update_ae_obj_id_crud({
|
||||
api_cfg: api_cfg,
|
||||
obj_type: object_type,
|
||||
obj_id: object_id,
|
||||
field_name: field_name,
|
||||
field_value: new_field_value,
|
||||
key: api_cfg.api_crud_super_key,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
|
||||
if (!results) {
|
||||
if (log_lvl) console.log(
|
||||
`Not Patched - Field Name: ${field_name} with new Field Value: ${new_field_value}; Account ID: ${api_cfg.account_id}`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (log_lvl) console.log(`Patched - Field Name: ${field_name} with new Field Value: ${new_field_value}`);
|
||||
|
||||
if (object_reload) {
|
||||
if (log_lvl) console.log(`Reloading the object after patching...`);
|
||||
|
||||
const reload_fns: { [key: string]: (args: any) => Promise<any> } = {
|
||||
person: load_ae_obj_id__person,
|
||||
archive: load_ae_obj_id__archive,
|
||||
archive_content: load_ae_obj_id__archive_content,
|
||||
journal: load_ae_obj_id__journal,
|
||||
journal_entry: load_ae_obj_id__journal_entry,
|
||||
event: load_ae_obj_id__event,
|
||||
event_device: load_ae_obj_id__event_device,
|
||||
event_file: load_ae_obj_id__event_file,
|
||||
event_location: load_ae_obj_id__event_location,
|
||||
event_presentation: load_ae_obj_id__event_presentation,
|
||||
event_presenter: load_ae_obj_id__event_presenter,
|
||||
event_session: load_ae_obj_id__event_session,
|
||||
post: load_ae_obj_id__post,
|
||||
post_comment: load_ae_obj_id__post_comment
|
||||
};
|
||||
|
||||
const reload_fn = reload_fns[object_type];
|
||||
if (reload_fn) {
|
||||
const id_key = `${object_type}_id`;
|
||||
return await reload_fn({ api_cfg, [id_key]: object_id, log_lvl });
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log('Something went wrong patching the record.', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function download_export_li({
|
||||
api_cfg,
|
||||
get_obj_type,
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
exp_alt = null,
|
||||
file_type = 'CSV',
|
||||
return_file = true,
|
||||
filename = 'no_filename.csv',
|
||||
auto_download = false,
|
||||
limit = 5000,
|
||||
params = {},
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
get_obj_type: string;
|
||||
for_obj_type: string;
|
||||
for_obj_id: string;
|
||||
exp_alt?: null | string;
|
||||
file_type?: string;
|
||||
return_file?: boolean;
|
||||
filename?: string;
|
||||
auto_download?: boolean;
|
||||
limit?: number;
|
||||
params?: key_val;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) console.log('*** download_export_li() ***');
|
||||
|
||||
const endpoint = `/v2/crud/${get_obj_type}/list`;
|
||||
params['for_obj_type'] = for_obj_type;
|
||||
params['for_obj_id'] = for_obj_id;
|
||||
|
||||
if (file_type === 'CSV' || file_type === 'Excel') {
|
||||
params['file_type'] = file_type;
|
||||
}
|
||||
params['return_file'] = true;
|
||||
params['mdl_alt'] = 'out';
|
||||
|
||||
if (exp_alt) {
|
||||
params['exp_alt'] = exp_alt;
|
||||
}
|
||||
|
||||
const clean_filename = filename.replace(/[^a-zA-Z0-9\[\]-_.]/gi, '_');
|
||||
|
||||
if (limit >= 0) {
|
||||
params['limit'] = limit;
|
||||
}
|
||||
|
||||
const download_result = await api.get_object({
|
||||
api_cfg: api_cfg,
|
||||
endpoint: endpoint,
|
||||
params: params,
|
||||
timeout: 90000,
|
||||
return_blob: return_file,
|
||||
filename: clean_filename,
|
||||
auto_download: auto_download,
|
||||
task_id: for_obj_id,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
|
||||
if (log_lvl) console.log('download_result:', download_result);
|
||||
return download_result;
|
||||
}
|
||||
@@ -39,7 +39,7 @@ export async function load_ae_obj_by_code__data_store({
|
||||
}
|
||||
|
||||
try {
|
||||
const get_ds_result = await api.get_data_store_v3({
|
||||
const get_ds_result = await api.get_data_store({
|
||||
api_cfg,
|
||||
code,
|
||||
log_lvl
|
||||
@@ -50,17 +50,25 @@ export async function load_ae_obj_by_code__data_store({
|
||||
return null;
|
||||
}
|
||||
|
||||
const ds_id = get_ds_result.data_store_id_random || get_ds_result.id_random;
|
||||
const ds_id =
|
||||
get_ds_result.data_store_id_random || get_ds_result.id_random;
|
||||
|
||||
if (!ds_id) {
|
||||
if (log_lvl) console.log('*ae_func* Something went wrong? No data store ID found.');
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
'*ae_func* Something went wrong? No data store ID found.'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Map content fields for convenience
|
||||
const text_val = get_ds_result.text || '';
|
||||
const json_val = get_ds_result.json || (get_ds_result.json_str ? JSON.parse(get_ds_result.json_str) : null);
|
||||
|
||||
const json_val =
|
||||
get_ds_result.json ||
|
||||
(get_ds_result.json_str
|
||||
? JSON.parse(get_ds_result.json_str)
|
||||
: null);
|
||||
|
||||
const mapped_ds: ae_DataStore = {
|
||||
...get_ds_result,
|
||||
id: ds_id,
|
||||
@@ -77,7 +85,6 @@ export async function load_ae_obj_by_code__data_store({
|
||||
if (data_type === 'html') return mapped_ds.html;
|
||||
if (data_type === 'json') return mapped_ds.json;
|
||||
return mapped_ds.text;
|
||||
|
||||
} catch (error) {
|
||||
if (log_lvl) console.error('*ae_func* Fetch failed.', error);
|
||||
return null;
|
||||
|
||||
@@ -22,11 +22,13 @@ export async function load_ae_obj_id__hosted_file({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_HostedFile | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_id__hosted_file() *** [V3] id=${hosted_file_id}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_id__hosted_file() *** [V3] id=${hosted_file_id}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
ae_promises.load__hosted_file_obj = await api.get_ae_obj_v3({
|
||||
ae_promises.load__hosted_file_obj = await api.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'hosted_file',
|
||||
obj_id: hosted_file_id,
|
||||
@@ -36,10 +38,11 @@ export async function load_ae_obj_id__hosted_file({
|
||||
|
||||
if (ae_promises.load__hosted_file_obj) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__hosted_file_props({
|
||||
obj_li: [ae_promises.load__hosted_file_obj],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__hosted_file_props({
|
||||
obj_li: [ae_promises.load__hosted_file_obj],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'file',
|
||||
@@ -49,12 +52,14 @@ export async function load_ae_obj_id__hosted_file({
|
||||
});
|
||||
}
|
||||
} else if (try_cache) {
|
||||
ae_promises.load__hosted_file_obj = await db_core.file.get(hosted_file_id);
|
||||
ae_promises.load__hosted_file_obj =
|
||||
await db_core.file.get(hosted_file_id);
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log('V3 Request failed.', error);
|
||||
if (try_cache) {
|
||||
ae_promises.load__hosted_file_obj = await db_core.file.get(hosted_file_id);
|
||||
ae_promises.load__hosted_file_obj =
|
||||
await db_core.file.get(hosted_file_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,11 +95,13 @@ export async function load_ae_obj_li__hosted_file({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_HostedFile[]> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_li__hosted_file() *** [V3] for=${for_obj_type}:${for_obj_id}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_li__hosted_file() *** [V3] for=${for_obj_type}:${for_obj_id}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
ae_promises.load__hosted_file_obj_li = await api.get_ae_obj_li_v3({
|
||||
ae_promises.load__hosted_file_obj_li = await api.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'hosted_file',
|
||||
for_obj_type,
|
||||
@@ -109,10 +116,11 @@ export async function load_ae_obj_li__hosted_file({
|
||||
|
||||
if (ae_promises.load__hosted_file_obj_li) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__hosted_file_props({
|
||||
obj_li: ae_promises.load__hosted_file_obj_li,
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__hosted_file_props({
|
||||
obj_li: ae_promises.load__hosted_file_obj_li,
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'file',
|
||||
@@ -123,14 +131,16 @@ export async function load_ae_obj_li__hosted_file({
|
||||
}
|
||||
} else if (try_cache) {
|
||||
ae_promises.load__hosted_file_obj_li = await db_core.file
|
||||
.where('for_id').equals(for_obj_id)
|
||||
.where('for_id')
|
||||
.equals(for_obj_id)
|
||||
.toArray();
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log('V3 List Request failed.', error);
|
||||
if (try_cache) {
|
||||
ae_promises.load__hosted_file_obj_li = await db_core.file
|
||||
.where('for_id').equals(for_obj_id)
|
||||
.where('for_id')
|
||||
.equals(for_obj_id)
|
||||
.toArray();
|
||||
}
|
||||
}
|
||||
@@ -159,7 +169,9 @@ export async function delete_ae_obj_id__hosted_file({
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** delete_ae_obj_id__hosted_file() *** [Special] id=${hosted_file_id}`);
|
||||
console.log(
|
||||
`*** delete_ae_obj_id__hosted_file() *** [Special] id=${hosted_file_id}`
|
||||
);
|
||||
}
|
||||
|
||||
// Use the specialized hosted file delete endpoint
|
||||
@@ -203,7 +215,9 @@ export async function download_ae_obj_id__hosted_file({
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** download_ae_obj_id__hosted_file() *** id=${hosted_file_id}`);
|
||||
console.log(
|
||||
`*** download_ae_obj_id__hosted_file() *** id=${hosted_file_id}`
|
||||
);
|
||||
}
|
||||
|
||||
const task_id = hosted_file_id;
|
||||
@@ -290,11 +304,15 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on;
|
||||
const name = processed_obj.name ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
@@ -315,4 +333,4 @@ export async function process_ae_obj__hosted_file_props({
|
||||
obj_type: 'hosted_file',
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ function find_object_id(
|
||||
log_lvl: number
|
||||
): string | number | undefined {
|
||||
const potential_keys = [
|
||||
'id',
|
||||
'id_random',
|
||||
`${table_name}_id`,
|
||||
'id',
|
||||
'id_random',
|
||||
`${table_name}_id`,
|
||||
`${table_name}_id_random`,
|
||||
`event_${table_name}_id`,
|
||||
`event_${table_name}_id_random`
|
||||
@@ -115,7 +115,9 @@ export async function db_save_ae_obj_li__ae_obj<T extends Record<string, any>>({
|
||||
|
||||
if (data_to_save.length === 0) {
|
||||
if (log_lvl > 0) {
|
||||
console.warn('All objects were skipped, likely due to missing IDs.');
|
||||
console.warn(
|
||||
'All objects were skipped, likely due to missing IDs.'
|
||||
);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
@@ -124,7 +126,9 @@ export async function db_save_ae_obj_li__ae_obj<T extends Record<string, any>>({
|
||||
// bulkPut efficiently handles both inserts and updates.
|
||||
const keys = await db_table.bulkPut(data_to_save);
|
||||
if (log_lvl > 0) {
|
||||
console.log(`Successfully saved ${data_to_save.length} objects to "${table_name}".`);
|
||||
console.log(
|
||||
`Successfully saved ${data_to_save.length} objects to "${table_name}".`
|
||||
);
|
||||
}
|
||||
return keys;
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,736 +0,0 @@
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
import { api } from '$lib/api/api';
|
||||
|
||||
import { db_save_ae_obj_li__ae_obj } from '$lib/ae_core/core__idb_dexie';
|
||||
import { db_core } from '$lib/ae_core/db_core';
|
||||
|
||||
const ae_promises: key_val = {};
|
||||
|
||||
// Updated 2025-06-10
|
||||
export async function load_ae_obj_id__person({
|
||||
api_cfg,
|
||||
person_id,
|
||||
params = {},
|
||||
try_cache = false,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
person_id: string;
|
||||
params?: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_id__person() *** person_id=${person_id}`);
|
||||
}
|
||||
|
||||
ae_promises.load__person_obj = await api
|
||||
.get_ae_obj_id_crud({
|
||||
api_cfg: api_cfg,
|
||||
obj_type: 'person',
|
||||
obj_id: person_id,
|
||||
use_alt_table: false,
|
||||
use_alt_base: false,
|
||||
params: params,
|
||||
log_lvl: log_lvl
|
||||
})
|
||||
.then(async function (person_obj_get_result) {
|
||||
if (person_obj_get_result) {
|
||||
if (try_cache) {
|
||||
// Process the results first
|
||||
const processed_obj_li = await process_ae_obj__person_props({
|
||||
obj_li: [person_obj_get_result],
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
if (log_lvl) {
|
||||
console.log('Processed object list:', processed_obj_li);
|
||||
}
|
||||
// Save the updated results list to the database
|
||||
if (log_lvl) {
|
||||
console.log('Saving to DB...');
|
||||
}
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'person',
|
||||
obj_li: processed_obj_li,
|
||||
properties_to_save: properties_to_save,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
if (log_lvl) {
|
||||
console.log('DB save completed.');
|
||||
}
|
||||
|
||||
// // This is expecting a list
|
||||
// db_save_ae_obj_li__person({
|
||||
// obj_type: 'person',
|
||||
// obj_li: [person_obj_get_result],
|
||||
// log_lvl: log_lvl
|
||||
// });
|
||||
}
|
||||
return person_obj_get_result;
|
||||
} else {
|
||||
console.log('No results returned.');
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.catch(function (error: any) {
|
||||
console.log('No results returned or failed.', error);
|
||||
});
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('ae_promises.load__person_obj:', ae_promises.load__person_obj);
|
||||
}
|
||||
|
||||
return ae_promises.load__person_obj;
|
||||
}
|
||||
|
||||
// Updated 2025-06-10
|
||||
export async function load_ae_obj_li__person({
|
||||
api_cfg,
|
||||
for_obj_type = 'account',
|
||||
for_obj_id,
|
||||
qry_email = null,
|
||||
qry_user_id = null,
|
||||
enabled = 'enabled',
|
||||
hidden = 'not_hidden',
|
||||
limit = 99,
|
||||
offset = 0,
|
||||
order_by_li = [
|
||||
{ family_name: 'ASC' },
|
||||
{ given_name: 'ASC' },
|
||||
{ updated_on: 'DESC' },
|
||||
{ created_on: 'DESC' }
|
||||
],
|
||||
// params_json = {},
|
||||
params = {},
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
for_obj_type: string;
|
||||
for_obj_id: string;
|
||||
qry_email?: string | null;
|
||||
qry_user_id?: string | null;
|
||||
enabled?: 'enabled' | 'all' | 'not_enabled' | undefined; // all, disabled, enabled
|
||||
hidden?: 'hidden' | 'all' | 'not_hidden' | undefined; // all, hidden, not_hidden
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
order_by_li?: { [key: string]: 'ASC' | 'DESC' }[] | null;
|
||||
// params_json?: null|key_val,
|
||||
params?: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`*** load_ae_obj_li__person() *** for_obj_type=${for_obj_type} for_obj_id=${for_obj_id} enabled=${enabled} hidden=${hidden} limit=${limit} offset=${offset}`
|
||||
);
|
||||
}
|
||||
|
||||
const params_json: key_val = {};
|
||||
|
||||
// console.log('params_json:', params_json);
|
||||
if (qry_user_id) {
|
||||
// params_json['and_qry'] = {};
|
||||
// params_json['and_qry']['user_id_random'] = qry_user_id;
|
||||
|
||||
params_json['qry'] = [];
|
||||
|
||||
const qry_param = {
|
||||
type: 'AND',
|
||||
field: 'user_id_random',
|
||||
operator: '=',
|
||||
value: qry_user_id
|
||||
};
|
||||
params_json['qry'].push(qry_param);
|
||||
}
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('params_json:', params_json);
|
||||
}
|
||||
|
||||
ae_promises.load__person_obj_li = await api
|
||||
.get_ae_obj_li_for_obj_id_crud_v2({
|
||||
api_cfg: api_cfg,
|
||||
obj_type: 'person',
|
||||
for_obj_type: for_obj_type,
|
||||
for_obj_id: for_obj_id,
|
||||
use_alt_tbl: false,
|
||||
use_alt_mdl: false,
|
||||
use_alt_exp: false,
|
||||
enabled: enabled,
|
||||
hidden: hidden,
|
||||
order_by_li: order_by_li,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
params_json: params_json,
|
||||
params: params,
|
||||
log_lvl: log_lvl
|
||||
})
|
||||
.then(async function (person_obj_li_get_result) {
|
||||
if (person_obj_li_get_result) {
|
||||
if (try_cache) {
|
||||
// Process the results first
|
||||
const processed_obj_li = await process_ae_obj__person_props({
|
||||
obj_li: person_obj_li_get_result,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
if (log_lvl) {
|
||||
console.log('Processed object list:', processed_obj_li);
|
||||
}
|
||||
// Save the updated results list to the database
|
||||
if (log_lvl) {
|
||||
console.log('Saving to DB...');
|
||||
}
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'person',
|
||||
obj_li: processed_obj_li,
|
||||
properties_to_save: properties_to_save,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
if (log_lvl) {
|
||||
console.log('DB save completed.');
|
||||
}
|
||||
|
||||
// db_save_ae_obj_li__person({
|
||||
// obj_type: 'person',
|
||||
// obj_li: person_obj_li_get_result,
|
||||
// log_lvl: log_lvl
|
||||
// });
|
||||
}
|
||||
return person_obj_li_get_result;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
})
|
||||
.catch(function (error: any) {
|
||||
console.log('No results returned or failed.', error);
|
||||
});
|
||||
|
||||
console.log('ae_promises.load__person_obj_li:', ae_promises.load__person_obj_li);
|
||||
return ae_promises.load__person_obj_li;
|
||||
}
|
||||
|
||||
// Updated 2025-06-10
|
||||
export async function create_ae_obj__person({
|
||||
api_cfg,
|
||||
user_id,
|
||||
data_kv,
|
||||
params = {},
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
user_id?: string;
|
||||
data_kv: key_val;
|
||||
params?: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** create_ae_obj__person() *** user_id=${user_id}`);
|
||||
}
|
||||
|
||||
ae_promises.create__person = await api
|
||||
.create_ae_obj_crud({
|
||||
api_cfg: api_cfg,
|
||||
obj_type: 'person',
|
||||
fields: {
|
||||
user_id_random: user_id,
|
||||
...data_kv
|
||||
},
|
||||
key: api_cfg.api_crud_super_key,
|
||||
params: params,
|
||||
return_obj: true,
|
||||
log_lvl: log_lvl
|
||||
})
|
||||
.then(async function (person_obj_create_result) {
|
||||
if (person_obj_create_result) {
|
||||
if (try_cache) {
|
||||
// Process the results first
|
||||
const processed_obj_li = await process_ae_obj__person_props({
|
||||
obj_li: [person_obj_create_result],
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
if (log_lvl) {
|
||||
console.log('Processed object list:', processed_obj_li);
|
||||
}
|
||||
// Save the updated results list to the database
|
||||
if (log_lvl) {
|
||||
console.log('Saving to DB...');
|
||||
}
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'person',
|
||||
obj_li: processed_obj_li,
|
||||
properties_to_save: properties_to_save,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
if (log_lvl) {
|
||||
console.log('DB save completed.');
|
||||
}
|
||||
|
||||
// db_save_ae_obj_li__person(
|
||||
// {
|
||||
// obj_type: 'person',
|
||||
// obj_li: [person_obj_create_result],
|
||||
// log_lvl: log_lvl
|
||||
// });
|
||||
}
|
||||
return person_obj_create_result;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.catch(function (error: any) {
|
||||
console.log('No results returned or failed.', error);
|
||||
})
|
||||
.finally(function () {});
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('ae_promises.create__person:', ae_promises.create__person);
|
||||
}
|
||||
return ae_promises.create__person;
|
||||
}
|
||||
|
||||
// Updated 2025-05-10
|
||||
export async function delete_ae_obj_id__person({
|
||||
api_cfg,
|
||||
person_id,
|
||||
method = 'delete', // 'delete', 'disable', 'hide'
|
||||
params = {},
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
person_id: string;
|
||||
method?: string;
|
||||
params?: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** delete_ae_obj_id__person() *** person_id=${person_id}`);
|
||||
}
|
||||
|
||||
ae_promises.delete__person_obj = await api
|
||||
.delete_ae_obj_id_crud({
|
||||
api_cfg: api_cfg,
|
||||
obj_type: 'person',
|
||||
obj_id: person_id,
|
||||
key: api_cfg.api_crud_super_key,
|
||||
params: params,
|
||||
method: method,
|
||||
log_lvl: log_lvl
|
||||
})
|
||||
.catch(function (error: any) {
|
||||
console.log('No results returned or failed.', error);
|
||||
})
|
||||
.finally(async function () {
|
||||
if (try_cache) {
|
||||
if (log_lvl) {
|
||||
console.log(`Attempting to remove IDB entry for person_id=${person_id}`);
|
||||
}
|
||||
await db_core.person.delete(person_id);
|
||||
}
|
||||
});
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('ae_promises.delete__person_obj:', ae_promises.delete__person_obj);
|
||||
}
|
||||
|
||||
return ae_promises.delete__person_obj;
|
||||
}
|
||||
|
||||
// Updated 2025-06-10
|
||||
export async function update_ae_obj__person({
|
||||
api_cfg,
|
||||
person_id,
|
||||
data_kv,
|
||||
params = {},
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
person_id: string;
|
||||
data_kv: key_val;
|
||||
params?: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** update_ae_obj__person() *** person_id=${person_id}`, data_kv);
|
||||
}
|
||||
|
||||
// log_lvl = 1;
|
||||
|
||||
// Perform the API update
|
||||
const result = await api.update_ae_obj_id_crud({
|
||||
api_cfg: api_cfg,
|
||||
obj_type: 'person',
|
||||
obj_id: person_id,
|
||||
fields: data_kv,
|
||||
key: api_cfg.api_crud_super_key,
|
||||
params: params,
|
||||
return_obj: true,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
|
||||
// Handle the result
|
||||
if (result) {
|
||||
if (try_cache) {
|
||||
// Process the results first
|
||||
const processed_obj_li = await process_ae_obj__person_props({
|
||||
obj_li: [result],
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
if (log_lvl) {
|
||||
console.log('Processed object list:', processed_obj_li);
|
||||
}
|
||||
// Save the updated results list to the database
|
||||
if (log_lvl) {
|
||||
console.log('Saving to DB...');
|
||||
}
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'person',
|
||||
obj_li: processed_obj_li,
|
||||
properties_to_save: properties_to_save,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
if (log_lvl) {
|
||||
console.log('DB save completed.');
|
||||
}
|
||||
|
||||
// await db_save_ae_obj_li__person({
|
||||
// obj_type: 'person',
|
||||
// obj_li: [result],
|
||||
// log_lvl: log_lvl,
|
||||
// });
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
console.error('Failed to update person.');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Updated 2024-06-10
|
||||
export function db_save_ae_obj_li__person({
|
||||
obj_type,
|
||||
obj_li,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
obj_type: string;
|
||||
obj_li: any;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** db_save_ae_obj_li__person() ***`);
|
||||
}
|
||||
|
||||
if (obj_li && obj_li.length) {
|
||||
obj_li.forEach(async function (obj: any) {
|
||||
if (log_lvl) {
|
||||
console.log(`ae_obj ${obj_type}:`, obj);
|
||||
}
|
||||
|
||||
const obj_record = {
|
||||
id: obj.person_id_random,
|
||||
// id_random: obj.person_id_random,
|
||||
person_id: obj.person_id_random,
|
||||
person_id_random: obj.person_id_random,
|
||||
|
||||
external_id: obj.external_id,
|
||||
external_sys_id: obj.external_sys_id,
|
||||
code: obj.code,
|
||||
|
||||
account_id: obj.account_id_random,
|
||||
account_id_random: obj.account_id_random,
|
||||
|
||||
person_profile_id: obj.person_profile_id_random,
|
||||
person_profile_id_random: obj.person_profile_id_random, // The new table person_profile will be used soon...
|
||||
|
||||
user_id: obj.user_id_random,
|
||||
user_id_random: obj.user_id_random,
|
||||
|
||||
pronouns: obj.pronouns,
|
||||
informal_name: obj.informal_name,
|
||||
title_names: obj.title_names,
|
||||
given_name: obj.given_name,
|
||||
middle_name: obj.middle_name,
|
||||
family_name: obj.family_name,
|
||||
designations: obj.designations,
|
||||
|
||||
professional_title: obj.professional_title,
|
||||
|
||||
full_name: obj.full_name,
|
||||
full_name_override: obj.full_name_override, // was display_name and display_name_override
|
||||
|
||||
affiliations: obj.affiliations,
|
||||
|
||||
primary_email: obj.primary_email,
|
||||
|
||||
biography: obj.biography,
|
||||
|
||||
agree: obj.agree,
|
||||
comments: obj.comments,
|
||||
|
||||
allow_auth_key: obj.allow_auth_key, // For sign in without password
|
||||
// auth_key: obj.auth_key,
|
||||
passcode: obj.passcode,
|
||||
|
||||
data_json: obj.data_json,
|
||||
|
||||
enable: obj.enable,
|
||||
hide: obj.hide,
|
||||
priority: obj.priority,
|
||||
sort: obj.sort,
|
||||
group: obj.group,
|
||||
notes: obj.notes,
|
||||
created_on: obj.created_on,
|
||||
updated_on: obj.updated_on,
|
||||
|
||||
// From SQL view
|
||||
username: obj.username,
|
||||
user_name: obj.user_name,
|
||||
user_email: obj.user_email,
|
||||
user_allow_auth_key: obj.user_allow_auth_key, // For sign in without password
|
||||
user_super: obj.user_super,
|
||||
user_manager: obj.user_manager,
|
||||
user_administrator: obj.user_administrator,
|
||||
user_public: obj.user_public
|
||||
};
|
||||
|
||||
let id_random = null;
|
||||
|
||||
try {
|
||||
id_random = await db_core.person.update(obj_record.id, obj_record);
|
||||
} catch (error) {
|
||||
console.log(`Error: Failed to update ${obj_record.id}: ${error}`);
|
||||
}
|
||||
if (!id_random) {
|
||||
if (log_lvl) {
|
||||
console.log(`Failed to update record with ID: ${obj_record.id}. Trying put...`);
|
||||
}
|
||||
try {
|
||||
id_random = await db_core.person.put(obj_record);
|
||||
} catch (error) {
|
||||
console.log(`Error: Failed to put ${obj.person_id_random}: ${error}`);
|
||||
}
|
||||
} else {
|
||||
if (log_lvl) {
|
||||
console.log(`Updated record with ID: ${obj_record.id}`);
|
||||
}
|
||||
}
|
||||
if (!id_random) {
|
||||
console.log(`Failed to save record with ID: ${obj_record.id}`);
|
||||
} else {
|
||||
if (log_lvl) {
|
||||
console.log(`Saved record with ID: ${obj_record.id}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Updated 2025-06-10
|
||||
const properties_to_save = [
|
||||
'id',
|
||||
'person_id',
|
||||
// 'person_id_random',
|
||||
|
||||
'external_id',
|
||||
'external_sys_id',
|
||||
'code',
|
||||
|
||||
'account_id',
|
||||
// 'account_id_random',
|
||||
|
||||
'person_profile_id',
|
||||
// 'person_profile_id_random', // The new table person_profile will be used soon...
|
||||
|
||||
'user_id',
|
||||
// 'user_id_random',
|
||||
|
||||
'pronouns',
|
||||
'informal_name',
|
||||
'title_names',
|
||||
'given_name',
|
||||
'middle_name',
|
||||
'family_name',
|
||||
'designations',
|
||||
|
||||
'professional_title',
|
||||
|
||||
'full_name',
|
||||
'full_name_override', // was display_name and display_name_override
|
||||
|
||||
'affiliations',
|
||||
'primary_email',
|
||||
'biography',
|
||||
'agree',
|
||||
'comments',
|
||||
|
||||
'allow_auth_key', // For sign in without password
|
||||
// 'auth_key', // Should this be saved locally?
|
||||
'passcode',
|
||||
|
||||
// 'passcode_timeout',
|
||||
// 'passcode_read', // For LLM (AI) generated summary...???
|
||||
// 'passcode_read_expire',
|
||||
// 'passcode_write',
|
||||
// 'passcode_write_expire',
|
||||
// 'private_passcode',
|
||||
|
||||
// 'alert',
|
||||
// 'alert_msg',
|
||||
|
||||
'data_json',
|
||||
|
||||
'enable',
|
||||
'hide',
|
||||
'priority',
|
||||
'sort',
|
||||
'group',
|
||||
'notes',
|
||||
'created_on',
|
||||
'updated_on',
|
||||
|
||||
// Generated fields for sorting locally only
|
||||
'tmp_sort_1',
|
||||
'tmp_sort_2',
|
||||
'tmp_sort_3',
|
||||
|
||||
// From SQL view
|
||||
'username',
|
||||
// 'user_username', // Same as username
|
||||
'user_name',
|
||||
'user_email',
|
||||
'user_allow_auth_key', // For sign in without password
|
||||
'user_super',
|
||||
'user_manager',
|
||||
'user_administrator',
|
||||
'user_public',
|
||||
|
||||
'organization_id',
|
||||
// 'organization_id_random',
|
||||
'organization_name',
|
||||
|
||||
'contact_id',
|
||||
// 'contact_id_random',
|
||||
'contact_name',
|
||||
'contact_email',
|
||||
'contact_cc_email',
|
||||
'contact_phone_mobile',
|
||||
'contact_phone_home',
|
||||
'contact_phone_office',
|
||||
'contact_phone_land',
|
||||
'contact_phone_fax',
|
||||
'contact_phone_other',
|
||||
|
||||
'address_id',
|
||||
// 'address_id_random',
|
||||
'address_city',
|
||||
'address_country_alpha_2_code' // contact_address_country_alpha_2_code
|
||||
];
|
||||
|
||||
/**
|
||||
* NON-EXPORTED LOCAL HELPER
|
||||
* Processes a list of Aether objects by applying common and specific transformations.
|
||||
*/
|
||||
async function _process_generic_props<T extends Record<string, any>>({
|
||||
obj_li,
|
||||
obj_type,
|
||||
log_lvl = 0,
|
||||
specific_processor
|
||||
}: {
|
||||
obj_li: T[];
|
||||
obj_type: string;
|
||||
log_lvl?: number;
|
||||
specific_processor?: (obj: T) => Promise<T> | T;
|
||||
}): Promise<T[]> {
|
||||
if (log_lvl > 0) {
|
||||
console.log(
|
||||
`*** _process_generic_props: Processing ${obj_li.length} objects of type "${obj_type}" ***`
|
||||
);
|
||||
}
|
||||
|
||||
if (!obj_li || obj_li.length === 0) {
|
||||
if (log_lvl > 0) console.log('No objects to process.');
|
||||
return [];
|
||||
}
|
||||
|
||||
const processed_obj_li: T[] = [];
|
||||
|
||||
for (const original_obj of obj_li) {
|
||||
let processed_obj = { ...original_obj };
|
||||
|
||||
// --- Common Transformations ---
|
||||
|
||||
// 1. Standardize ID and other '_random' fields
|
||||
// The API often returns fields like 'person_id_random', which need to be aliased to 'person_id'.
|
||||
for (const key in processed_obj) {
|
||||
if (key.endsWith('_random')) {
|
||||
const new_key = key.slice(0, -7); // Remove '_random' suffix
|
||||
(processed_obj as any)[new_key] = processed_obj[key];
|
||||
}
|
||||
}
|
||||
// Ensure 'id' is set from '[obj_type]_id_random'
|
||||
const random_id_key = `${obj_type}_id_random`;
|
||||
if (processed_obj[random_id_key]) {
|
||||
(processed_obj as any).id = processed_obj[random_id_key];
|
||||
}
|
||||
|
||||
// 2. Create common computed properties for client-side sorting.
|
||||
const group = processed_obj.group ?? '0';
|
||||
const priority = processed_obj.priority ? 1 : 0;
|
||||
const sort = processed_obj.sort ?? '0';
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on;
|
||||
const name = processed_obj.name ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
// --- Specific Transformations ---
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
}
|
||||
|
||||
return processed_obj_li;
|
||||
}
|
||||
|
||||
// Updated 2025-06-10
|
||||
export async function process_ae_obj__person_props({
|
||||
obj_li,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
obj_li: any[];
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
return _process_generic_props({
|
||||
obj_li,
|
||||
obj_type: 'person',
|
||||
log_lvl,
|
||||
specific_processor: (obj) => {
|
||||
// Person-specific computed sort fields, overriding generic ones if needed
|
||||
obj.tmp_sort_1 = `${obj.group ?? '0'}_${obj.priority ? 1 : 0}_${
|
||||
obj.sort ?? '0'
|
||||
}_${obj.updated_on}_${obj.created_on}`;
|
||||
obj.tmp_sort_2 = `${obj.group ?? '0'}_${obj.priority ? 1 : 0}_${
|
||||
obj.sort ?? '0'
|
||||
}_${obj.updated_on ?? obj.created_on}`;
|
||||
obj.tmp_sort_3 = `${obj.group ?? '0'}_${obj.priority ? 1 : 0}_${
|
||||
obj.sort ?? '0'
|
||||
}_${obj.name ?? ''}_${obj.updated_on ?? obj.created_on}`;
|
||||
|
||||
return obj;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -49,7 +49,8 @@ export async function generate_qr_code({
|
||||
|
||||
if (qr_type == 'vcard') {
|
||||
if (qr_data.informal_name) {
|
||||
params['n'] = `${qr_data.family_name};${qr_data.given_name};${qr_data.informal_name}`;
|
||||
params['n'] =
|
||||
`${qr_data.family_name};${qr_data.given_name};${qr_data.informal_name}`;
|
||||
} else {
|
||||
params['n'] = `${qr_data.family_name};${qr_data.given_name}`;
|
||||
}
|
||||
@@ -99,7 +100,8 @@ export async function generate_qr_code({
|
||||
|
||||
// If return_blob is true, ensure we return an object URL for use in <img src=...>
|
||||
if (return_blob) {
|
||||
const data = ae_promises.generate_qr_code.data ?? ae_promises.generate_qr_code;
|
||||
const data =
|
||||
ae_promises.generate_qr_code.data ?? ae_promises.generate_qr_code;
|
||||
|
||||
// If already a Blob, use it directly
|
||||
if (data instanceof Blob) {
|
||||
@@ -133,7 +135,8 @@ export async function generate_qr_code({
|
||||
const blob = new Blob([data as BlobPart], { type: 'image/png' });
|
||||
return URL.createObjectURL(blob);
|
||||
} catch (e) {
|
||||
if (log_lvl) console.error('Could not create QR code image blob:', e, data);
|
||||
if (log_lvl)
|
||||
console.error('Could not create QR code image blob:', e, data);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -155,7 +158,10 @@ export async function generate_qr_code({
|
||||
* @returns {Promise<string>} A promise that resolves to a Base64 data URL of the QR code image.
|
||||
* @throws {Error} If the qr_type is unknown or data is missing.
|
||||
*/
|
||||
export async function js_generate_qr_code(qr_type: string, params: key_val = {}) {
|
||||
export async function js_generate_qr_code(
|
||||
qr_type: string,
|
||||
params: key_val = {}
|
||||
) {
|
||||
const {
|
||||
n = '',
|
||||
fn = '',
|
||||
@@ -175,10 +181,12 @@ export async function js_generate_qr_code(qr_type: string, params: key_val = {})
|
||||
key,
|
||||
val,
|
||||
js,
|
||||
str
|
||||
str,
|
||||
log_lvl = 0
|
||||
} = params;
|
||||
|
||||
console.log(`*** js_generate_qr_code() *** qr_type=${qr_type}`, params);
|
||||
if (log_lvl >= 2)
|
||||
console.log(`*** js_generate_qr_code() *** qr_type=${qr_type}`, params);
|
||||
|
||||
let qr_data: string | null = null;
|
||||
|
||||
@@ -210,13 +218,15 @@ export async function js_generate_qr_code(qr_type: string, params: key_val = {})
|
||||
|
||||
case 'obj':
|
||||
// Custom format: OBJ:ot:obj_type,oi:obj_id
|
||||
if (!obj_type || !obj_id) throw new Error('Missing obj_type or obj_id for type "obj".');
|
||||
if (!obj_type || !obj_id)
|
||||
throw new Error('Missing obj_type or obj_id for type "obj".');
|
||||
qr_data = `OBJ:ot:${obj_type},oi:${obj_id}`;
|
||||
break;
|
||||
|
||||
case 'kv':
|
||||
// Custom format: KV:k:"key",v:"val"
|
||||
if (!key || !val) throw new Error('Missing key or val for type "kv".');
|
||||
if (!key || !val)
|
||||
throw new Error('Missing key or val for type "kv".');
|
||||
qr_data = `KV:k:"${key}",v:"${val}"`;
|
||||
break;
|
||||
|
||||
@@ -228,7 +238,8 @@ export async function js_generate_qr_code(qr_type: string, params: key_val = {})
|
||||
|
||||
case 'str':
|
||||
// Raw string data
|
||||
if (!str) throw new Error('Missing raw string data for type "str".');
|
||||
if (!str)
|
||||
throw new Error('Missing raw string data for type "str".');
|
||||
qr_data = str;
|
||||
break;
|
||||
|
||||
@@ -251,7 +262,6 @@ export async function js_generate_qr_code(qr_type: string, params: key_val = {})
|
||||
scale: 10, // Corresponds to box_size
|
||||
type: 'image/png'
|
||||
});
|
||||
console.log('Generated QR code data URL:', data_url);
|
||||
return data_url;
|
||||
} catch (error) {
|
||||
console.error('Error generating QR code:', error);
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
export interface Site {
|
||||
id: string;
|
||||
// id_random: string;
|
||||
site_id: string;
|
||||
site_id_random?: string;
|
||||
|
||||
code?: string;
|
||||
|
||||
account_id: string;
|
||||
account_id_random?: string;
|
||||
|
||||
name: string;
|
||||
description?: null | string;
|
||||
|
||||
restrict_access?: null | boolean;
|
||||
access_key?: null | string;
|
||||
access_code_kv_json?: null | string;
|
||||
|
||||
logo_path?: null | string;
|
||||
logo_bg_color?: null | string; // Not really used currently.
|
||||
// background_image_path?: null|string; // Legacy field
|
||||
// background_bg_color?: null|string; // Legacy field
|
||||
title?: null | string;
|
||||
|
||||
// header_html?: null|string; // Legacy field
|
||||
// header_css?: null|string; // Legacy field
|
||||
// header_image_path?: null|string; // Legacy field
|
||||
// header_image_bg_color?: null|string; // Legacy field
|
||||
// body_html?: null|string; // Legacy field
|
||||
tagline?: null | string;
|
||||
// site_header_h1?: null|string; // Legacy field
|
||||
// site_header_h2?: null|string; // Legacy field
|
||||
style_href?: null | string; // Legacy field
|
||||
// script_src?: null|string; // Legacy field
|
||||
google_tracking_id?: null | string;
|
||||
|
||||
cfg_json?: null | string; // key value config json
|
||||
|
||||
enable: null | boolean;
|
||||
enable_from?: null | Date;
|
||||
enable_to?: null | Date;
|
||||
|
||||
hide?: null | boolean;
|
||||
priority?: null | boolean;
|
||||
sort?: null | number;
|
||||
group?: null | string;
|
||||
notes?: null | string;
|
||||
created_on: Date;
|
||||
updated_on?: null | Date;
|
||||
}
|
||||
@@ -1,69 +1,72 @@
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
import { api } from '$lib/api/api';
|
||||
import { db_lookups, LOOKUP_TTL_MS } from '$lib/ae_core/db_lookups';
|
||||
|
||||
import { db_core } from '$lib/ae_core/db_core';
|
||||
/**
|
||||
* Time zone lookup — IDB-backed SWR helper.
|
||||
*
|
||||
* Fetches priority timezones (only_priority=true, ~72 records). Calling this
|
||||
* function triggers a background API refresh if IDB is empty or older than
|
||||
* 24 hours. Components subscribe to db_lookups.lu_time_zone via liveQuery and
|
||||
* receive automatic updates when the refresh completes.
|
||||
*
|
||||
* Updated 2026-03-23 — replaced $ae_loc + localStorage pattern with IDB + 24h TTL
|
||||
*/
|
||||
|
||||
const ae_promises: key_val = {};
|
||||
|
||||
// Updated 2026-02-20
|
||||
export async function load_ae_obj_li__time_zone({
|
||||
async function _refresh_lu_time_zone_background({
|
||||
api_cfg,
|
||||
// account_id,
|
||||
enabled = 'enabled',
|
||||
hidden = 'not_hidden',
|
||||
limit = 1800, // There are roughly 1780 as of 2026-02
|
||||
offset = 0,
|
||||
// order_by_li = {'priority': 'DESC', 'group': 'ASC', 'sort': 'DESC', 'name': 'ASC'},
|
||||
order_by_li = { priority: 'DESC', sort: 'DESC', name: 'ASC' } as const,
|
||||
params = {},
|
||||
only_priority = false,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
api_cfg: any;
|
||||
// account_id: string,
|
||||
enabled?: 'enabled' | 'all' | 'not_enabled' | undefined;
|
||||
hidden?: 'hidden' | 'all' | 'not_hidden' | undefined;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
order_by_li?: key_val;
|
||||
params?: key_val;
|
||||
only_priority?: boolean;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_li__time_zone() *** only_priority=${only_priority}`);
|
||||
}
|
||||
|
||||
const params_json: key_val = {};
|
||||
|
||||
// console.log('params_json:', params_json);
|
||||
|
||||
ae_promises.load__time_zone_li = await api
|
||||
.get_ae_obj_li_for_lu({
|
||||
api_cfg: api_cfg,
|
||||
if (log_lvl) console.log('*** _refresh_lu_time_zone_background() ***');
|
||||
try {
|
||||
const result = await api.get_ae_obj_li_for_lu({
|
||||
api_cfg,
|
||||
for_lu_type: 'time_zone',
|
||||
enabled: enabled,
|
||||
hidden: hidden,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
params: params,
|
||||
only_priority: only_priority,
|
||||
log_lvl: log_lvl
|
||||
})
|
||||
.then(function (time_zone_li_get_result) {
|
||||
if (time_zone_li_get_result) {
|
||||
// handle_db_save_ae_obj_li__time_zone({obj_type: 'time_zone', obj_li: time_zone_li_get_result});
|
||||
return time_zone_li_get_result;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
})
|
||||
.catch(function (error: any) {
|
||||
console.log('No results returned or failed.', error);
|
||||
enabled: 'enabled',
|
||||
hidden: 'not_hidden',
|
||||
only_priority: true, // ~72 priority timezone records
|
||||
limit: 1800,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
console.log('ae_promises.load__time_zone_li:', ae_promises.load__time_zone_li);
|
||||
return ae_promises.load__time_zone_li;
|
||||
if (result?.length) {
|
||||
await db_lookups.lu_time_zone.clear();
|
||||
await db_lookups.lu_time_zone.bulkPut(result);
|
||||
await db_lookups.lu_cache_meta.put({
|
||||
lu_type: 'time_zone',
|
||||
refreshed_at: Date.now()
|
||||
});
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`lu_time_zone: saved ${result.length} records to IDB`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('lu_time_zone refresh failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function load_ae_obj_li__time_zone({
|
||||
api_cfg,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
api_cfg: any;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) console.log('*** load_ae_obj_li__time_zone() ***');
|
||||
|
||||
const count = await db_lookups.lu_time_zone.count();
|
||||
const meta = await db_lookups.lu_cache_meta.get('time_zone');
|
||||
const is_stale = !meta || Date.now() - meta.refreshed_at > LOOKUP_TTL_MS;
|
||||
|
||||
if (count === 0 || is_stale) {
|
||||
_refresh_lu_time_zone_background({ api_cfg, log_lvl });
|
||||
} else if (log_lvl) {
|
||||
console.log(
|
||||
`lu_time_zone: IDB fresh (${count} records), skipping refresh`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,352 +0,0 @@
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
import { api } from '$lib/api/api';
|
||||
|
||||
import { db_core } from '$lib/ae_core/db_core';
|
||||
|
||||
/*
|
||||
* *** LEGACY AUTHENTICATION HEADER LOGIC ***
|
||||
*
|
||||
* The functions in this file interact with legacy Aether API authentication endpoints
|
||||
* (e.g., /user/authenticate, /user/lookup_email).
|
||||
*
|
||||
* Unlike V3 endpoints which handle context automatically or via standard headers,
|
||||
* these legacy endpoints have specific requirements:
|
||||
*
|
||||
* 1. They often require the `x-account-id` header to be explicitly set to the target
|
||||
* account ID to find the user within that specific account context.
|
||||
* 2. The standard API wrapper logic might strip `x-account-id` if `x-no-account-id`
|
||||
* is present (Bootstrap Paradox logic). We must explicitly remove `x-no-account-id`
|
||||
* and set `x-account-id` to ensure the request is routed correctly.
|
||||
* 3. Some endpoints accept `account_id` as a query parameter, while others (like email sending)
|
||||
* may crash (500 Error) if unexpected parameters are passed.
|
||||
*/
|
||||
|
||||
const ae_promises: key_val = {};
|
||||
|
||||
// Updated 2025-04-04
|
||||
// This function handles username/password authentication.
|
||||
// It explicitly sets the x-account-id header to ensure the user is looked up in the correct account.
|
||||
export async function auth_ae_obj__username_password({
|
||||
api_cfg,
|
||||
account_id,
|
||||
null_account_id = false,
|
||||
username,
|
||||
password,
|
||||
params = {},
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
account_id: string;
|
||||
null_account_id?: boolean;
|
||||
username: string;
|
||||
password: string;
|
||||
params?: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`*** auth_ae_obj__username_password() *** account_id=${account_id} username=${username} password=${password}`
|
||||
);
|
||||
}
|
||||
|
||||
const endpoint = '/user/authenticate';
|
||||
|
||||
// Prepare API config with correct headers to override global guest settings
|
||||
const use_api_cfg = { ...api_cfg, headers: { ...api_cfg.headers } };
|
||||
if (account_id) {
|
||||
use_api_cfg.headers['x-account-id'] = account_id;
|
||||
delete use_api_cfg.headers['x-no-account-id'];
|
||||
params['account_id'] = account_id;
|
||||
}
|
||||
|
||||
if (null_account_id) {
|
||||
params['null_account_id'] = true;
|
||||
}
|
||||
params['username'] = username; // Required
|
||||
params['password'] = password; // Required
|
||||
params['inc_jwt'] = true; // Request a JWT in the response
|
||||
if (log_lvl > 1) {
|
||||
console.log(`auth_ae_obj__username_password() - params:`, params);
|
||||
}
|
||||
|
||||
ae_promises.auth__username_password = await api
|
||||
.get_object({
|
||||
api_cfg: use_api_cfg,
|
||||
endpoint: endpoint,
|
||||
params: params,
|
||||
// data: {},
|
||||
log_lvl: log_lvl
|
||||
})
|
||||
.then(async function (user_obj_get_result) {
|
||||
if (user_obj_get_result) {
|
||||
// if (try_cache) {
|
||||
// // This is expecting a list
|
||||
// db_save_ae_obj_li__user({
|
||||
// obj_type: 'user',
|
||||
// obj_li: [user_obj_get_result],
|
||||
// log_lvl: log_lvl
|
||||
// });
|
||||
// }
|
||||
return user_obj_get_result;
|
||||
} else {
|
||||
console.log('No results returned.');
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.catch(function (error: any) {
|
||||
console.log('No results returned or failed.', error);
|
||||
});
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('ae_promises.auth__username_password:', ae_promises.auth__username_password);
|
||||
}
|
||||
return ae_promises.auth__username_password;
|
||||
}
|
||||
|
||||
// Updated 2025-04-04
|
||||
// This function handles authentication using a User ID and a one-time auth key.
|
||||
export async function auth_ae_obj__user_id_user_auth_key({
|
||||
api_cfg,
|
||||
account_id,
|
||||
user_id,
|
||||
user_auth_key,
|
||||
params = {},
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
account_id: string;
|
||||
user_id: string;
|
||||
user_auth_key: string;
|
||||
params?: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`*** auth_ae_obj__user_id_user_auth_key() *** account_id=${account_id} user_id=${user_id}`
|
||||
);
|
||||
}
|
||||
|
||||
const endpoint = '/user/authenticate';
|
||||
|
||||
// Prepare API config with correct headers to override global guest settings
|
||||
const use_api_cfg = { ...api_cfg, headers: { ...api_cfg.headers } };
|
||||
if (account_id) {
|
||||
use_api_cfg.headers['x-account-id'] = account_id;
|
||||
delete use_api_cfg.headers['x-no-account-id'];
|
||||
params['account_id'] = account_id;
|
||||
}
|
||||
|
||||
params['user_id'] = user_id; // Required
|
||||
params['auth_key'] = user_auth_key; // Required
|
||||
params['inc_jwt'] = true; // Request a JWT in the response
|
||||
if (log_lvl > 1) {
|
||||
console.log(`auth_ae_obj__user_id_user_auth_key() - params:`, params);
|
||||
}
|
||||
|
||||
ae_promises.auth__user_id_user_key = await api
|
||||
.get_object({
|
||||
api_cfg: use_api_cfg,
|
||||
endpoint: endpoint,
|
||||
params: params,
|
||||
log_lvl: log_lvl
|
||||
})
|
||||
.then(async function (user_obj_get_result) {
|
||||
if (user_obj_get_result) {
|
||||
return user_obj_get_result;
|
||||
} else {
|
||||
console.log('No results returned.');
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.catch(function (error: any) {
|
||||
console.log('No results returned or failed.', error);
|
||||
});
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('ae_promises.auth__user_id_user_key:', ae_promises.auth__user_id_user_key);
|
||||
}
|
||||
return ae_promises.auth__user_id_user_key;
|
||||
}
|
||||
|
||||
// Send an email to the user with a new one time use authentication key. The new key must be generated and returned first.
|
||||
// Updated 2025-04-08
|
||||
// NOTE: This legacy endpoint is sensitive to extra query parameters and will 500 if account_id is passed in the URL.
|
||||
export async function send_email_auth_ae_obj__user_id({
|
||||
api_cfg,
|
||||
account_id,
|
||||
user_id,
|
||||
base_url,
|
||||
key_param_name = 'user_key', // API defaults to 'auth_key'
|
||||
params = {},
|
||||
// try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
account_id: string;
|
||||
user_id: string;
|
||||
base_url?: string;
|
||||
key_param_name?: string;
|
||||
params?: key_val;
|
||||
// try_cache?: boolean,
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`*** send_email_auth_ae_obj__user_id() *** account_id=${account_id} user_id=${user_id}`
|
||||
);
|
||||
}
|
||||
if (log_lvl > 1) {
|
||||
console.log(api_cfg);
|
||||
}
|
||||
|
||||
const email_auth_key_endpoint = `/user/${user_id}/email_auth_key_url`;
|
||||
params = {
|
||||
root_url: base_url,
|
||||
key_param_name: key_param_name
|
||||
};
|
||||
|
||||
// Prepare API config with correct headers to override global guest settings
|
||||
const use_api_cfg = { ...api_cfg, headers: { ...api_cfg.headers } };
|
||||
if (account_id) {
|
||||
use_api_cfg.headers['x-account-id'] = account_id;
|
||||
delete use_api_cfg.headers['x-no-account-id'];
|
||||
// WARNING: Do NOT add account_id to params here, as it causes a 500 error on the legacy backend.
|
||||
}
|
||||
|
||||
ae_promises.auth_key__send_email = await api.get_object({
|
||||
api_cfg: use_api_cfg,
|
||||
endpoint: email_auth_key_endpoint,
|
||||
params: params,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
|
||||
return ae_promises.auth_key__send_email;
|
||||
}
|
||||
|
||||
// Look up user based on email address provided
|
||||
// Updated 2025-04-08
|
||||
export async function qry_ae_obj_li__user_email({
|
||||
api_cfg,
|
||||
account_id,
|
||||
null_account_id = false,
|
||||
email,
|
||||
params = {},
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
account_id: string;
|
||||
null_account_id?: boolean;
|
||||
email: string;
|
||||
params?: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** qry_ae_obj_li__user_email() *** account_id=${account_id} email=${email}`);
|
||||
}
|
||||
|
||||
const endpoint = '/user/lookup_email';
|
||||
|
||||
// Prepare API config with correct headers to override global guest settings
|
||||
const use_api_cfg = { ...api_cfg, headers: { ...api_cfg.headers } };
|
||||
if (account_id) {
|
||||
use_api_cfg.headers['x-account-id'] = account_id;
|
||||
delete use_api_cfg.headers['x-no-account-id'];
|
||||
params['account_id'] = account_id;
|
||||
}
|
||||
|
||||
params['email'] = email; // Required
|
||||
params['null_account_id'] = null_account_id || false;
|
||||
|
||||
if (log_lvl > 1) {
|
||||
console.log(`qry_ae_obj_li__user_email() - params:`, params);
|
||||
}
|
||||
|
||||
ae_promises.qry__user_email = await api
|
||||
.get_object({
|
||||
api_cfg: use_api_cfg,
|
||||
endpoint: endpoint,
|
||||
params: params,
|
||||
log_lvl: log_lvl
|
||||
})
|
||||
.then(async function (user_obj_get_result) {
|
||||
if (user_obj_get_result) {
|
||||
return user_obj_get_result;
|
||||
} else {
|
||||
console.log('No results returned.');
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.catch(function (error: any) {
|
||||
console.log('No results returned or failed.', error);
|
||||
});
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('ae_promises.qry__user_email:', ae_promises.qry__user_email);
|
||||
}
|
||||
return ae_promises.qry__user_email;
|
||||
}
|
||||
|
||||
// Change user password
|
||||
// endpoint: PATCH /user/{user_id}/change_password
|
||||
// params:
|
||||
// data_kv: password (the new password)
|
||||
// Updated 2025-04-11
|
||||
export async function auth_ae_obj__user_id_change_password({
|
||||
api_cfg,
|
||||
account_id,
|
||||
user_id,
|
||||
password,
|
||||
params = {},
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
account_id: string;
|
||||
user_id: string;
|
||||
password: string;
|
||||
params?: key_val;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`*** auth_ae_obj__user_id_change_password() *** account_id=${account_id} user_id=${user_id}`
|
||||
);
|
||||
}
|
||||
|
||||
const endpoint = `/user/${user_id}/change_password`;
|
||||
|
||||
params['user_id'] = user_id; // Required
|
||||
if (log_lvl > 1) {
|
||||
console.log(`auth_ae_obj__user_id_change_password() - params:`, params);
|
||||
}
|
||||
|
||||
ae_promises.change_password__user_id = await api
|
||||
.patch_object({
|
||||
api_cfg: api_cfg,
|
||||
endpoint: endpoint,
|
||||
params: params,
|
||||
data: { password: password },
|
||||
log_lvl: log_lvl
|
||||
})
|
||||
.then(async function (change_password_result) {
|
||||
if (change_password_result) {
|
||||
return change_password_result;
|
||||
} else {
|
||||
console.log('No results returned.');
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.catch(function (error: any) {
|
||||
console.log('No results returned or failed.', error);
|
||||
});
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('ae_promises.change_password__user_id:', ae_promises.change_password__user_id);
|
||||
}
|
||||
return ae_promises.change_password__user_id;
|
||||
}
|
||||
81
src/lib/ae_core/db_lookups.ts
Normal file
81
src/lib/ae_core/db_lookups.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import Dexie, { type Table } from 'dexie';
|
||||
|
||||
/**
|
||||
* Lookup DB — IDB-backed cache for V3 Uniform Lookup System reference data.
|
||||
*
|
||||
* These tables store the deduplicated, priority-ranked list returned by
|
||||
* GET /v3/lookup/{lu_type}/list. Data is refreshed automatically on a 24-hour
|
||||
* TTL via the core__*.ts load helpers; components subscribe via liveQuery.
|
||||
*
|
||||
* Updated 2026-03-23
|
||||
*/
|
||||
|
||||
export interface LuCountry {
|
||||
id: number;
|
||||
group: string; // dedup key = alpha_2_code (e.g. "US")
|
||||
alpha_2_code: string;
|
||||
name: string;
|
||||
english_short_name?: string;
|
||||
name_override?: string;
|
||||
enable?: number;
|
||||
hide?: number;
|
||||
priority?: number;
|
||||
sort?: number;
|
||||
account_id?: number | null;
|
||||
[key: string]: unknown; // allow extra fields from API without TS errors
|
||||
}
|
||||
|
||||
export interface LuCountrySubdivision {
|
||||
id: number;
|
||||
group: string; // dedup key = code (e.g. "US-NY")
|
||||
code: string;
|
||||
name: string;
|
||||
country_alpha_2_code?: string;
|
||||
name_override?: string;
|
||||
enable?: number;
|
||||
hide?: number;
|
||||
priority?: number;
|
||||
sort?: number;
|
||||
account_id?: number | null;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface LuTimeZone {
|
||||
id: number;
|
||||
group: string; // dedup key = name (IANA identifier, e.g. "US/Eastern")
|
||||
name: string;
|
||||
name_override?: string; // display label override; prefer this over name when set
|
||||
enable?: number;
|
||||
hide?: number;
|
||||
priority?: number;
|
||||
sort?: number;
|
||||
account_id?: number | null;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface LuCacheMeta {
|
||||
lu_type: 'country' | 'country_subdivision' | 'time_zone';
|
||||
refreshed_at: number; // Unix timestamp ms — used for 24h TTL check
|
||||
}
|
||||
|
||||
class LookupsDexie extends Dexie {
|
||||
lu_country!: Table<LuCountry>;
|
||||
lu_country_subdivision!: Table<LuCountrySubdivision>;
|
||||
lu_time_zone!: Table<LuTimeZone>;
|
||||
lu_cache_meta!: Table<LuCacheMeta>;
|
||||
|
||||
constructor() {
|
||||
super('ae_lookups_db');
|
||||
this.version(1).stores({
|
||||
lu_country: 'id, alpha_2_code, group',
|
||||
lu_country_subdivision: 'id, code, country_alpha_2_code, group',
|
||||
lu_time_zone: 'id, name, group',
|
||||
lu_cache_meta: 'lu_type'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const db_lookups = new LookupsDexie();
|
||||
|
||||
/** 24-hour TTL in milliseconds */
|
||||
export const LOOKUP_TTL_MS = 24 * 60 * 60 * 1000;
|
||||
@@ -1,125 +1,140 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* AE_AITools.svelte
|
||||
* GENERIC Aether AI Toolset (Runes/Svelte 5)
|
||||
* Extracted logic from Journals module to be system-wide.
|
||||
*/
|
||||
import OpenAI from 'openai';
|
||||
import { Modal } from 'flowbite-svelte';
|
||||
import {
|
||||
Bot, BotMessageSquare, Loader, FileText,
|
||||
Save, FilePenLine, RotateCcw, Settings,
|
||||
RefreshCcw, Globe, Copy
|
||||
} from '@lucide/svelte';
|
||||
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
||||
import AE_Comp_Editor_CodeMirror from '$lib/elements/AE_Comp_Editor_CodeMirror.svelte';
|
||||
/**
|
||||
* AE_AITools.svelte
|
||||
* GENERIC Aether AI Toolset (Runes/Svelte 5)
|
||||
* Extracted logic from Journals module to be system-wide.
|
||||
*/
|
||||
import OpenAI from 'openai';
|
||||
import { Modal } from 'flowbite-svelte';
|
||||
import {
|
||||
Bot,
|
||||
BotMessageSquare,
|
||||
Loader,
|
||||
FileText,
|
||||
Save,
|
||||
FilePenLine,
|
||||
RotateCcw,
|
||||
Settings,
|
||||
RefreshCcw,
|
||||
Globe,
|
||||
Copy
|
||||
} from '@lucide/svelte';
|
||||
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
||||
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte';
|
||||
|
||||
interface Props {
|
||||
// Core Props
|
||||
content: string; // The text to summarize/analyze
|
||||
summary: string; // The result (bindable)
|
||||
|
||||
// Configuration (Bindable for global settings persistence)
|
||||
model?: string;
|
||||
baseUrl?: string;
|
||||
token?: string;
|
||||
systemPrompt?: string;
|
||||
maxTokens?: number;
|
||||
temperature?: number;
|
||||
|
||||
// Callbacks
|
||||
onSave?: (newSummary: string) => void;
|
||||
onSyncConfig?: () => void; // Optional: callback to sync from global site config
|
||||
|
||||
// UI Customization
|
||||
buttonClass?: string;
|
||||
log_lvl?: number;
|
||||
interface Props {
|
||||
// Core Props
|
||||
content: string; // The text to summarize/analyze
|
||||
summary: string; // The result (bindable)
|
||||
|
||||
// Configuration (Bindable for global settings persistence)
|
||||
model?: string;
|
||||
baseUrl?: string;
|
||||
token?: string;
|
||||
systemPrompt?: string;
|
||||
maxTokens?: number;
|
||||
temperature?: number;
|
||||
|
||||
// Callbacks
|
||||
onSave?: (newSummary: string) => void;
|
||||
onSyncConfig?: () => void; // Optional: callback to sync from global site config
|
||||
|
||||
// UI Customization
|
||||
buttonClass?: string;
|
||||
log_lvl?: number;
|
||||
}
|
||||
|
||||
let {
|
||||
content,
|
||||
summary = $bindable(),
|
||||
model = $bindable(),
|
||||
baseUrl = $bindable(),
|
||||
token = $bindable(),
|
||||
systemPrompt = $bindable(),
|
||||
maxTokens = $bindable(),
|
||||
temperature = $bindable(),
|
||||
onSave,
|
||||
onSyncConfig,
|
||||
buttonClass = 'btn btn-sm preset-tonal-primary shadow-lg hover:scale-105 transition-all',
|
||||
log_lvl = 0
|
||||
}: Props = $props();
|
||||
|
||||
// Apply defaults if undefined (Safe for Svelte 5 Runes)
|
||||
if (model === undefined) model = 'dgrzone-deepseek-8b-quick';
|
||||
if (baseUrl === undefined) baseUrl = 'https://ai.dgrzone.com/api';
|
||||
if (token === undefined) token = '';
|
||||
if (systemPrompt === undefined) systemPrompt = 'You are a helpful assistant.';
|
||||
if (maxTokens === undefined) maxTokens = 512;
|
||||
if (temperature === undefined) temperature = 0.7;
|
||||
|
||||
// Internal State
|
||||
let ae_promises: any = $state(null);
|
||||
let show_modal = $state(false);
|
||||
let active_tab: 'result' | 'settings' = $state('result');
|
||||
let tmp_summary = $state('');
|
||||
|
||||
async function generate_ai_result() {
|
||||
if (!content) {
|
||||
alert('No content available to analyze.');
|
||||
return;
|
||||
}
|
||||
|
||||
let {
|
||||
content,
|
||||
summary = $bindable(),
|
||||
model = $bindable(),
|
||||
baseUrl = $bindable(),
|
||||
token = $bindable(),
|
||||
systemPrompt = $bindable(),
|
||||
maxTokens = $bindable(),
|
||||
temperature = $bindable(),
|
||||
onSave,
|
||||
onSyncConfig,
|
||||
buttonClass = "btn btn-sm preset-tonal-primary shadow-lg hover:scale-105 transition-all",
|
||||
log_lvl = 0
|
||||
}: Props = $props();
|
||||
active_tab = 'result';
|
||||
|
||||
// Apply defaults if undefined (Safe for Svelte 5 Runes)
|
||||
if (model === undefined) model = 'dgrzone-deepseek-8b-quick';
|
||||
if (baseUrl === undefined) baseUrl = 'https://ai.dgrzone.com/api';
|
||||
if (token === undefined) token = '';
|
||||
if (systemPrompt === undefined) systemPrompt = 'You are a helpful assistant.';
|
||||
if (maxTokens === undefined) maxTokens = 512;
|
||||
if (temperature === undefined) temperature = 0.7;
|
||||
|
||||
// Internal State
|
||||
let ae_promises: any = $state(null);
|
||||
let show_modal = $state(false);
|
||||
let active_tab: 'result' | 'settings' = $state('result');
|
||||
let tmp_summary = $state('');
|
||||
|
||||
async function generate_ai_result() {
|
||||
if (!content) {
|
||||
alert('No content available to analyze.');
|
||||
return;
|
||||
}
|
||||
|
||||
active_tab = 'result';
|
||||
|
||||
// If no token is provided, trigger a "Demo Mode" placeholder after a fake delay
|
||||
if (!token || token === '') {
|
||||
console.log('AE_AITools: No token provided. Entering Demo Mode.');
|
||||
ae_promises = new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
tmp_summary = `### AI Summary (DEMO MODE)\n\nThis is a placeholder summary because no API token was provided in the settings. \n\n**Original Content Length:** ${content.length} characters.\n\n**System Prompt:** ${systemPrompt}\n\n**Model:** ${model}`;
|
||||
show_modal = true;
|
||||
resolve(true);
|
||||
}, 1500);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const ai_client = new OpenAI({
|
||||
apiKey: token,
|
||||
baseURL: baseUrl,
|
||||
dangerouslyAllowBrowser: true
|
||||
// If no token is provided, trigger a "Demo Mode" placeholder after a fake delay
|
||||
if (!token || token === '') {
|
||||
console.log('AE_AITools: No token provided. Entering Demo Mode.');
|
||||
ae_promises = new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
tmp_summary = `### AI Summary (DEMO MODE)\n\nThis is a placeholder summary because no API token was provided in the settings. \n\n**Original Content Length:** ${content.length} characters.\n\n**System Prompt:** ${systemPrompt}\n\n**Model:** ${model}`;
|
||||
show_modal = true;
|
||||
resolve(true);
|
||||
}, 1500);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ae_promises = ai_client.chat.completions.create({
|
||||
const ai_client = new OpenAI({
|
||||
apiKey: token,
|
||||
baseURL: baseUrl,
|
||||
dangerouslyAllowBrowser: true
|
||||
});
|
||||
|
||||
try {
|
||||
ae_promises = ai_client.chat.completions
|
||||
.create({
|
||||
model: model || 'dgrzone-deepseek-8b-quick',
|
||||
max_tokens: maxTokens,
|
||||
temperature: temperature,
|
||||
messages: [
|
||||
{ role: 'system', content: systemPrompt || 'You are a helpful assistant.' },
|
||||
{
|
||||
role: 'system',
|
||||
content: systemPrompt || 'You are a helpful assistant.'
|
||||
},
|
||||
{ role: 'user', content: content }
|
||||
]
|
||||
}).then((resp) => {
|
||||
const result = resp?.choices?.[0]?.message?.content || 'No result generated.';
|
||||
})
|
||||
.then((resp) => {
|
||||
const result =
|
||||
resp?.choices?.[0]?.message?.content ||
|
||||
'No result generated.';
|
||||
tmp_summary = result;
|
||||
show_modal = true;
|
||||
});
|
||||
} catch (err: any) {
|
||||
console.error('AE_AITools: AI Error:', err);
|
||||
// Even on error, show the modal with the error message so the UI can be inspected
|
||||
tmp_summary = `### AI Error\n\nFailed to connect to the AI service.\n\n**Error:** ${err.message}\n\nCheck your Settings tab for Base URL and Token configuration.`;
|
||||
show_modal = true;
|
||||
ae_promises = Promise.resolve();
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('AE_AITools: AI Error:', err);
|
||||
// Even on error, show the modal with the error message so the UI can be inspected
|
||||
tmp_summary = `### AI Error\n\nFailed to connect to the AI service.\n\n**Error:** ${err.message}\n\nCheck your Settings tab for Base URL and Token configuration.`;
|
||||
show_modal = true;
|
||||
ae_promises = Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
function handle_save() {
|
||||
summary = tmp_summary;
|
||||
if (onSave) onSave(tmp_summary);
|
||||
show_modal = false;
|
||||
}
|
||||
function handle_save() {
|
||||
summary = tmp_summary;
|
||||
if (onSave) onSave(tmp_summary);
|
||||
show_modal = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="ae-ai-tools-wrapper inline-flex items-center gap-1">
|
||||
@@ -128,13 +143,12 @@
|
||||
type="button"
|
||||
onclick={generate_ai_result}
|
||||
class={buttonClass}
|
||||
title="Generate AI summary/analysis"
|
||||
>
|
||||
title="Generate AI summary/analysis">
|
||||
{#await ae_promises}
|
||||
<Loader class="inline-block mr-1 animate-spin" size="1.2em" />
|
||||
<Loader class="mr-1 inline-block animate-spin" size="1.2em" />
|
||||
<span class="text-sm">Processing...</span>
|
||||
{:then}
|
||||
<BotMessageSquare class="inline-block mr-1" size="1.2em" />
|
||||
<BotMessageSquare class="mr-1 inline-block" size="1.2em" />
|
||||
<span class="text-sm">Summarize</span>
|
||||
{:catch}
|
||||
<span class="text-sm text-red-500">Error</span>
|
||||
@@ -149,8 +163,7 @@
|
||||
show_modal = true;
|
||||
}}
|
||||
class="btn btn-sm variant-soft-surface shadow-md"
|
||||
title="AI Settings"
|
||||
>
|
||||
title="AI Settings">
|
||||
<Settings size="1.2em" />
|
||||
</button>
|
||||
|
||||
@@ -160,32 +173,37 @@
|
||||
title="Aether AI Assistant"
|
||||
bind:open={show_modal}
|
||||
size="lg"
|
||||
class="bg-white dark:bg-gray-800"
|
||||
>
|
||||
class="bg-white dark:bg-gray-800">
|
||||
<div class="space-y-4 p-2">
|
||||
<!-- Tab Navigation -->
|
||||
<div class="flex gap-1 border-b border-surface-500/20 pb-2">
|
||||
<button
|
||||
class="btn btn-sm {active_tab === 'result' ? 'variant-filled-primary' : 'variant-soft-surface'}"
|
||||
onclick={() => active_tab = 'result'}
|
||||
>
|
||||
<div class="border-surface-500/20 flex gap-1 border-b pb-2">
|
||||
<button
|
||||
class="btn btn-sm {active_tab === 'result'
|
||||
? 'variant-filled-primary'
|
||||
: 'variant-soft-surface'}"
|
||||
onclick={() => (active_tab = 'result')}>
|
||||
<Bot size="1.1em" class="mr-1" /> Result
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm {active_tab === 'settings' ? 'variant-filled-secondary' : 'variant-soft-surface'}"
|
||||
onclick={() => active_tab = 'settings'}
|
||||
>
|
||||
<button
|
||||
class="btn btn-sm {active_tab === 'settings'
|
||||
? 'variant-filled-secondary'
|
||||
: 'variant-soft-surface'}"
|
||||
onclick={() => (active_tab = 'settings')}>
|
||||
<Settings size="1.1em" class="mr-1" /> Settings
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if active_tab === 'result'}
|
||||
<div class="space-y-4 animate-in fade-in duration-200">
|
||||
<div class="flex gap-2 justify-start">
|
||||
<button class="btn btn-sm variant-filled-success" onclick={handle_save}>
|
||||
<div class="animate-in fade-in space-y-4 duration-200">
|
||||
<div class="flex justify-start gap-2">
|
||||
<button
|
||||
class="btn btn-sm variant-filled-success"
|
||||
onclick={handle_save}>
|
||||
<Save size="1.1em" class="mr-1" /> Save Result
|
||||
</button>
|
||||
<button class="btn btn-sm variant-ghost-primary" onclick={generate_ai_result}>
|
||||
<button
|
||||
class="btn btn-sm variant-ghost-primary"
|
||||
onclick={generate_ai_result}>
|
||||
<RotateCcw size="1.1em" class="mr-1" /> Re-run
|
||||
</button>
|
||||
</div>
|
||||
@@ -195,57 +213,84 @@
|
||||
bind:new_content={tmp_summary}
|
||||
theme_mode={$ae_loc.theme_mode}
|
||||
placeholder="AI Result will appear here..."
|
||||
class_li="p-2 border rounded-lg h-96 shadow-inner bg-surface-500/5"
|
||||
/>
|
||||
class_li="p-2 border rounded-lg h-96 shadow-inner bg-surface-500/5" />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="space-y-6 animate-in slide-in-from-left-4 duration-200">
|
||||
<div
|
||||
class="animate-in slide-in-from-left-4 space-y-6 duration-200">
|
||||
<!-- Connection Settings -->
|
||||
<div class="space-y-4">
|
||||
<h3 class="text-sm font-bold uppercase tracking-widest text-surface-500 flex items-center gap-2">
|
||||
<h3
|
||||
class="text-surface-500 flex items-center gap-2 text-sm font-bold tracking-widest uppercase">
|
||||
<Globe size="1.1em" /> API Connection
|
||||
</h3>
|
||||
|
||||
|
||||
{#if onSyncConfig}
|
||||
<button class="btn btn-sm variant-soft-primary" onclick={onSyncConfig}>
|
||||
<Copy size="1.1em" class="mr-1" /> Sync Global Defaults
|
||||
<button
|
||||
class="btn btn-sm variant-soft-primary"
|
||||
onclick={onSyncConfig}>
|
||||
<Copy size="1.1em" class="mr-1" /> Sync Global
|
||||
Defaults
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<label class="label">
|
||||
<span>Base URL</span>
|
||||
<input type="text" bind:value={baseUrl} class="input input-sm" />
|
||||
<input
|
||||
type="text"
|
||||
bind:value={baseUrl}
|
||||
class="input input-sm" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span>Model</span>
|
||||
<input type="text" bind:value={model} class="input input-sm" />
|
||||
<input
|
||||
type="text"
|
||||
bind:value={model}
|
||||
class="input input-sm" />
|
||||
</label>
|
||||
</div>
|
||||
<label class="label">
|
||||
<span>API Token</span>
|
||||
<input type="password" bind:value={token} class="input input-sm font-mono" />
|
||||
<input
|
||||
type="password"
|
||||
bind:value={token}
|
||||
class="input input-sm font-mono" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Model Parameters -->
|
||||
<div class="space-y-4 pt-4 border-t border-surface-500/10">
|
||||
<h3 class="text-sm font-bold uppercase tracking-widest text-surface-500 flex items-center gap-2">
|
||||
<div
|
||||
class="border-surface-500/10 space-y-4 border-t pt-4">
|
||||
<h3
|
||||
class="text-surface-500 flex items-center gap-2 text-sm font-bold tracking-widest uppercase">
|
||||
<FilePenLine size="1.1em" /> Inference Parameters
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<label class="label">
|
||||
<span>Temperature ({temperature})</span>
|
||||
<input type="range" bind:value={temperature} min="0" max="1" step="0.1" class="range" />
|
||||
<input
|
||||
type="range"
|
||||
bind:value={temperature}
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.1"
|
||||
class="range" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span>Max Tokens</span>
|
||||
<input type="number" bind:value={maxTokens} class="input input-sm" />
|
||||
<input
|
||||
type="number"
|
||||
bind:value={maxTokens}
|
||||
class="input input-sm" />
|
||||
</label>
|
||||
</div>
|
||||
<label class="label">
|
||||
<span>System Prompt</span>
|
||||
<textarea bind:value={systemPrompt} class="textarea h-24 text-xs font-mono"></textarea>
|
||||
<textarea
|
||||
bind:value={systemPrompt}
|
||||
class="textarea h-24 font-mono text-xs"
|
||||
></textarea>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -253,4 +298,4 @@
|
||||
</div>
|
||||
</Modal>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* AE_MetadataFooter.svelte
|
||||
* GENERIC Aether Metadata Display
|
||||
* Reusable across all modules to standardize created/updated/original info.
|
||||
*/
|
||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||
import { ae_loc } from '$lib/stores/ae_stores';
|
||||
import { Clock, CalendarClock, Globe } from '@lucide/svelte';
|
||||
|
||||
interface Props {
|
||||
obj: any;
|
||||
showOriginal?: boolean;
|
||||
containerClass?: string;
|
||||
}
|
||||
|
||||
let {
|
||||
obj,
|
||||
showOriginal = true,
|
||||
containerClass = "ae_meta flex flex-col gap-2 p-4 border-t border-surface-500/10 text-xs text-surface-500 w-full"
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
<footer class={containerClass}>
|
||||
<!-- Original Date/Time Info (if applicable) -->
|
||||
{#if showOriginal && (obj?.original_datetime || obj?.original_timezone)}
|
||||
<div class="flex flex-row flex-wrap gap-x-4 gap-y-1 items-center bg-surface-500/5 p-2 rounded">
|
||||
<span class="flex items-center gap-1">
|
||||
<Globe size="1.1em" class="opacity-70" />
|
||||
<span class="font-bold uppercase tracking-tighter">Original:</span>
|
||||
{obj?.original_datetime ? ae_util.iso_datetime_formatter(obj.original_datetime, 'datetime_iso_12_no_seconds') : '--'}
|
||||
</span>
|
||||
{#if obj?.original_timezone}
|
||||
<span class="flex items-center gap-1">
|
||||
<span class="font-bold uppercase tracking-tighter">TZ:</span>
|
||||
{obj.original_timezone}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- System Timestamps -->
|
||||
<div class="flex flex-col sm:flex-row justify-between items-center gap-2 px-1">
|
||||
<div class="flex flex-wrap gap-4 justify-center sm:justify-start">
|
||||
<span class="flex items-center gap-1" title="Creation date">
|
||||
<CalendarClock size="1.1em" class="opacity-70 text-primary-500" />
|
||||
<span class="font-semibold uppercase tracking-tighter">Created:</span>
|
||||
{ae_util.iso_datetime_formatter(obj?.created_on, 'datetime_iso_12_no_seconds')}
|
||||
</span>
|
||||
{#if obj?.updated_on}
|
||||
<span class="flex items-center gap-1" title="Last update">
|
||||
<Clock size="1.1em" class="opacity-70 text-secondary-500" />
|
||||
<span class="font-semibold uppercase tracking-tighter">Updated:</span>
|
||||
{ae_util.iso_datetime_formatter(obj.updated_on, 'datetime_iso_12_no_seconds')}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if obj?.journal_entry_type || obj?.type}
|
||||
<span class="badge variant-soft-surface text-[10px] uppercase font-bold tracking-widest">
|
||||
Type: {obj?.journal_entry_type || obj?.type}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
</footer>
|
||||
@@ -1,58 +1,64 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* AE_ObjectFlags.svelte
|
||||
* GENERIC Aether Object Flags & Visibility Toggles
|
||||
* Manages: alert, private, public, personal, professional, template
|
||||
*/
|
||||
import {
|
||||
Siren, MessageSquareWarning, Fingerprint,
|
||||
Globe, BookHeart, BriefcaseBusiness, NotepadTextDashed,
|
||||
Settings
|
||||
} from '@lucide/svelte';
|
||||
import { ae_loc } from '$lib/stores/ae_stores';
|
||||
/**
|
||||
* AE_ObjectFlags.svelte
|
||||
* GENERIC Aether Object Flags & Visibility Toggles
|
||||
* Manages: alert, private, public, personal, professional, template
|
||||
*/
|
||||
import {
|
||||
Siren,
|
||||
MessageSquareWarning,
|
||||
Fingerprint,
|
||||
Globe,
|
||||
BookHeart,
|
||||
BriefcaseBusiness,
|
||||
NotepadTextDashed,
|
||||
Settings
|
||||
} from '@lucide/svelte';
|
||||
import { ae_loc } from '$lib/stores/ae_stores';
|
||||
|
||||
interface Props {
|
||||
// The object containing the flags (bindable)
|
||||
obj: any;
|
||||
interface Props {
|
||||
// The object containing the flags (bindable)
|
||||
obj: any;
|
||||
|
||||
// Visibility configuration (optional overrides)
|
||||
show_labels?: boolean;
|
||||
hide_alert?: boolean;
|
||||
hide_private?: boolean;
|
||||
hide_public?: boolean;
|
||||
hide_personal?: boolean;
|
||||
hide_professional?: boolean;
|
||||
hide_template?: boolean;
|
||||
// Visibility configuration (optional overrides)
|
||||
show_labels?: boolean;
|
||||
hide_alert?: boolean;
|
||||
hide_private?: boolean;
|
||||
hide_public?: boolean;
|
||||
hide_personal?: boolean;
|
||||
hide_professional?: boolean;
|
||||
hide_template?: boolean;
|
||||
|
||||
// Callbacks
|
||||
on_toggle?: (prop: string, newValue: boolean) => void;
|
||||
// Callbacks
|
||||
on_toggle?: (prop: string, newValue: boolean) => void;
|
||||
|
||||
// Styling
|
||||
container_class?: string;
|
||||
}
|
||||
// Styling
|
||||
container_class?: string;
|
||||
}
|
||||
|
||||
let {
|
||||
obj = $bindable(),
|
||||
show_labels = true,
|
||||
hide_alert: hide_alert = false,
|
||||
hide_private: hide_private = false,
|
||||
hide_public: hide_public = false,
|
||||
hide_personal: hide_personal = false,
|
||||
hide_professional: hide_professional = false,
|
||||
hide_template: hide_template = false,
|
||||
on_toggle: onToggle,
|
||||
container_class = "flex flex-row flex-wrap gap-1 items-center justify-evenly py-2 border-y border-surface-500/10"
|
||||
}: Props = $props();
|
||||
let {
|
||||
obj = $bindable(),
|
||||
show_labels = true,
|
||||
hide_alert: hide_alert = false,
|
||||
hide_private: hide_private = false,
|
||||
hide_public: hide_public = false,
|
||||
hide_personal: hide_personal = false,
|
||||
hide_professional: hide_professional = false,
|
||||
hide_template: hide_template = false,
|
||||
on_toggle: onToggle,
|
||||
container_class = 'flex flex-row flex-wrap gap-1 items-center justify-evenly py-2 border-y border-surface-500/10'
|
||||
}: Props = $props();
|
||||
|
||||
function handle_toggle(prop: string) {
|
||||
obj[prop] = !obj[prop];
|
||||
if (onToggle) onToggle(prop, obj[prop]);
|
||||
}
|
||||
function handle_toggle(prop: string) {
|
||||
obj[prop] = !obj[prop];
|
||||
if (onToggle) onToggle(prop, obj[prop]);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class={container_class}>
|
||||
{#if show_labels}
|
||||
<span class="text-xs text-surface-500 flex items-center gap-1 uppercase font-bold tracking-wider mr-2">
|
||||
<span
|
||||
class="text-surface-500 mr-2 flex items-center gap-1 text-xs font-bold tracking-wider uppercase">
|
||||
<Settings size="1.1em" /> Flags:
|
||||
</span>
|
||||
{/if}
|
||||
@@ -63,9 +69,10 @@
|
||||
type="button"
|
||||
onclick={() => handle_toggle('alert')}
|
||||
class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition"
|
||||
title="Toggle Alert Status"
|
||||
>
|
||||
<Siren size="1.2em" class={obj?.alert ? 'text-error-500' : 'opacity-40'} />
|
||||
title="Toggle Alert Status">
|
||||
<Siren
|
||||
size="1.2em"
|
||||
class={obj?.alert ? 'text-error-500' : 'opacity-40'} />
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
@@ -75,9 +82,10 @@
|
||||
type="button"
|
||||
onclick={() => handle_toggle('private')}
|
||||
class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition"
|
||||
title="Toggle Private/Encrypted"
|
||||
>
|
||||
<Fingerprint size="1.2em" class={obj?.private ? 'text-success-500' : 'opacity-40'} />
|
||||
title="Toggle Private/Encrypted">
|
||||
<Fingerprint
|
||||
size="1.2em"
|
||||
class={obj?.private ? 'text-success-500' : 'opacity-40'} />
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
@@ -87,9 +95,10 @@
|
||||
type="button"
|
||||
onclick={() => handle_toggle('public')}
|
||||
class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition"
|
||||
title="Toggle Public Visibility"
|
||||
>
|
||||
<Globe size="1.2em" class={obj?.public ? 'text-success-500' : 'opacity-40'} />
|
||||
title="Toggle Public Visibility">
|
||||
<Globe
|
||||
size="1.2em"
|
||||
class={obj?.public ? 'text-success-500' : 'opacity-40'} />
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
@@ -99,9 +108,10 @@
|
||||
type="button"
|
||||
onclick={() => handle_toggle('personal')}
|
||||
class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition"
|
||||
title="Toggle Personal Scope"
|
||||
>
|
||||
<BookHeart size="1.2em" class={obj?.personal ? 'text-success-500' : 'opacity-40'} />
|
||||
title="Toggle Personal Scope">
|
||||
<BookHeart
|
||||
size="1.2em"
|
||||
class={obj?.personal ? 'text-success-500' : 'opacity-40'} />
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
@@ -111,9 +121,10 @@
|
||||
type="button"
|
||||
onclick={() => handle_toggle('professional')}
|
||||
class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition"
|
||||
title="Toggle Professional Scope"
|
||||
>
|
||||
<BriefcaseBusiness size="1.2em" class={obj?.professional ? 'text-success-500' : 'opacity-40'} />
|
||||
title="Toggle Professional Scope">
|
||||
<BriefcaseBusiness
|
||||
size="1.2em"
|
||||
class={obj?.professional ? 'text-success-500' : 'opacity-40'} />
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
@@ -123,9 +134,10 @@
|
||||
type="button"
|
||||
onclick={() => handle_toggle('template')}
|
||||
class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition"
|
||||
title="Toggle Template Mode"
|
||||
>
|
||||
<NotepadTextDashed size="1.2em" class={obj?.template ? 'text-success-500' : 'opacity-40'} />
|
||||
title="Toggle Template Mode">
|
||||
<NotepadTextDashed
|
||||
size="1.2em"
|
||||
class={obj?.template ? 'text-success-500' : 'opacity-40'} />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,95 +1,96 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* AE_Record_Controls.svelte
|
||||
* GENERIC Aether Record Management Controls
|
||||
* Manages: priority, hide, enable, alert, delete/disable
|
||||
*
|
||||
* Emits events — NO API calls. Parent is responsible for:
|
||||
* 1. Calling the API (update_ae_obj_v3, delete_ae_obj_id__*)
|
||||
* 2. Refreshing the object from cache/API
|
||||
* 3. Navigating away on delete
|
||||
*
|
||||
* Usage:
|
||||
* <AE_Record_Controls
|
||||
* obj={$lq__event_session_obj}
|
||||
* obj_label="session"
|
||||
* allow_delete={$ae_loc.manager_access}
|
||||
* allow_disable={$ae_loc.administrator_access}
|
||||
* on_toggle={(field, val) => { ... }}
|
||||
* on_delete={(method) => { ... }}
|
||||
* />
|
||||
*/
|
||||
import {
|
||||
Star,
|
||||
Eye,
|
||||
EyeOff,
|
||||
ToggleLeft,
|
||||
ToggleRight,
|
||||
Bell,
|
||||
BellOff,
|
||||
Trash2,
|
||||
CircleMinus,
|
||||
Settings
|
||||
} from '@lucide/svelte';
|
||||
/**
|
||||
* AE_Record_Controls.svelte
|
||||
* GENERIC Aether Record Management Controls
|
||||
* Manages: priority, hide, enable, alert, delete/disable
|
||||
*
|
||||
* Emits events — NO API calls. Parent is responsible for:
|
||||
* 1. Calling the API (update_ae_obj, delete_ae_obj_id__*)
|
||||
* 2. Refreshing the object from cache/API
|
||||
* 3. Navigating away on delete
|
||||
*
|
||||
* Usage:
|
||||
* <AE_Record_Controls
|
||||
* obj={$lq__event_session_obj}
|
||||
* obj_label="session"
|
||||
* allow_delete={$ae_loc.manager_access}
|
||||
* allow_disable={$ae_loc.administrator_access}
|
||||
* on_toggle={(field, val) => { ... }}
|
||||
* on_delete={(method) => { ... }}
|
||||
* />
|
||||
*/
|
||||
import {
|
||||
Star,
|
||||
Eye,
|
||||
EyeOff,
|
||||
ToggleLeft,
|
||||
ToggleRight,
|
||||
Bell,
|
||||
BellOff,
|
||||
Trash2,
|
||||
CircleMinus,
|
||||
Settings
|
||||
} from '@lucide/svelte';
|
||||
|
||||
interface Props {
|
||||
// The object whose flags are being displayed (read-only — parent owns state)
|
||||
obj: any;
|
||||
interface Props {
|
||||
// The object whose flags are being displayed (read-only — parent owns state)
|
||||
obj: any;
|
||||
|
||||
// Human-readable label for confirm dialogs ("session", "presenter", "location", etc.)
|
||||
obj_label?: string;
|
||||
// Human-readable label for confirm dialogs ("session", "presenter", "location", etc.)
|
||||
obj_label?: string;
|
||||
|
||||
// Visibility — hide any control that doesn't apply for this object type
|
||||
show_alert?: boolean;
|
||||
show_priority?: boolean;
|
||||
show_enable?: boolean;
|
||||
show_hide?: boolean;
|
||||
show_labels?: boolean;
|
||||
// Visibility — hide any control that doesn't apply for this object type
|
||||
show_alert?: boolean;
|
||||
show_priority?: boolean;
|
||||
show_enable?: boolean;
|
||||
show_hide?: boolean;
|
||||
show_labels?: boolean;
|
||||
|
||||
// Permission gates — parent passes booleans derived from $ae_loc
|
||||
allow_delete?: boolean; // Hard permanent delete (manager+)
|
||||
allow_disable?: boolean; // Soft disable/remove (administrator+)
|
||||
// Permission gates — parent passes booleans derived from $ae_loc
|
||||
allow_delete?: boolean; // Hard permanent delete (manager+)
|
||||
allow_disable?: boolean; // Soft disable/remove (administrator+)
|
||||
|
||||
// Callbacks — parent handles API + refresh + navigation
|
||||
on_toggle?: (field: string, new_val: boolean) => void;
|
||||
on_delete?: (method: 'delete' | 'disable') => void;
|
||||
// Callbacks — parent handles API + refresh + navigation
|
||||
on_toggle?: (field: string, new_val: boolean) => void;
|
||||
on_delete?: (method: 'delete' | 'disable') => void;
|
||||
|
||||
// Styling
|
||||
container_class?: string;
|
||||
}
|
||||
// Styling
|
||||
container_class?: string;
|
||||
}
|
||||
|
||||
let {
|
||||
obj,
|
||||
obj_label = 'record',
|
||||
show_alert = true,
|
||||
show_priority = true,
|
||||
show_enable = true,
|
||||
show_hide = true,
|
||||
show_labels = true,
|
||||
allow_delete = false,
|
||||
allow_disable = false,
|
||||
on_toggle,
|
||||
on_delete,
|
||||
container_class = 'flex flex-row flex-wrap gap-1 items-center justify-evenly py-2 border-y border-surface-500/10'
|
||||
}: Props = $props();
|
||||
let {
|
||||
obj,
|
||||
obj_label = 'record',
|
||||
show_alert = true,
|
||||
show_priority = true,
|
||||
show_enable = true,
|
||||
show_hide = true,
|
||||
show_labels = true,
|
||||
allow_delete = false,
|
||||
allow_disable = false,
|
||||
on_toggle,
|
||||
on_delete,
|
||||
container_class = 'flex flex-row flex-wrap gap-1 items-center justify-evenly py-2 border-y border-surface-500/10'
|
||||
}: Props = $props();
|
||||
|
||||
function toggle(field: string) {
|
||||
if (on_toggle) on_toggle(field, !obj?.[field]);
|
||||
}
|
||||
function toggle(field: string) {
|
||||
if (on_toggle) on_toggle(field, !obj?.[field]);
|
||||
}
|
||||
|
||||
function handle_delete(method: 'delete' | 'disable') {
|
||||
const msg =
|
||||
method === 'delete'
|
||||
? `Permanently delete this ${obj_label}? This cannot be undone.`
|
||||
: `Remove (disable) this ${obj_label}?`;
|
||||
if (!confirm(msg)) return;
|
||||
if (on_delete) on_delete(method);
|
||||
}
|
||||
function handle_delete(method: 'delete' | 'disable') {
|
||||
const msg =
|
||||
method === 'delete'
|
||||
? `Permanently delete this ${obj_label}? This cannot be undone.`
|
||||
: `Remove (disable) this ${obj_label}?`;
|
||||
if (!confirm(msg)) return;
|
||||
if (on_delete) on_delete(method);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class={container_class}>
|
||||
{#if show_labels}
|
||||
<span class="text-xs text-surface-500 flex items-center gap-1 uppercase font-bold tracking-wider mr-2">
|
||||
<span
|
||||
class="text-surface-500 mr-2 flex items-center gap-1 text-xs font-bold tracking-wider uppercase">
|
||||
<Settings size="1.1em" /> Controls:
|
||||
</span>
|
||||
{/if}
|
||||
@@ -103,12 +104,10 @@
|
||||
class:preset-filled-warning-500={obj?.priority}
|
||||
class:preset-tonal-secondary={!obj?.priority}
|
||||
class:hover:preset-filled-warning-500={!obj?.priority}
|
||||
title={obj?.priority ? 'Remove priority flag' : 'Mark as priority'}
|
||||
>
|
||||
title={obj?.priority ? 'Remove priority flag' : 'Mark as priority'}>
|
||||
<Star
|
||||
size="1.2em"
|
||||
class={obj?.priority ? 'fill-current' : 'opacity-50'}
|
||||
/>
|
||||
class={obj?.priority ? 'fill-current' : 'opacity-50'} />
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
@@ -121,8 +120,7 @@
|
||||
class:preset-filled-warning-500={obj?.hide}
|
||||
class:preset-tonal-secondary={!obj?.hide}
|
||||
class:hover:preset-filled-warning-500={!obj?.hide}
|
||||
title={obj?.hide ? 'Unhide this record' : 'Hide this record'}
|
||||
>
|
||||
title={obj?.hide ? 'Unhide this record' : 'Hide this record'}>
|
||||
{#if obj?.hide}
|
||||
<EyeOff size="1.2em" class="text-warning-500" />
|
||||
{:else}
|
||||
@@ -140,8 +138,7 @@
|
||||
class:preset-filled-success-500={obj?.enable}
|
||||
class:preset-filled-error-500={!obj?.enable}
|
||||
class:hover:preset-filled-success-500={!obj?.enable}
|
||||
title={obj?.enable ? 'Disable this record' : 'Enable this record'}
|
||||
>
|
||||
title={obj?.enable ? 'Disable this record' : 'Enable this record'}>
|
||||
{#if obj?.enable}
|
||||
<ToggleRight size="1.2em" class="text-success-300" />
|
||||
{:else}
|
||||
@@ -159,8 +156,7 @@
|
||||
class:preset-filled-error-500={obj?.alert}
|
||||
class:preset-tonal-secondary={!obj?.alert}
|
||||
class:hover:preset-filled-error-500={!obj?.alert}
|
||||
title={obj?.alert ? 'Remove alert status' : 'Mark as alert'}
|
||||
>
|
||||
title={obj?.alert ? 'Remove alert status' : 'Mark as alert'}>
|
||||
{#if obj?.alert}
|
||||
<Bell size="1.2em" class="text-error-300" />
|
||||
{:else}
|
||||
@@ -175,8 +171,7 @@
|
||||
type="button"
|
||||
onclick={() => handle_delete('delete')}
|
||||
class="btn-icon btn-icon-sm preset-filled-error-500 hover:preset-filled-error-600 transition"
|
||||
title="Permanently delete this {obj_label}"
|
||||
>
|
||||
title="Permanently delete this {obj_label}">
|
||||
<Trash2 size="1.2em" />
|
||||
</button>
|
||||
{:else if allow_disable}
|
||||
@@ -184,8 +179,7 @@
|
||||
type="button"
|
||||
onclick={() => handle_delete('disable')}
|
||||
class="btn-icon btn-icon-sm preset-filled-warning-500 hover:preset-filled-warning-600 transition"
|
||||
title="Disable / soft-remove this {obj_label}"
|
||||
>
|
||||
title="Disable / soft-remove this {obj_label}">
|
||||
<CircleMinus size="1.2em" />
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
import { api } from '$lib/api/api';
|
||||
import { get_ae_obj_li_for_obj_id_crud_v2 } from '$lib/ae_api/api_get__crud_obj_li_v2';
|
||||
|
||||
import { db_save_ae_obj_li__ae_obj } from '$lib/ae_core/core__idb_dexie';
|
||||
import { db_events } from '$lib/ae_events/db_events';
|
||||
@@ -47,7 +46,9 @@ export async function load_ae_obj_id__event({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Event | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_id__event() *** event_id=${event_id} (SWR Optimization)`);
|
||||
console.log(
|
||||
`*** load_ae_obj_id__event() *** event_id=${event_id} (SWR Optimization)`
|
||||
);
|
||||
}
|
||||
|
||||
// Hierarchy Enforcement: Pulling presentations/presenters requires pulling sessions first
|
||||
@@ -58,20 +59,43 @@ export async function load_ae_obj_id__event({
|
||||
try {
|
||||
const cached_event = await db_events.event.get(event_id);
|
||||
if (cached_event) {
|
||||
if (log_lvl) console.log('EVENT LOAD: Cache hit. Returning stale data immediately.');
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
'EVENT LOAD: Cache hit. Returning stale data immediately.'
|
||||
);
|
||||
|
||||
// Trigger background refresh
|
||||
_refresh_event_v3_background({
|
||||
api_cfg, event_id, view, try_cache,
|
||||
inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_template_li,
|
||||
enabled, hidden,
|
||||
_refresh_event_background({
|
||||
api_cfg,
|
||||
event_id,
|
||||
view,
|
||||
try_cache,
|
||||
inc_device_li,
|
||||
inc_file_li,
|
||||
inc_location_li,
|
||||
inc_session_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
inc_template_li,
|
||||
enabled,
|
||||
hidden,
|
||||
log_lvl: 0
|
||||
});
|
||||
|
||||
// Still handle nested loads for the cached version to ensure UI richness
|
||||
return await _handle_nested_loads(cached_event, {
|
||||
api_cfg, inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_template_li,
|
||||
enabled, hidden, try_cache, log_lvl
|
||||
api_cfg,
|
||||
inc_device_li,
|
||||
inc_file_li,
|
||||
inc_location_li,
|
||||
inc_session_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
inc_template_li,
|
||||
enabled,
|
||||
hidden,
|
||||
try_cache,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -80,10 +104,20 @@ export async function load_ae_obj_id__event({
|
||||
}
|
||||
|
||||
// 2. SLOW PATH: Wait for API if cache is empty or try_cache is false
|
||||
return await _refresh_event_v3_background({
|
||||
api_cfg, event_id, view, try_cache,
|
||||
inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_template_li,
|
||||
enabled, hidden,
|
||||
return await _refresh_event_background({
|
||||
api_cfg,
|
||||
event_id,
|
||||
view,
|
||||
try_cache,
|
||||
inc_device_li,
|
||||
inc_file_li,
|
||||
inc_location_li,
|
||||
inc_session_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
inc_template_li,
|
||||
enabled,
|
||||
hidden,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
@@ -91,10 +125,20 @@ export async function load_ae_obj_id__event({
|
||||
/**
|
||||
* Internal helper to perform the actual API fetch and cache update for events
|
||||
*/
|
||||
async function _refresh_event_v3_background({
|
||||
api_cfg, event_id, view, try_cache,
|
||||
inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_template_li,
|
||||
enabled, hidden,
|
||||
async function _refresh_event_background({
|
||||
api_cfg,
|
||||
event_id,
|
||||
view,
|
||||
try_cache,
|
||||
inc_device_li,
|
||||
inc_file_li,
|
||||
inc_location_li,
|
||||
inc_session_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
inc_template_li,
|
||||
enabled,
|
||||
hidden,
|
||||
log_lvl
|
||||
}: any) {
|
||||
// Check if offline
|
||||
@@ -104,7 +148,7 @@ async function _refresh_event_v3_background({
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await api.get_ae_obj_v3({
|
||||
const result = await api.get_ae_obj({
|
||||
api_cfg: api_cfg,
|
||||
obj_type: 'event',
|
||||
obj_id: event_id,
|
||||
@@ -130,8 +174,18 @@ async function _refresh_event_v3_background({
|
||||
}
|
||||
|
||||
return await _handle_nested_loads(processed_obj, {
|
||||
api_cfg, inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_template_li,
|
||||
enabled, hidden, try_cache: false, log_lvl
|
||||
api_cfg,
|
||||
inc_device_li,
|
||||
inc_file_li,
|
||||
inc_location_li,
|
||||
inc_session_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
inc_template_li,
|
||||
enabled,
|
||||
hidden,
|
||||
try_cache: false,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
} catch (error: any) {
|
||||
@@ -143,8 +197,27 @@ async function _refresh_event_v3_background({
|
||||
/**
|
||||
* Shared logic for loading nested child collections
|
||||
*/
|
||||
async function _handle_nested_loads(event_obj: any, { api_cfg, inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_template_li, enabled, hidden, try_cache, log_lvl }: any) {
|
||||
if (log_lvl) console.log(`Loading nested collections for event: ${event_obj.event_id} (Devices: ${inc_device_li}, Files: ${inc_file_li}, Locations: ${inc_location_li}, Sessions: ${inc_session_li}, Presentations: ${inc_presentation_li}, Presenters: ${inc_presenter_li}, Templates: ${inc_template_li})`);
|
||||
async function _handle_nested_loads(
|
||||
event_obj: any,
|
||||
{
|
||||
api_cfg,
|
||||
inc_device_li,
|
||||
inc_file_li,
|
||||
inc_location_li,
|
||||
inc_session_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
inc_template_li,
|
||||
enabled,
|
||||
hidden,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}: any
|
||||
) {
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`Loading nested collections for event: ${event_obj.event_id} (Devices: ${inc_device_li}, Files: ${inc_file_li}, Locations: ${inc_location_li}, Sessions: ${inc_session_li}, Presentations: ${inc_presentation_li}, Presenters: ${inc_presenter_li}, Templates: ${inc_template_li})`
|
||||
);
|
||||
// String-Only ID Vision: the '_id' field IS the string ID
|
||||
const current_event_id = event_obj.id || event_obj.event_id;
|
||||
|
||||
@@ -152,56 +225,66 @@ async function _handle_nested_loads(event_obj: any, { api_cfg, inc_device_li, in
|
||||
const tasks = [];
|
||||
|
||||
if (inc_device_li) {
|
||||
tasks.push(load_ae_obj_li__event_device({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: current_event_id,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}).then(res => event_obj.event_device_obj_li = res));
|
||||
tasks.push(
|
||||
load_ae_obj_li__event_device({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: current_event_id,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}).then((res) => (event_obj.event_device_obj_li = res))
|
||||
);
|
||||
}
|
||||
if (inc_file_li) {
|
||||
tasks.push(load_ae_obj_li__event_file({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: current_event_id,
|
||||
enabled: 'all',
|
||||
limit: 100,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}).then(res => event_obj.event_file_li = res));
|
||||
tasks.push(
|
||||
load_ae_obj_li__event_file({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: current_event_id,
|
||||
enabled: 'all',
|
||||
limit: 100,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}).then((res) => (event_obj.event_file_li = res))
|
||||
);
|
||||
}
|
||||
if (inc_location_li) {
|
||||
tasks.push(load_ae_obj_li__event_location({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: current_event_id,
|
||||
enabled,
|
||||
hidden,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}).then(res => event_obj.event_location_obj_li = res));
|
||||
tasks.push(
|
||||
load_ae_obj_li__event_location({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: current_event_id,
|
||||
enabled,
|
||||
hidden,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}).then((res) => (event_obj.event_location_obj_li = res))
|
||||
);
|
||||
}
|
||||
if (inc_session_li) {
|
||||
tasks.push(load_ae_obj_li__event_session({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: current_event_id,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}).then(res => event_obj.event_session_obj_li = res));
|
||||
tasks.push(
|
||||
load_ae_obj_li__event_session({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: current_event_id,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}).then((res) => (event_obj.event_session_obj_li = res))
|
||||
);
|
||||
}
|
||||
if (inc_template_li) {
|
||||
tasks.push(load_ae_obj_li__event_badge_template({
|
||||
api_cfg,
|
||||
event_id: current_event_id,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}).then(res => event_obj.event_badge_template_obj_li = res));
|
||||
tasks.push(
|
||||
load_ae_obj_li__event_badge_template({
|
||||
api_cfg,
|
||||
event_id: current_event_id,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}).then((res) => (event_obj.event_badge_template_obj_li = res))
|
||||
);
|
||||
}
|
||||
|
||||
if (tasks.length > 0) await Promise.all(tasks);
|
||||
@@ -239,20 +322,25 @@ export async function load_ae_obj_li__event({
|
||||
inc_presenter_li?: boolean;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
order_by_li?: Record<string, 'ASC' | 'DESC'> | Record<string, 'ASC' | 'DESC'>[];
|
||||
order_by_li?:
|
||||
| Record<string, 'ASC' | 'DESC'>
|
||||
| Record<string, 'ASC' | 'DESC'>[];
|
||||
params?: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Event[]> {
|
||||
|
||||
// Hierarchy Enforcement: Pulling presentations/presenters requires pulling sessions first
|
||||
if (inc_presenter_li || inc_presentation_li) inc_session_li = true;
|
||||
|
||||
// Check if offline
|
||||
if (typeof navigator !== 'undefined' && !navigator.onLine) {
|
||||
if (log_lvl) console.log('Browser is offline. Skipping API and attempting cache load.');
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
'Browser is offline. Skipping API and attempting cache load.'
|
||||
);
|
||||
ae_promises.load__event_obj_li = await db_events.event
|
||||
.where('account_id').equals(for_obj_id)
|
||||
.where('account_id')
|
||||
.equals(for_obj_id)
|
||||
.toArray();
|
||||
return ae_promises.load__event_obj_li || [];
|
||||
}
|
||||
@@ -265,10 +353,14 @@ export async function load_ae_obj_li__event({
|
||||
};
|
||||
|
||||
if (for_obj_id) {
|
||||
search_query.and.push({ field: `${for_obj_type}_id`, op: 'eq', value: for_obj_id });
|
||||
search_query.and.push({
|
||||
field: `${for_obj_type}_id`,
|
||||
op: 'eq',
|
||||
value: for_obj_id
|
||||
});
|
||||
}
|
||||
|
||||
promise = api.search_ae_obj_v3({
|
||||
promise = api.search_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event',
|
||||
headers: { 'x-account-id': for_obj_id },
|
||||
@@ -282,7 +374,7 @@ export async function load_ae_obj_li__event({
|
||||
log_lvl
|
||||
});
|
||||
} else {
|
||||
promise = api.get_ae_obj_li_v3({
|
||||
promise = api.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'event',
|
||||
for_obj_type,
|
||||
@@ -320,9 +412,11 @@ export async function load_ae_obj_li__event({
|
||||
} else {
|
||||
console.log('No results returned from API.');
|
||||
if (try_cache) {
|
||||
if (log_lvl) console.log('Attempting to load from local cache...');
|
||||
if (log_lvl)
|
||||
console.log('Attempting to load from local cache...');
|
||||
ae_promises.load__event_obj_li = await db_events.event
|
||||
.where('account_id').equals(for_obj_id)
|
||||
.where('account_id')
|
||||
.equals(for_obj_id)
|
||||
.toArray();
|
||||
} else {
|
||||
ae_promises.load__event_obj_li = [];
|
||||
@@ -331,9 +425,13 @@ export async function load_ae_obj_li__event({
|
||||
} catch (error: any) {
|
||||
console.log('API request failed.', error);
|
||||
if (try_cache) {
|
||||
if (log_lvl) console.log('Attempting to load from local cache after error...');
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
'Attempting to load from local cache after error...'
|
||||
);
|
||||
ae_promises.load__event_obj_li = await db_events.event
|
||||
.where('account_id').equals(for_obj_id)
|
||||
.where('account_id')
|
||||
.equals(for_obj_id)
|
||||
.toArray();
|
||||
} else {
|
||||
ae_promises.load__event_obj_li = [];
|
||||
@@ -341,18 +439,20 @@ export async function load_ae_obj_li__event({
|
||||
}
|
||||
|
||||
if (inc_session_li && ae_promises.load__event_obj_li) {
|
||||
const session_tasks = ae_promises.load__event_obj_li.map((event_obj: any) => {
|
||||
const current_event_id = event_obj.id || event_obj.event_id;
|
||||
return load_ae_obj_li__event_session({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: current_event_id,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}).then((res) => (event_obj.event_session_obj_li = res));
|
||||
});
|
||||
const session_tasks = ae_promises.load__event_obj_li.map(
|
||||
(event_obj: any) => {
|
||||
const current_event_id = event_obj.id || event_obj.event_id;
|
||||
return load_ae_obj_li__event_session({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: current_event_id,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}).then((res) => (event_obj.event_session_obj_li = res));
|
||||
}
|
||||
);
|
||||
await Promise.all(session_tasks);
|
||||
}
|
||||
|
||||
@@ -375,7 +475,7 @@ export async function create_ae_obj__event({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Event | null> {
|
||||
const result = await api.create_ae_obj_v3({
|
||||
const result = await api.create_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event',
|
||||
fields: {
|
||||
@@ -424,7 +524,7 @@ export async function delete_ae_obj_id__event({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
const result = await api.delete_ae_obj_v3({
|
||||
const result = await api.delete_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event',
|
||||
obj_id: event_id,
|
||||
@@ -456,7 +556,7 @@ export async function update_ae_obj__event({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Event | null> {
|
||||
const result = await api.update_ae_obj_v3({
|
||||
const result = await api.update_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event',
|
||||
obj_id: event_id,
|
||||
@@ -490,10 +590,10 @@ export async function update_ae_obj__event({
|
||||
// Updated 2026-01-21
|
||||
/**
|
||||
* Unified Search for Events (V3 API)
|
||||
*
|
||||
*
|
||||
* STRATEGY: Hybrid Search/Filter
|
||||
* 1. Server-side (V3 Search): Used for text search (qry_str) to reduce payload.
|
||||
* 2. Client-side (Filter Layer): Handles all other filters (Type, Location, Person)
|
||||
* 2. Client-side (Filter Layer): Handles all other filters (Type, Location, Person)
|
||||
* to ensure correct inclusive OR logic and stable ID matching.
|
||||
*/
|
||||
export async function search__event({
|
||||
@@ -544,21 +644,29 @@ export async function search__event({
|
||||
};
|
||||
const params: key_val = {};
|
||||
|
||||
search_query.and.push({ field: 'default_qry_str', op: 'like', value: `%${qry_str.trim()}%` });
|
||||
params['lk_qry'] = { 'default_qry_str': qry_str.trim() };
|
||||
search_query.and.push({
|
||||
field: 'default_qry_str',
|
||||
op: 'like',
|
||||
value: `%${qry_str.trim()}%`
|
||||
});
|
||||
params['lk_qry'] = { default_qry_str: qry_str.trim() };
|
||||
|
||||
if (for_obj_id) {
|
||||
// V3 Standard: Use random string ID for body filters.
|
||||
// V3 Standard: Use random string ID for body filters.
|
||||
// The API resolves this to the integer column automatically.
|
||||
search_query.and.push({ field: `${for_obj_type}_id_random`, op: 'eq', value: for_obj_id });
|
||||
search_query.and.push({
|
||||
field: `${for_obj_type}_id_random`,
|
||||
op: 'eq',
|
||||
value: for_obj_id
|
||||
});
|
||||
}
|
||||
|
||||
// NOTE: We do NOT push 'physical' and 'virtual' to the server-side query here.
|
||||
// The V3 Search API uses AND logic for the body, which would exclude
|
||||
// The V3 Search API uses AND logic for the body, which would exclude
|
||||
// meetings that are only physical or only virtual if both filters are active.
|
||||
// We handle this in the Client-side Filter Layer below for correct OR logic.
|
||||
|
||||
result_li = await api.search_ae_obj_v3({
|
||||
result_li = await api.search_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event',
|
||||
headers: { 'x-account-id': for_obj_id },
|
||||
@@ -574,7 +682,7 @@ export async function search__event({
|
||||
});
|
||||
} else {
|
||||
// Option B: List All
|
||||
result_li = await api.get_ae_obj_li_v3({
|
||||
result_li = await api.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'event',
|
||||
for_obj_type,
|
||||
@@ -594,7 +702,11 @@ export async function search__event({
|
||||
let valid_result_li: ae_Event[] = [];
|
||||
if (Array.isArray(result_li)) {
|
||||
valid_result_li = result_li;
|
||||
} else if (result_li && typeof result_li === 'object' && Array.isArray((result_li as any).data)) {
|
||||
} else if (
|
||||
result_li &&
|
||||
typeof result_li === 'object' &&
|
||||
Array.isArray((result_li as any).data)
|
||||
) {
|
||||
valid_result_li = (result_li as any).data;
|
||||
} else {
|
||||
return [];
|
||||
@@ -642,13 +754,12 @@ export async function search__event({
|
||||
|
||||
// Handle person ID filter
|
||||
if (qry_person_id) {
|
||||
const match = (
|
||||
const match =
|
||||
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
|
||||
);
|
||||
ev.poc_event_person_id_random === qry_person_id;
|
||||
if (!match) return false;
|
||||
}
|
||||
|
||||
@@ -656,7 +767,9 @@ export async function search__event({
|
||||
});
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`Filter results (Hybrid): Input=${processed_obj_li.length}, Output=${filtered_obj_li.length}`);
|
||||
console.log(
|
||||
`Filter results (Hybrid): Input=${processed_obj_li.length}, Output=${filtered_obj_li.length}`
|
||||
);
|
||||
}
|
||||
|
||||
return filtered_obj_li.slice(0, limit);
|
||||
@@ -664,139 +777,6 @@ export async function search__event({
|
||||
|
||||
export const qry_ae_obj_li__event = search__event;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Specialized search function for IDAA module using legacy V2 endpoints.
|
||||
* This is isolated to prevent V3 migration bugs from affecting Recovery Meetings.
|
||||
*/
|
||||
// Updated 2026-01-20
|
||||
export async function qry_ae_obj_li__event_v2({
|
||||
api_cfg,
|
||||
for_obj_type = 'account',
|
||||
for_obj_id,
|
||||
qry_str,
|
||||
qry_person_id = null,
|
||||
qry_conference = null,
|
||||
qry_physical = null,
|
||||
qry_virtual = null,
|
||||
qry_type = null,
|
||||
enabled = 'enabled',
|
||||
hidden = 'not_hidden',
|
||||
view = 'default',
|
||||
limit = 99,
|
||||
offset = 0,
|
||||
order_by_li = { start_datetime: 'DESC' } as const,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
for_obj_type?: string;
|
||||
for_obj_id: string;
|
||||
qry_str?: string;
|
||||
qry_person_id?: string | null;
|
||||
qry_conference?: boolean | null;
|
||||
qry_physical?: boolean | null;
|
||||
qry_virtual?: boolean | null;
|
||||
qry_type?: string | null;
|
||||
enabled?: 'enabled' | 'all' | 'not_enabled';
|
||||
hidden?: 'hidden' | 'all' | 'not_hidden';
|
||||
view?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
order_by_li?: Record<string, 'ASC' | 'DESC'>;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) console.log('*** qry_ae_obj_li__event_v2() ***');
|
||||
|
||||
const params_json: any = { qry: { and: [] } };
|
||||
|
||||
if (qry_str) {
|
||||
// Use default_qry_str for searching as requested
|
||||
params_json.qry.and.push({ field: 'default_qry_str', op: 'like', value: `%${qry_str}%` });
|
||||
}
|
||||
|
||||
const result_li = await get_ae_obj_li_for_obj_id_crud_v2({
|
||||
api_cfg,
|
||||
obj_type: 'event',
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
params_json,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (!result_li) return [];
|
||||
|
||||
const processed_obj_li = await process_ae_obj__event_props({
|
||||
obj_li: result_li,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'event',
|
||||
obj_li: processed_obj_li,
|
||||
properties_to_save: properties_to_save,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
// Client-side Filter Layer
|
||||
const filtered_obj_li = processed_obj_li.filter((ev: any) => {
|
||||
// Handle conference filter
|
||||
if (qry_conference != null) {
|
||||
const ev_conf = ev.conference === true || ev.conference === 1 || ev.conference === '1';
|
||||
if (ev_conf !== !!qry_conference) return false;
|
||||
}
|
||||
|
||||
// Location Filtering (Inclusive OR logic)
|
||||
// If either filter is explicitly true, we restrict results.
|
||||
// If both are false or null, we show everything.
|
||||
if (qry_physical === true || qry_virtual === true) {
|
||||
const ev_physical = ev.physical === true || ev.physical === 1 || ev.physical === '1';
|
||||
const ev_virtual = ev.virtual === true || ev.virtual === 1 || ev.virtual === '1';
|
||||
|
||||
let match = false;
|
||||
if (qry_physical === true && ev_physical) match = true;
|
||||
if (qry_virtual === true && ev_virtual) match = true;
|
||||
|
||||
if (!match) return false;
|
||||
}
|
||||
|
||||
// Handle type filter (skip if null, undefined, 'all', or empty string)
|
||||
if (qry_type != null && qry_type !== 'all' && qry_type !== '') {
|
||||
if (ev.type !== qry_type) return false;
|
||||
}
|
||||
|
||||
// Handle person ID filter
|
||||
if (qry_person_id) {
|
||||
const match = (
|
||||
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
|
||||
);
|
||||
if (!match) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`Filter results (V2): Input=${processed_obj_li.length}, Output=${filtered_obj_li.length}`);
|
||||
}
|
||||
|
||||
return filtered_obj_li.slice(0, limit);
|
||||
}
|
||||
|
||||
// Updated 2026-03-10
|
||||
export const properties_to_save = [
|
||||
'id',
|
||||
@@ -913,14 +893,21 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
const group = processed_obj.group ?? '0';
|
||||
const priority = processed_obj.priority ? 1 : 0;
|
||||
const sort = processed_obj.sort ?? '0';
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on ?? new Date(0).toISOString();
|
||||
const updated =
|
||||
processed_obj.updated_on ??
|
||||
processed_obj.created_on ??
|
||||
new Date(0).toISOString();
|
||||
const name = processed_obj.name ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
@@ -982,7 +969,8 @@ export function sync_config__event_pres_mgmt({
|
||||
pres_mgmt_cfg_remote?.label__session_poc_name_short ?? 'POC';
|
||||
pres_mgmt_cfg_local.label__session_poc_name =
|
||||
pres_mgmt_cfg_remote?.label__session_poc_name ?? 'Point of Contact';
|
||||
pres_mgmt_cfg_local.hide__session_poc = pres_mgmt_cfg_remote?.hide__session_poc ?? false;
|
||||
pres_mgmt_cfg_local.hide__session_poc =
|
||||
pres_mgmt_cfg_remote?.hide__session_poc ?? false;
|
||||
pres_mgmt_cfg_local.require__presenter_agree =
|
||||
pres_mgmt_cfg_remote?.require__presenter_agree ?? false;
|
||||
pres_mgmt_cfg_local.require__session_agree =
|
||||
@@ -1007,27 +995,31 @@ export function sync_config__event_pres_mgmt({
|
||||
pres_mgmt_cfg_local.hide__presentation_datetime =
|
||||
pres_mgmt_cfg_remote?.hide__presentation_datetime ?? false;
|
||||
pres_mgmt_cfg_local.show_content__presentation_description =
|
||||
pres_mgmt_cfg_remote?.show_content__presentation_description ?? false;
|
||||
pres_mgmt_cfg_remote?.show_content__presentation_description ??
|
||||
false;
|
||||
pres_mgmt_cfg_local.hide__presenter_code =
|
||||
pres_mgmt_cfg_remote?.hide__presenter_code ?? false;
|
||||
pres_mgmt_cfg_local.hide__presenter_biography =
|
||||
pres_mgmt_cfg_remote?.hide__presenter_biography ?? false;
|
||||
pres_mgmt_cfg_local.hide__session_code = pres_mgmt_cfg_remote?.hide__session_code ?? false;
|
||||
pres_mgmt_cfg_local.hide__session_code =
|
||||
pres_mgmt_cfg_remote?.hide__session_code ?? false;
|
||||
pres_mgmt_cfg_local.hide__session_description =
|
||||
pres_mgmt_cfg_remote?.hide__session_description ?? false;
|
||||
pres_mgmt_cfg_local.hide__session_location =
|
||||
pres_mgmt_cfg_remote?.hide__session_location ?? false;
|
||||
pres_mgmt_cfg_local.hide__session_msg = pres_mgmt_cfg_remote?.hide__session_msg ?? false;
|
||||
pres_mgmt_cfg_local.hide__session_msg =
|
||||
pres_mgmt_cfg_remote?.hide__session_msg ?? false;
|
||||
pres_mgmt_cfg_local.hide__session_poc_profile =
|
||||
pres_mgmt_cfg_remote?.hide__session_poc_profile ?? false;
|
||||
pres_mgmt_cfg_local.hide__session_poc_biography =
|
||||
pres_mgmt_cfg_remote?.hide__session_poc_biography ?? false;
|
||||
pres_mgmt_cfg_local.hide__session_poc_profile_pic =
|
||||
pres_mgmt_cfg_remote?.hide__session_poc_profile_pic ?? false;
|
||||
pres_mgmt_cfg_local.hide_launcher_link = pres_mgmt_cfg_remote?.hide_launcher_link ?? false;
|
||||
pres_mgmt_cfg_local.hide_launcher_link =
|
||||
pres_mgmt_cfg_remote?.hide_launcher_link ?? false;
|
||||
pres_mgmt_cfg_local.hide_launcher_link_legacy =
|
||||
pres_mgmt_cfg_remote?.hide_launcher_link_legacy ?? false;
|
||||
}
|
||||
|
||||
return pres_mgmt_cfg_local;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,31 +3,45 @@ import { describe, it, expect, vi } from 'vitest';
|
||||
// Use hoist-safe factories for vi.mock
|
||||
vi.mock('$lib/api/api', () => ({
|
||||
api: {
|
||||
create_nested_obj_v3: vi.fn(),
|
||||
update_nested_obj_v3: vi.fn(),
|
||||
delete_nested_ae_obj_v3: vi.fn(),
|
||||
search_ae_obj_v3: vi.fn()
|
||||
create_nested_obj: vi.fn(),
|
||||
update_nested_obj: vi.fn(),
|
||||
delete_nested_ae_obj: vi.fn(),
|
||||
search_ae_obj: vi.fn()
|
||||
}
|
||||
}));
|
||||
vi.mock('$lib/ae_core/core__idb_dexie', () => ({ db_save_ae_obj_li__ae_obj: vi.fn() }));
|
||||
vi.mock('$lib/ae_events/db_events', () => ({ db_events: { badge: { get: vi.fn(), delete: vi.fn() } } }));
|
||||
vi.mock('$lib/ae_events/ae_events__event_badge_template', () => ({ load_ae_obj_id__event_badge_template: vi.fn() }));
|
||||
vi.mock('$lib/ae_core/core__idb_dexie', () => ({
|
||||
db_save_ae_obj_li__ae_obj: vi.fn()
|
||||
}));
|
||||
vi.mock('$lib/ae_events/db_events', () => ({
|
||||
db_events: { badge: { get: vi.fn(), delete: vi.fn() } }
|
||||
}));
|
||||
vi.mock('$lib/ae_events/ae_events__event_badge_template', () => ({
|
||||
load_ae_obj_id__event_badge_template: vi.fn()
|
||||
}));
|
||||
|
||||
import { create_ae_obj__event_badge } from './ae_events__event_badge';
|
||||
|
||||
describe('create_ae_obj__event_badge', () => {
|
||||
it('calls api.create_nested_obj_v3 with the correct params and returns the result', async () => {
|
||||
it('calls api.create_nested_obj with the correct params and returns the result', async () => {
|
||||
const mocked = await import('$lib/api/api');
|
||||
const mockCreateNested = mocked.api.create_nested_obj_v3 as any;
|
||||
const mockCreateNested = mocked.api.create_nested_obj as any;
|
||||
|
||||
const fakeResult = { event_badge_id: 'eb123', full_name: 'Test User' };
|
||||
mockCreateNested.mockResolvedValue(fakeResult);
|
||||
|
||||
const api_cfg = { base_url: 'http://localhost', headers: {} };
|
||||
const event_id = 'evt1';
|
||||
const data_kv = { full_name_override: 'Test User', email: 't@example.com' };
|
||||
const data_kv = {
|
||||
full_name_override: 'Test User',
|
||||
email: 't@example.com'
|
||||
};
|
||||
|
||||
const result = await create_ae_obj__event_badge({ api_cfg, event_id, data_kv, try_cache: false });
|
||||
const result = await create_ae_obj__event_badge({
|
||||
api_cfg,
|
||||
event_id,
|
||||
data_kv,
|
||||
try_cache: false
|
||||
});
|
||||
|
||||
expect(mockCreateNested).toHaveBeenCalled();
|
||||
expect(mockCreateNested).toHaveBeenCalledWith({
|
||||
@@ -45,15 +59,22 @@ describe('create_ae_obj__event_badge', () => {
|
||||
});
|
||||
|
||||
describe('update_ae_obj__event_badge', () => {
|
||||
it('calls api.update_nested_obj_v3 with correct params and returns result', async () => {
|
||||
it('calls api.update_nested_obj with correct params and returns result', async () => {
|
||||
const mocked = await import('$lib/api/api');
|
||||
const mockUpdate = mocked.api.update_nested_obj_v3 as any;
|
||||
const mockUpdate = mocked.api.update_nested_obj as any;
|
||||
const fakeResult = { event_badge_id: 'eb999', full_name: 'Updated' };
|
||||
mockUpdate.mockResolvedValue(fakeResult);
|
||||
|
||||
const { update_ae_obj__event_badge } = await import('./ae_events__event_badge');
|
||||
const { update_ae_obj__event_badge } =
|
||||
await import('./ae_events__event_badge');
|
||||
const api_cfg = { base_url: 'http://localhost', headers: {} };
|
||||
const res = await update_ae_obj__event_badge({ api_cfg, event_id: 'evt1', event_badge_id: 'eb999', data_kv: { full_name_override: 'Updated' }, try_cache: false });
|
||||
const res = await update_ae_obj__event_badge({
|
||||
api_cfg,
|
||||
event_id: 'evt1',
|
||||
event_badge_id: 'eb999',
|
||||
data_kv: { full_name_override: 'Updated' },
|
||||
try_cache: false
|
||||
});
|
||||
|
||||
expect(mockUpdate).toHaveBeenCalled();
|
||||
expect(mockUpdate).toHaveBeenCalledWith({
|
||||
@@ -71,18 +92,24 @@ describe('update_ae_obj__event_badge', () => {
|
||||
});
|
||||
|
||||
describe('delete_ae_obj_id__event_badge', () => {
|
||||
it('calls api.delete_nested_ae_obj_v3 and deletes from local DB when try_cache true', async () => {
|
||||
it('calls api.delete_nested_ae_obj and deletes from local DB when try_cache true', async () => {
|
||||
const mocked = await import('$lib/api/api');
|
||||
const mockDelete = mocked.api.delete_nested_ae_obj_v3 as any;
|
||||
const mockDelete = mocked.api.delete_nested_ae_obj as any;
|
||||
mockDelete.mockResolvedValue({ success: true });
|
||||
|
||||
const db = await import('$lib/ae_events/db_events');
|
||||
const dbDelete = db.db_events.badge.delete as any;
|
||||
|
||||
const { delete_ae_obj_id__event_badge } = await import('./ae_events__event_badge');
|
||||
const { delete_ae_obj_id__event_badge } =
|
||||
await import('./ae_events__event_badge');
|
||||
|
||||
const api_cfg = { base_url: 'http://localhost', headers: {} };
|
||||
const res = await delete_ae_obj_id__event_badge({ api_cfg, event_id: 'evt1', event_badge_id: 'ebDel', try_cache: true });
|
||||
const res = await delete_ae_obj_id__event_badge({
|
||||
api_cfg,
|
||||
event_id: 'evt1',
|
||||
event_badge_id: 'ebDel',
|
||||
try_cache: true
|
||||
});
|
||||
|
||||
expect(mockDelete).toHaveBeenCalled();
|
||||
expect(dbDelete).toHaveBeenCalledWith('ebDel');
|
||||
@@ -91,15 +118,21 @@ describe('delete_ae_obj_id__event_badge', () => {
|
||||
});
|
||||
|
||||
describe('search__event_badge', () => {
|
||||
it('calls api.search_ae_obj_v3 and returns list (handles data envelope)', async () => {
|
||||
it('calls api.search_ae_obj and returns list (handles data envelope)', async () => {
|
||||
const mocked = await import('$lib/api/api');
|
||||
const mockSearch = mocked.api.search_ae_obj_v3 as any;
|
||||
const mockSearch = mocked.api.search_ae_obj as any;
|
||||
const fakeList = [{ event_badge_id: 'eb1' }, { event_badge_id: 'eb2' }];
|
||||
mockSearch.mockResolvedValue({ data: fakeList });
|
||||
|
||||
const { search__event_badge } = await import('./ae_events__event_badge');
|
||||
const { search__event_badge } =
|
||||
await import('./ae_events__event_badge');
|
||||
const api_cfg = { base_url: 'http://localhost', headers: {} };
|
||||
const res = await search__event_badge({ api_cfg, event_id: 'evt1', fulltext_search_qry_str: 'Test', try_cache: false });
|
||||
const res = await search__event_badge({
|
||||
api_cfg,
|
||||
event_id: 'evt1',
|
||||
fulltext_search_qry_str: 'Test',
|
||||
try_cache: false
|
||||
});
|
||||
|
||||
expect(mockSearch).toHaveBeenCalled();
|
||||
expect(Array.isArray(res)).toBe(true);
|
||||
|
||||
@@ -35,29 +35,36 @@ export async function load_ae_obj_id__event_badge({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventBadge | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_id__event_badge() *** event_badge_id=${event_badge_id}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_id__event_badge() *** event_badge_id=${event_badge_id}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
ae_promises.load__event_badge_obj = await api
|
||||
.get_ae_obj_v3({
|
||||
api_cfg,
|
||||
obj_type: 'event_badge',
|
||||
obj_id: event_badge_id,
|
||||
view,
|
||||
log_lvl
|
||||
});
|
||||
ae_promises.load__event_badge_obj = await api.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_badge',
|
||||
obj_id: event_badge_id,
|
||||
view,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (ae_promises.load__event_badge_obj) {
|
||||
if (try_cache) {
|
||||
// In theory we should be able to use the event_id found in the Badge load object. 2026-02-04
|
||||
// This keeps coming up as undefined: ae_promises.load__event_badge_obj.event_id
|
||||
if (log_lvl) console.log(`Saving to local cache... Event ID: ${event_id} or ${ae_promises.load__event_badge_obj.event_id}`);
|
||||
const processed_obj_li = await process_ae_obj__event_badge_props({
|
||||
obj_li: [ae_promises.load__event_badge_obj],
|
||||
event_id: event_id || ae_promises.load__event_badge_obj.event_id,
|
||||
log_lvl
|
||||
});
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`Saving to local cache... Event ID: ${event_id} or ${ae_promises.load__event_badge_obj.event_id}`
|
||||
);
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__event_badge_props({
|
||||
obj_li: [ae_promises.load__event_badge_obj],
|
||||
event_id:
|
||||
event_id ||
|
||||
ae_promises.load__event_badge_obj.event_id,
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'badge',
|
||||
@@ -71,15 +78,21 @@ export async function load_ae_obj_id__event_badge({
|
||||
} else {
|
||||
console.log('No results returned from API.');
|
||||
if (try_cache) {
|
||||
if (log_lvl) console.log('Attempting to load from local cache...');
|
||||
ae_promises.load__event_badge_obj = await db_events.badge.get(event_badge_id);
|
||||
if (log_lvl)
|
||||
console.log('Attempting to load from local cache...');
|
||||
ae_promises.load__event_badge_obj =
|
||||
await db_events.badge.get(event_badge_id);
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log('API request failed.', error);
|
||||
if (try_cache) {
|
||||
if (log_lvl) console.log('Attempting to load from local cache after error...');
|
||||
ae_promises.load__event_badge_obj = await db_events.badge.get(event_badge_id);
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
'Attempting to load from local cache after error...'
|
||||
);
|
||||
ae_promises.load__event_badge_obj =
|
||||
await db_events.badge.get(event_badge_id);
|
||||
} else {
|
||||
ae_promises.load__event_badge_obj = null;
|
||||
}
|
||||
@@ -87,14 +100,16 @@ export async function load_ae_obj_id__event_badge({
|
||||
|
||||
if (inc_template && ae_promises.load__event_badge_obj) {
|
||||
// Load the templates for the event badge
|
||||
const current_template_id = ae_promises.load__event_badge_obj.event_badge_template_id;
|
||||
const current_template_id =
|
||||
ae_promises.load__event_badge_obj.event_badge_template_id;
|
||||
if (current_template_id) {
|
||||
ae_promises.load__event_badge_obj.event_badge_template = await load_ae_obj_id__event_badge_template({
|
||||
api_cfg: api_cfg,
|
||||
event_badge_template_id: current_template_id,
|
||||
try_cache: try_cache,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
ae_promises.load__event_badge_obj.event_badge_template =
|
||||
await load_ae_obj_id__event_badge_template({
|
||||
api_cfg: api_cfg,
|
||||
event_badge_template_id: current_template_id,
|
||||
try_cache: try_cache,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +126,12 @@ export async function load_ae_obj_li__event_badge({
|
||||
view = 'default',
|
||||
limit = 99,
|
||||
offset = 0,
|
||||
order_by_li = { priority: 'DESC', sort: 'DESC', updated_on: 'DESC', created_on: 'DESC' },
|
||||
order_by_li = {
|
||||
priority: 'DESC',
|
||||
sort: 'DESC',
|
||||
updated_on: 'DESC',
|
||||
created_on: 'DESC'
|
||||
},
|
||||
params = {},
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
@@ -130,32 +150,34 @@ export async function load_ae_obj_li__event_badge({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventBadge[]> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_li__event_badge() *** event_id=${event_id}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_li__event_badge() *** event_id=${event_id}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
ae_promises.load__event_badge_obj_li = await api
|
||||
.get_ae_obj_li_v3({
|
||||
api_cfg,
|
||||
obj_type: 'event_badge',
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
enabled: enabled,
|
||||
hidden: hidden,
|
||||
view: view,
|
||||
order_by_li: order_by_li,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
ae_promises.load__event_badge_obj_li = await api.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'event_badge',
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
enabled: enabled,
|
||||
hidden: hidden,
|
||||
view: view,
|
||||
order_by_li: order_by_li,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
|
||||
if (ae_promises.load__event_badge_obj_li) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__event_badge_props({
|
||||
obj_li: ae_promises.load__event_badge_obj_li,
|
||||
event_id,
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__event_badge_props({
|
||||
obj_li: ae_promises.load__event_badge_obj_li,
|
||||
event_id,
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'badge',
|
||||
@@ -169,9 +191,11 @@ export async function load_ae_obj_li__event_badge({
|
||||
} else {
|
||||
console.log('No results returned from API.');
|
||||
if (try_cache) {
|
||||
if (log_lvl) console.log('Attempting to load from local cache...');
|
||||
if (log_lvl)
|
||||
console.log('Attempting to load from local cache...');
|
||||
ae_promises.load__event_badge_obj_li = await db_events.badge
|
||||
.where('event_id').equals(event_id)
|
||||
.where('event_id')
|
||||
.equals(event_id)
|
||||
.toArray();
|
||||
} else {
|
||||
ae_promises.load__event_badge_obj_li = [];
|
||||
@@ -180,9 +204,13 @@ export async function load_ae_obj_li__event_badge({
|
||||
} catch (error: any) {
|
||||
console.log('API request failed.', error);
|
||||
if (try_cache) {
|
||||
if (log_lvl) console.log('Attempting to load from local cache after error...');
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
'Attempting to load from local cache after error...'
|
||||
);
|
||||
ae_promises.load__event_badge_obj_li = await db_events.badge
|
||||
.where('event_id').equals(event_id)
|
||||
.where('event_id')
|
||||
.equals(event_id)
|
||||
.toArray();
|
||||
} else {
|
||||
ae_promises.load__event_badge_obj_li = [];
|
||||
@@ -193,12 +221,13 @@ export async function load_ae_obj_li__event_badge({
|
||||
for (const badge_obj of ae_promises.load__event_badge_obj_li) {
|
||||
const current_template_id = badge_obj.event_badge_template_id;
|
||||
if (current_template_id) {
|
||||
badge_obj.event_badge_template = await load_ae_obj_id__event_badge_template({
|
||||
api_cfg: api_cfg,
|
||||
event_badge_template_id: current_template_id,
|
||||
try_cache: try_cache,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
badge_obj.event_badge_template =
|
||||
await load_ae_obj_id__event_badge_template({
|
||||
api_cfg: api_cfg,
|
||||
event_badge_template_id: current_template_id,
|
||||
try_cache: try_cache,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,10 +252,12 @@ export async function create_ae_obj__event_badge({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventBadge | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** create_ae_obj__event_badge() *** event_id=${event_id}`);
|
||||
console.log(
|
||||
`*** create_ae_obj__event_badge() *** event_id=${event_id}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await api.create_nested_obj_v3({
|
||||
const result = await api.create_nested_obj({
|
||||
api_cfg,
|
||||
parent_type: 'event',
|
||||
parent_id: event_id,
|
||||
@@ -275,10 +306,12 @@ export async function delete_ae_obj_id__event_badge({
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** delete_ae_obj_id__event_badge() *** event_badge_id=${event_badge_id}`);
|
||||
console.log(
|
||||
`*** delete_ae_obj_id__event_badge() *** event_badge_id=${event_badge_id}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await api.delete_nested_ae_obj_v3({
|
||||
const result = await api.delete_nested_ae_obj({
|
||||
api_cfg,
|
||||
parent_type: 'event',
|
||||
parent_id: event_id,
|
||||
@@ -315,10 +348,12 @@ export async function update_ae_obj__event_badge({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventBadge | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** update_ae_obj__event_badge() *** event_badge_id=${event_badge_id}`);
|
||||
console.log(
|
||||
`*** update_ae_obj__event_badge() *** event_badge_id=${event_badge_id}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await api.update_nested_obj_v3({
|
||||
const result = await api.update_nested_obj({
|
||||
api_cfg,
|
||||
parent_type: 'event',
|
||||
parent_id: event_id,
|
||||
@@ -391,7 +426,9 @@ export async function search__event_badge({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventBadge[] | false> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** search__event_badge() *** event_id=${event_id} ft=${fulltext_search_qry_str}`);
|
||||
console.log(
|
||||
`*** search__event_badge() *** event_id=${event_id} ft=${fulltext_search_qry_str}`
|
||||
);
|
||||
}
|
||||
|
||||
const search_query: any = {
|
||||
@@ -406,15 +443,23 @@ export async function search__event_badge({
|
||||
// Multi-word support: Split query by spaces and search for all words (AND logic)
|
||||
if (fulltext_search_qry_str && fulltext_search_qry_str.trim().length > 0) {
|
||||
const qry = fulltext_search_qry_str.trim();
|
||||
const words = qry.split(/\s+/).filter(w => w.length > 0);
|
||||
const words = qry.split(/\s+/).filter((w) => w.length > 0);
|
||||
|
||||
if (words.length === 1) {
|
||||
// Single word: use simple LIKE
|
||||
search_query.and.push({ field: 'default_qry_str', op: 'like', value: `%${words[0]}%` });
|
||||
search_query.and.push({
|
||||
field: 'default_qry_str',
|
||||
op: 'like',
|
||||
value: `%${words[0]}%`
|
||||
});
|
||||
} else if (words.length > 1) {
|
||||
// Multiple words: each word must match (AND logic)
|
||||
for (const word of words) {
|
||||
search_query.and.push({ field: 'default_qry_str', op: 'like', value: `%${word}%` });
|
||||
search_query.and.push({
|
||||
field: 'default_qry_str',
|
||||
op: 'like',
|
||||
value: `%${word}%`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,7 +469,11 @@ export async function search__event_badge({
|
||||
|
||||
if (affiliations_qry_str && affiliations_qry_str.trim().length > 0) {
|
||||
const qry = affiliations_qry_str.trim();
|
||||
search_query.and.push({ field: 'affiliations', op: 'like', value: `%${qry}%` });
|
||||
search_query.and.push({
|
||||
field: 'affiliations',
|
||||
op: 'like',
|
||||
value: `%${qry}%`
|
||||
});
|
||||
params['lk_qry'] = params['lk_qry'] || {};
|
||||
params['lk_qry']['affiliations'] = qry;
|
||||
}
|
||||
@@ -435,11 +484,19 @@ export async function search__event_badge({
|
||||
}
|
||||
|
||||
if (external_event_id) {
|
||||
search_query.and.push({ field: 'external_event_id', op: 'eq', value: external_event_id });
|
||||
search_query.and.push({
|
||||
field: 'external_event_id',
|
||||
op: 'eq',
|
||||
value: external_event_id
|
||||
});
|
||||
}
|
||||
|
||||
if (type_code) {
|
||||
search_query.and.push({ field: 'badge_type_code', op: 'eq', value: type_code });
|
||||
search_query.and.push({
|
||||
field: 'badge_type_code',
|
||||
op: 'eq',
|
||||
value: type_code
|
||||
});
|
||||
}
|
||||
|
||||
if (printed_status === 'printed') {
|
||||
@@ -448,14 +505,18 @@ export async function search__event_badge({
|
||||
search_query.and.push({ field: 'print_count', op: 'eq', value: 0 });
|
||||
}
|
||||
|
||||
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: true });
|
||||
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: false });
|
||||
if (enabled === 'enabled')
|
||||
search_query.and.push({ field: 'enable', op: 'eq', value: true });
|
||||
else if (enabled === 'not_enabled')
|
||||
search_query.and.push({ field: 'enable', op: 'eq', value: false });
|
||||
|
||||
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: true });
|
||||
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: false });
|
||||
if (hidden === 'hidden')
|
||||
search_query.and.push({ field: 'hide', op: 'eq', value: true });
|
||||
else if (hidden === 'not_hidden')
|
||||
search_query.and.push({ field: 'hide', op: 'eq', value: false });
|
||||
|
||||
ae_promises.search__event_badge_obj_li = await api
|
||||
.search_ae_obj_v3({
|
||||
.search_ae_obj({
|
||||
api_cfg: api_cfg,
|
||||
obj_type: 'event_badge',
|
||||
search_query,
|
||||
@@ -473,17 +534,21 @@ export async function search__event_badge({
|
||||
let result_li: ae_EventBadge[] = [];
|
||||
if (Array.isArray(badge_obj_li_get_result)) {
|
||||
result_li = badge_obj_li_get_result;
|
||||
} else if (badge_obj_li_get_result?.data && Array.isArray(badge_obj_li_get_result.data)) {
|
||||
} else if (
|
||||
badge_obj_li_get_result?.data &&
|
||||
Array.isArray(badge_obj_li_get_result.data)
|
||||
) {
|
||||
result_li = badge_obj_li_get_result.data;
|
||||
}
|
||||
|
||||
if (result_li.length > 0) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__event_badge_props({
|
||||
obj_li: result_li,
|
||||
event_id,
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__event_badge_props({
|
||||
obj_li: result_li,
|
||||
event_id,
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'badge',
|
||||
@@ -631,14 +696,21 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
const group = processed_obj.group ?? '0';
|
||||
const priority = processed_obj.priority ? 1 : 0;
|
||||
const sort = processed_obj.sort ?? '0';
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on ?? new Date(0).toISOString();
|
||||
const updated =
|
||||
processed_obj.updated_on ??
|
||||
processed_obj.created_on ??
|
||||
new Date(0).toISOString();
|
||||
const name = processed_obj.name ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
@@ -663,7 +735,9 @@ export async function process_ae_obj__event_badge_props({
|
||||
log_lvl,
|
||||
specific_processor: (obj) => {
|
||||
if (log_lvl) {
|
||||
console.log(`*** process_ae_obj__event_badge_props() *** event_id=${event_id}`);
|
||||
console.log(
|
||||
`*** process_ae_obj__event_badge_props() *** event_id=${event_id}`
|
||||
);
|
||||
}
|
||||
if (event_id) {
|
||||
if (!obj.event_id) obj.event_id = event_id;
|
||||
@@ -672,4 +746,4 @@ export async function process_ae_obj__event_badge_props({
|
||||
return obj;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,11 +87,15 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on;
|
||||
const name = processed_obj.name ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
@@ -129,11 +133,13 @@ export async function load_ae_obj_id__event_badge_template({
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_id__event_badge_template() *** [V3] id=${event_badge_template_id}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_id__event_badge_template() *** [V3] id=${event_badge_template_id}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
ae_promises.load__event_badge_template_obj = await api.get_ae_obj_v3({
|
||||
ae_promises.load__event_badge_template_obj = await api.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_badge_template',
|
||||
obj_id: event_badge_template_id,
|
||||
@@ -156,11 +162,13 @@ export async function load_ae_obj_id__event_badge_template({
|
||||
});
|
||||
}
|
||||
} else if (try_cache) {
|
||||
ae_promises.load__event_badge_template_obj = await db_events.badge_template.get(event_badge_template_id);
|
||||
ae_promises.load__event_badge_template_obj =
|
||||
await db_events.badge_template.get(event_badge_template_id);
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (try_cache) {
|
||||
ae_promises.load__event_badge_template_obj = await db_events.badge_template.get(event_badge_template_id);
|
||||
ae_promises.load__event_badge_template_obj =
|
||||
await db_events.badge_template.get(event_badge_template_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,19 +205,21 @@ export async function load_ae_obj_li__event_badge_template({
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
try {
|
||||
ae_promises.load__event_badge_template_obj_li = await api.get_ae_obj_li_v3({
|
||||
api_cfg,
|
||||
obj_type: 'event_badge_template',
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
log_lvl
|
||||
});
|
||||
ae_promises.load__event_badge_template_obj_li = await api.get_ae_obj_li(
|
||||
{
|
||||
api_cfg,
|
||||
obj_type: 'event_badge_template',
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
log_lvl
|
||||
}
|
||||
);
|
||||
|
||||
if (ae_promises.load__event_badge_template_obj_li) {
|
||||
if (try_cache) {
|
||||
@@ -226,15 +236,19 @@ export async function load_ae_obj_li__event_badge_template({
|
||||
});
|
||||
}
|
||||
} else if (try_cache) {
|
||||
ae_promises.load__event_badge_template_obj_li = await db_events.badge_template
|
||||
.where('event_id').equals(event_id)
|
||||
.toArray();
|
||||
ae_promises.load__event_badge_template_obj_li =
|
||||
await db_events.badge_template
|
||||
.where('event_id')
|
||||
.equals(event_id)
|
||||
.toArray();
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (try_cache) {
|
||||
ae_promises.load__event_badge_template_obj_li = await db_events.badge_template
|
||||
.where('event_id').equals(event_id)
|
||||
.toArray();
|
||||
ae_promises.load__event_badge_template_obj_li =
|
||||
await db_events.badge_template
|
||||
.where('event_id')
|
||||
.equals(event_id)
|
||||
.toArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,115 +257,115 @@ export async function load_ae_obj_li__event_badge_template({
|
||||
|
||||
// Updated 2026-01-20 to V3
|
||||
export async function create_ae_obj__event_badge_template({
|
||||
api_cfg,
|
||||
event_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
api_cfg,
|
||||
event_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
event_id: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_id: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
const result = await api.create_nested_obj_v3({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_badge_template',
|
||||
fields: { ...data_kv },
|
||||
log_lvl
|
||||
});
|
||||
const result = await api.create_nested_obj({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_badge_template',
|
||||
fields: { ...data_kv },
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (result && try_cache) {
|
||||
const processed_obj_li = await process_ae_badge_template_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'badge_template',
|
||||
obj_li: processed_obj_li,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return result;
|
||||
if (result && try_cache) {
|
||||
const processed_obj_li = await process_ae_badge_template_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'badge_template',
|
||||
obj_li: processed_obj_li,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Updated 2026-01-20 to V3
|
||||
export async function delete_ae_obj_id__event_badge_template({
|
||||
api_cfg,
|
||||
event_id,
|
||||
event_badge_template_id,
|
||||
method = 'delete',
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
api_cfg,
|
||||
event_id,
|
||||
event_badge_template_id,
|
||||
method = 'delete',
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
event_id: string;
|
||||
event_badge_template_id: string;
|
||||
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_id: string;
|
||||
event_badge_template_id: string;
|
||||
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
const result = await api.delete_nested_ae_obj_v3({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_badge_template',
|
||||
obj_id: event_badge_template_id,
|
||||
method,
|
||||
log_lvl
|
||||
});
|
||||
const result = await api.delete_nested_ae_obj({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_badge_template',
|
||||
obj_id: event_badge_template_id,
|
||||
method,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (try_cache) {
|
||||
await db_events.badge_template.delete(event_badge_template_id);
|
||||
}
|
||||
return result;
|
||||
if (try_cache) {
|
||||
await db_events.badge_template.delete(event_badge_template_id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Updated 2026-01-20 to V3
|
||||
export async function update_ae_obj__event_badge_template({
|
||||
api_cfg,
|
||||
event_id,
|
||||
event_badge_template_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
api_cfg,
|
||||
event_id,
|
||||
event_badge_template_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
event_id: string;
|
||||
event_badge_template_id: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_id: string;
|
||||
event_badge_template_id: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
const result = await api.update_nested_obj_v3({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_badge_template',
|
||||
obj_id: event_badge_template_id,
|
||||
fields: data_kv,
|
||||
log_lvl
|
||||
});
|
||||
const result = await api.update_nested_obj({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_badge_template',
|
||||
obj_id: event_badge_template_id,
|
||||
fields: data_kv,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (result && try_cache) {
|
||||
const processed_obj_li = await process_ae_badge_template_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'badge_template',
|
||||
obj_li: processed_obj_li,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return result;
|
||||
if (result && try_cache) {
|
||||
const processed_obj_li = await process_ae_badge_template_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'badge_template',
|
||||
obj_li: processed_obj_li,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Updated 2026-01-20 to V3
|
||||
@@ -388,13 +402,17 @@ export async function search__event_badge_template({
|
||||
and: [{ field: 'event_id', op: 'eq', value: event_id }]
|
||||
};
|
||||
|
||||
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: true });
|
||||
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: false });
|
||||
if (enabled === 'enabled')
|
||||
search_query.and.push({ field: 'enable', op: 'eq', value: true });
|
||||
else if (enabled === 'not_enabled')
|
||||
search_query.and.push({ field: 'enable', op: 'eq', value: false });
|
||||
|
||||
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: true });
|
||||
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: false });
|
||||
if (hidden === 'hidden')
|
||||
search_query.and.push({ field: 'hide', op: 'eq', value: true });
|
||||
else if (hidden === 'not_hidden')
|
||||
search_query.and.push({ field: 'hide', op: 'eq', value: false });
|
||||
|
||||
const result_li = await api.search_ae_obj_v3({
|
||||
const result_li = await api.search_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_badge_template',
|
||||
search_query,
|
||||
@@ -418,4 +436,4 @@ export async function search__event_badge_template({
|
||||
});
|
||||
}
|
||||
return result_li || [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,11 +28,13 @@ export async function load_ae_obj_id__event_device({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventDevice | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_id__event_device() *** [V3] id=${event_device_id}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_id__event_device() *** [V3] id=${event_device_id}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await api.get_ae_obj_v3({
|
||||
const result = await api.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_device',
|
||||
obj_id: event_device_id,
|
||||
@@ -57,26 +59,30 @@ export async function load_ae_obj_id__event_device({
|
||||
});
|
||||
}
|
||||
} else if (try_cache) {
|
||||
ae_promises.load__event_device_obj = await db_events.device.get(event_device_id);
|
||||
ae_promises.load__event_device_obj =
|
||||
await db_events.device.get(event_device_id);
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log('V3 Request failed.', error);
|
||||
if (try_cache) {
|
||||
ae_promises.load__event_device_obj = await db_events.device.get(event_device_id);
|
||||
ae_promises.load__event_device_obj =
|
||||
await db_events.device.get(event_device_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ae_promises.load__event_device_obj) return null;
|
||||
|
||||
if (inc_location_id) {
|
||||
const current_location_id = ae_promises.load__event_device_obj.event_location_id;
|
||||
const current_location_id =
|
||||
ae_promises.load__event_device_obj.event_location_id;
|
||||
if (current_location_id) {
|
||||
ae_promises.load__event_device_obj.event_location_obj = await load_ae_obj_id__event_location({
|
||||
api_cfg,
|
||||
event_location_id: current_location_id,
|
||||
try_cache,
|
||||
log_lvl
|
||||
});
|
||||
ae_promises.load__event_device_obj.event_location_obj =
|
||||
await load_ae_obj_id__event_location({
|
||||
api_cfg,
|
||||
event_location_id: current_location_id,
|
||||
try_cache,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,11 +121,13 @@ export async function load_ae_obj_li__event_device({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventDevice[]> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_li__event_device() *** [V3] for=${for_obj_type}:${for_obj_id}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_li__event_device() *** [V3] for=${for_obj_type}:${for_obj_id}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const result_li = await api.get_ae_obj_li_v3({
|
||||
const result_li = await api.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'event_device',
|
||||
for_obj_type,
|
||||
@@ -150,14 +158,16 @@ export async function load_ae_obj_li__event_device({
|
||||
}
|
||||
} else if (try_cache) {
|
||||
ae_promises.load__event_device_obj_li = await db_events.device
|
||||
.where('event_id').equals(for_obj_id)
|
||||
.where('event_id')
|
||||
.equals(for_obj_id)
|
||||
.toArray();
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log('V3 List Request failed.', error);
|
||||
if (try_cache) {
|
||||
ae_promises.load__event_device_obj_li = await db_events.device
|
||||
.where('event_id').equals(for_obj_id)
|
||||
.where('event_id')
|
||||
.equals(for_obj_id)
|
||||
.toArray();
|
||||
}
|
||||
}
|
||||
@@ -165,12 +175,13 @@ export async function load_ae_obj_li__event_device({
|
||||
if (inc_location_id && ae_promises.load__event_device_obj_li) {
|
||||
for (const device of ae_promises.load__event_device_obj_li) {
|
||||
if (device.event_location_id) {
|
||||
device.event_location_obj = await load_ae_obj_id__event_location({
|
||||
api_cfg,
|
||||
event_location_id: device.event_location_id,
|
||||
try_cache,
|
||||
log_lvl
|
||||
});
|
||||
device.event_location_obj =
|
||||
await load_ae_obj_id__event_location({
|
||||
api_cfg,
|
||||
event_location_id: device.event_location_id,
|
||||
try_cache,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,155 +191,161 @@ export async function load_ae_obj_li__event_device({
|
||||
|
||||
// Updated 2026-01-20 to V3
|
||||
export async function create_ae_obj__event_device({
|
||||
api_cfg,
|
||||
event_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
api_cfg,
|
||||
event_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
event_id?: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_id?: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventDevice | null> {
|
||||
if (!event_id) event_id = get(slct).event_id;
|
||||
if (!event_id) {
|
||||
console.error('create_ae_obj__event_device: event_id is required');
|
||||
return null;
|
||||
}
|
||||
if (log_lvl) {
|
||||
console.log(`*** create_ae_obj__event_device() *** [V3] event_id=${event_id}`);
|
||||
}
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`*** create_ae_obj__event_device() *** [V3] event_id=${event_id}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await api.create_nested_obj_v3({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_device',
|
||||
fields: { ...data_kv },
|
||||
log_lvl
|
||||
});
|
||||
const result = await api.create_nested_obj({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_device',
|
||||
fields: { ...data_kv },
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_device_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj = processed[0];
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_device_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj = processed[0];
|
||||
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'device',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'device',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Updated 2026-01-20 to V3
|
||||
export async function delete_ae_obj_id__event_device({
|
||||
api_cfg,
|
||||
event_id,
|
||||
event_device_id,
|
||||
method = 'delete',
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
api_cfg,
|
||||
event_id,
|
||||
event_device_id,
|
||||
method = 'delete',
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
event_id?: string;
|
||||
event_device_id: string;
|
||||
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_id?: string;
|
||||
event_device_id: string;
|
||||
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (!event_id) event_id = get(slct).event_id;
|
||||
if (!event_id) {
|
||||
console.error('delete_ae_obj_id__event_device: event_id is required');
|
||||
return null;
|
||||
}
|
||||
if (log_lvl) {
|
||||
console.log(`*** delete_ae_obj_id__event_device() *** [V3] id=${event_device_id}`);
|
||||
}
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`*** delete_ae_obj_id__event_device() *** [V3] id=${event_device_id}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await api.delete_nested_ae_obj_v3({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_device',
|
||||
obj_id: event_device_id,
|
||||
method,
|
||||
log_lvl
|
||||
});
|
||||
const result = await api.delete_nested_ae_obj({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_device',
|
||||
obj_id: event_device_id,
|
||||
method,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (try_cache) {
|
||||
await db_events.device.delete(event_device_id);
|
||||
}
|
||||
if (try_cache) {
|
||||
await db_events.device.delete(event_device_id);
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Updated 2026-01-20 to V3
|
||||
export async function update_ae_obj__event_device({
|
||||
api_cfg,
|
||||
event_id,
|
||||
event_device_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
api_cfg,
|
||||
event_id,
|
||||
event_device_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
event_id?: string;
|
||||
event_device_id: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_id?: string;
|
||||
event_device_id: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventDevice | null> {
|
||||
if (!event_id) event_id = get(slct).event_id;
|
||||
if (!event_id) {
|
||||
console.error('update_ae_obj__event_device: event_id is required');
|
||||
return null;
|
||||
}
|
||||
if (log_lvl) {
|
||||
console.log(`*** update_ae_obj__event_device() *** [V3] id=${event_device_id}`);
|
||||
}
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`*** update_ae_obj__event_device() *** [V3] id=${event_device_id}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await api.update_nested_obj_v3({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_device',
|
||||
obj_id: event_device_id,
|
||||
fields: data_kv,
|
||||
log_lvl
|
||||
});
|
||||
const result = await api.update_nested_obj({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_device',
|
||||
obj_id: event_device_id,
|
||||
fields: data_kv,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_device_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj = processed[0];
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_device_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj = processed[0];
|
||||
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'device',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'device',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Updated 2026-01-20 to V3
|
||||
@@ -361,7 +378,9 @@ export async function search__event_device({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventDevice[]> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** search__event_device() *** [V3] event_id=${event_id} qry=${qry_str}`);
|
||||
console.log(
|
||||
`*** search__event_device() *** [V3] event_id=${event_id} qry=${qry_str}`
|
||||
);
|
||||
}
|
||||
|
||||
const search_query: any = {
|
||||
@@ -370,13 +389,17 @@ export async function search__event_device({
|
||||
};
|
||||
|
||||
// Logical filters
|
||||
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: true });
|
||||
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: false });
|
||||
if (enabled === 'enabled')
|
||||
search_query.and.push({ field: 'enable', op: 'eq', value: true });
|
||||
else if (enabled === 'not_enabled')
|
||||
search_query.and.push({ field: 'enable', op: 'eq', value: false });
|
||||
|
||||
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: true });
|
||||
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: false });
|
||||
if (hidden === 'hidden')
|
||||
search_query.and.push({ field: 'hide', op: 'eq', value: true });
|
||||
else if (hidden === 'not_hidden')
|
||||
search_query.and.push({ field: 'hide', op: 'eq', value: false });
|
||||
|
||||
const result_li = await api.search_ae_obj_v3({
|
||||
const result_li = await api.search_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_device',
|
||||
search_query,
|
||||
@@ -499,11 +522,15 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on;
|
||||
const name = processed_obj.name ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
@@ -524,7 +551,7 @@ export async function process_ae_obj__event_device_props({
|
||||
obj_type: 'event_device',
|
||||
log_lvl,
|
||||
specific_processor: (obj) => {
|
||||
// Note: V3 API returns proper ISO strings.
|
||||
// Note: V3 API returns proper ISO strings.
|
||||
// We no longer manually append 'Z' to avoid timezone corruption.
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ async function _refresh_file_id_background({
|
||||
}: any) {
|
||||
if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
|
||||
try {
|
||||
const result = await api.get_ae_obj_v3({
|
||||
const result = await api.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_file',
|
||||
obj_id: event_file_id,
|
||||
@@ -191,10 +191,12 @@ async function _refresh_file_li_background({
|
||||
if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
|
||||
try {
|
||||
if (log_lvl) {
|
||||
console.log(`📡 [DEBUG] _refresh_file_li_background: Fetching files for ${for_obj_type}:${for_obj_id}`);
|
||||
console.log(
|
||||
`📡 [DEBUG] _refresh_file_li_background: Fetching files for ${for_obj_type}:${for_obj_id}`
|
||||
);
|
||||
}
|
||||
|
||||
const result_li = await api.get_ae_obj_li_v3({
|
||||
const result_li = await api.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'event_file',
|
||||
for_obj_type,
|
||||
@@ -211,7 +213,10 @@ async function _refresh_file_li_background({
|
||||
|
||||
if (result_li) {
|
||||
if (log_lvl) {
|
||||
console.log(`📦 [DEBUG] Raw API results count:`, result_li.length);
|
||||
console.log(
|
||||
`📦 [DEBUG] Raw API results count:`,
|
||||
result_li.length
|
||||
);
|
||||
if (result_li.length > 0) {
|
||||
console.log(`🔍 [DEBUG] Sample Raw IDs:`, {
|
||||
id: result_li[0].id,
|
||||
@@ -253,7 +258,10 @@ async function _refresh_file_li_background({
|
||||
}
|
||||
|
||||
if (try_cache) {
|
||||
if (log_lvl) console.log(`💾 [DEBUG] Saving ${processed.length} records to ae_events_db.file`);
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`💾 [DEBUG] Saving ${processed.length} records to ae_events_db.file`
|
||||
);
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'file',
|
||||
@@ -265,10 +273,15 @@ async function _refresh_file_li_background({
|
||||
return processed;
|
||||
}
|
||||
} catch (e) {
|
||||
if (log_lvl) console.error(`❌ [DEBUG] Error in _refresh_file_li_background:`, e);
|
||||
if (log_lvl)
|
||||
console.error(
|
||||
`❌ [DEBUG] Error in _refresh_file_li_background:`,
|
||||
e
|
||||
);
|
||||
}
|
||||
return [];
|
||||
}export async function create_event_file_obj_from_hosted_file_async({
|
||||
}
|
||||
export async function create_event_file_obj_from_hosted_file_async({
|
||||
api_cfg,
|
||||
hosted_file_id,
|
||||
params = {},
|
||||
@@ -318,7 +331,7 @@ export async function delete_ae_obj_id__event_file({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
const result = await api.delete_ae_obj_v3({
|
||||
const result = await api.delete_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_file',
|
||||
obj_id: event_file_id,
|
||||
@@ -344,7 +357,7 @@ export async function update_ae_obj__event_file({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventFile | null> {
|
||||
const result = await api.update_ae_obj_v3({
|
||||
const result = await api.update_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_file',
|
||||
obj_id: event_file_id,
|
||||
@@ -427,7 +440,7 @@ export async function search__event_file({
|
||||
op: 'eq',
|
||||
value: qry_file_purpose
|
||||
});
|
||||
const result_li = await api.search_ae_obj_v3({
|
||||
const result_li = await api.search_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_file',
|
||||
search_query,
|
||||
@@ -459,7 +472,7 @@ export async function search__event_file({
|
||||
});
|
||||
}
|
||||
|
||||
return processed; // Return processed data with mapped fields
|
||||
return processed; // Return processed data with mapped fields
|
||||
}
|
||||
|
||||
return [];
|
||||
@@ -545,7 +558,11 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
if (key.endsWith('_random')) {
|
||||
const newKey = key.slice(0, -7);
|
||||
// ONLY overwrite if the random variant has a valid value
|
||||
if (processed_obj[key] !== null && processed_obj[key] !== undefined && processed_obj[key] !== '') {
|
||||
if (
|
||||
processed_obj[key] !== null &&
|
||||
processed_obj[key] !== undefined &&
|
||||
processed_obj[key] !== ''
|
||||
) {
|
||||
(processed_obj as any)[newKey] = processed_obj[key];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,9 @@ export async function load_ae_obj_id__event_location({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventLocation | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_id__event_location() *** [V3] id=${event_location_id} (SWR)`);
|
||||
console.log(
|
||||
`*** load_ae_obj_id__event_location() *** [V3] id=${event_location_id} (SWR)`
|
||||
);
|
||||
}
|
||||
|
||||
// 1. FAST PATH: Cache hit
|
||||
@@ -45,40 +47,95 @@ export async function load_ae_obj_id__event_location({
|
||||
try {
|
||||
const cached = await db_events.location.get(event_location_id);
|
||||
if (cached) {
|
||||
_refresh_location_id_background({
|
||||
api_cfg, event_location_id, view, try_cache,
|
||||
inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li,
|
||||
log_lvl: 0
|
||||
_refresh_location_id_background({
|
||||
api_cfg,
|
||||
event_location_id,
|
||||
view,
|
||||
try_cache,
|
||||
inc_file_li,
|
||||
inc_session_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
inc_device_li,
|
||||
inc_all_file_li,
|
||||
log_lvl: 0
|
||||
});
|
||||
return await _handle_nested_loads(cached, {
|
||||
api_cfg, inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li,
|
||||
log_lvl
|
||||
return await _handle_nested_loads(cached, {
|
||||
api_cfg,
|
||||
inc_file_li,
|
||||
inc_session_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
inc_device_li,
|
||||
inc_all_file_li,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// 2. SLOW PATH: Wait for API
|
||||
return await _refresh_location_id_background({
|
||||
api_cfg, event_location_id, view, try_cache,
|
||||
inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li,
|
||||
log_lvl
|
||||
return await _refresh_location_id_background({
|
||||
api_cfg,
|
||||
event_location_id,
|
||||
view,
|
||||
try_cache,
|
||||
inc_file_li,
|
||||
inc_session_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
inc_device_li,
|
||||
inc_all_file_li,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
async function _refresh_location_id_background({ api_cfg, event_location_id, view, try_cache, inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li, log_lvl }: any) {
|
||||
async function _refresh_location_id_background({
|
||||
api_cfg,
|
||||
event_location_id,
|
||||
view,
|
||||
try_cache,
|
||||
inc_file_li,
|
||||
inc_session_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
inc_device_li,
|
||||
inc_all_file_li,
|
||||
log_lvl
|
||||
}: any) {
|
||||
if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
|
||||
try {
|
||||
const result = await api.get_ae_obj_v3({ api_cfg, obj_type: 'event_location', obj_id: event_location_id, view, log_lvl });
|
||||
const result = await api.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_location',
|
||||
obj_id: event_location_id,
|
||||
view,
|
||||
log_lvl
|
||||
});
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_location_props({ obj_li: [result], log_lvl });
|
||||
const processed = await process_ae_obj__event_location_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj = processed[0];
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'location', obj_li: [processed_obj], properties_to_save, log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'location',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return await _handle_nested_loads(processed_obj, {
|
||||
api_cfg, inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li,
|
||||
log_lvl
|
||||
return await _handle_nested_loads(processed_obj, {
|
||||
api_cfg,
|
||||
inc_file_li,
|
||||
inc_session_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
inc_device_li,
|
||||
inc_all_file_li,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
} catch (e) {}
|
||||
@@ -129,24 +186,48 @@ export async function load_ae_obj_li__event_location({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventLocation[]> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_li__event_location() *** [V3] for=${for_obj_type}:${for_obj_id} (SWR)`);
|
||||
console.log(
|
||||
`*** load_ae_obj_li__event_location() *** [V3] for=${for_obj_type}:${for_obj_id} (SWR)`
|
||||
);
|
||||
}
|
||||
|
||||
// 1. FAST PATH: Check cache
|
||||
if (try_cache) {
|
||||
try {
|
||||
const cached_li = await db_events.location.where('event_id').equals(for_obj_id).toArray();
|
||||
const cached_li = await db_events.location
|
||||
.where('event_id')
|
||||
.equals(for_obj_id)
|
||||
.toArray();
|
||||
if (cached_li && cached_li.length > 0) {
|
||||
_refresh_location_li_background({
|
||||
api_cfg, for_obj_type, for_obj_id,
|
||||
inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li,
|
||||
enabled, hidden, view, limit, offset, order_by_li, try_cache,
|
||||
log_lvl: 0
|
||||
_refresh_location_li_background({
|
||||
api_cfg,
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
inc_file_li,
|
||||
inc_session_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
inc_device_li,
|
||||
inc_all_file_li,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
try_cache,
|
||||
log_lvl: 0
|
||||
});
|
||||
for (const loc of cached_li) {
|
||||
_handle_nested_loads(loc, {
|
||||
api_cfg, inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li,
|
||||
log_lvl: 0
|
||||
_handle_nested_loads(loc, {
|
||||
api_cfg,
|
||||
inc_file_li,
|
||||
inc_session_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
inc_device_li,
|
||||
inc_all_file_li,
|
||||
log_lvl: 0
|
||||
});
|
||||
}
|
||||
return cached_li;
|
||||
@@ -155,33 +236,91 @@ export async function load_ae_obj_li__event_location({
|
||||
}
|
||||
|
||||
// 2. SLOW PATH: API
|
||||
return await _refresh_location_li_background({
|
||||
api_cfg, for_obj_type, for_obj_id,
|
||||
inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li,
|
||||
enabled, hidden, view, limit, offset, order_by_li, try_cache,
|
||||
log_lvl
|
||||
return await _refresh_location_li_background({
|
||||
api_cfg,
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
inc_file_li,
|
||||
inc_session_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
inc_device_li,
|
||||
inc_all_file_li,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
try_cache,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
async function _refresh_location_li_background({ api_cfg, for_obj_type, for_obj_id, inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl }: any) {
|
||||
async function _refresh_location_li_background({
|
||||
api_cfg,
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
inc_file_li,
|
||||
inc_session_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
inc_device_li,
|
||||
inc_all_file_li,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}: any) {
|
||||
if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
|
||||
try {
|
||||
const result_li = await api.get_ae_obj_li_v3({ api_cfg, obj_type: 'event_location', for_obj_type, for_obj_id, enabled, hidden, view, limit, offset, order_by_li, log_lvl });
|
||||
const result_li = await api.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'event_location',
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
log_lvl
|
||||
});
|
||||
if (result_li) {
|
||||
const processed = await process_ae_obj__event_location_props({ obj_li: result_li, log_lvl });
|
||||
|
||||
const processed = await process_ae_obj__event_location_props({
|
||||
obj_li: result_li,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
// String-Only ID Vision: Ensure linking ID is set for indexing
|
||||
if (for_obj_type === 'event') {
|
||||
processed.forEach(loc => loc.event_id = for_obj_id);
|
||||
processed.forEach((loc) => (loc.event_id = for_obj_id));
|
||||
}
|
||||
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'location', obj_li: processed, properties_to_save, log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'location',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
for (const loc of processed) {
|
||||
_handle_nested_loads(loc, {
|
||||
api_cfg, inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li,
|
||||
log_lvl: 0
|
||||
_handle_nested_loads(loc, {
|
||||
api_cfg,
|
||||
inc_file_li,
|
||||
inc_session_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
inc_device_li,
|
||||
inc_all_file_li,
|
||||
log_lvl: 0
|
||||
});
|
||||
}
|
||||
return processed;
|
||||
@@ -193,34 +332,66 @@ async function _refresh_location_li_background({ api_cfg, for_obj_type, for_obj_
|
||||
/**
|
||||
* Handle nested data loads for a single location object.
|
||||
*/
|
||||
async function _handle_nested_loads(location_obj: any, { api_cfg, inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li, log_lvl }: any) {
|
||||
const current_location_id = location_obj.id || location_obj.event_location_id;
|
||||
async function _handle_nested_loads(
|
||||
location_obj: any,
|
||||
{
|
||||
api_cfg,
|
||||
inc_file_li,
|
||||
inc_session_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
inc_device_li,
|
||||
inc_all_file_li,
|
||||
log_lvl
|
||||
}: any
|
||||
) {
|
||||
const current_location_id =
|
||||
location_obj.id || location_obj.event_location_id;
|
||||
if (!current_location_id) return location_obj;
|
||||
|
||||
const tasks = [];
|
||||
if (inc_file_li) {
|
||||
tasks.push(load_ae_obj_li__event_file({
|
||||
api_cfg, for_obj_type: 'event_location', for_obj_id: current_location_id,
|
||||
enabled: 'all', limit: 25, log_lvl
|
||||
}).then(res => location_obj.event_file_li = res));
|
||||
tasks.push(
|
||||
load_ae_obj_li__event_file({
|
||||
api_cfg,
|
||||
for_obj_type: 'event_location',
|
||||
for_obj_id: current_location_id,
|
||||
enabled: 'all',
|
||||
limit: 25,
|
||||
log_lvl
|
||||
}).then((res) => (location_obj.event_file_li = res))
|
||||
);
|
||||
}
|
||||
|
||||
if (inc_session_li) {
|
||||
tasks.push(load_ae_obj_li__event_session({
|
||||
api_cfg, for_obj_type: 'event_location', for_obj_id: current_location_id,
|
||||
inc_file_li: inc_all_file_li,
|
||||
inc_all_file_li: inc_all_file_li,
|
||||
inc_presentation_li: inc_presentation_li,
|
||||
inc_presenter_li: inc_presenter_li,
|
||||
enabled: 'enabled', hidden: 'not_hidden', limit: 150, log_lvl
|
||||
}).then(res => location_obj.event_session_obj_li = res));
|
||||
tasks.push(
|
||||
load_ae_obj_li__event_session({
|
||||
api_cfg,
|
||||
for_obj_type: 'event_location',
|
||||
for_obj_id: current_location_id,
|
||||
inc_file_li: inc_all_file_li,
|
||||
inc_all_file_li: inc_all_file_li,
|
||||
inc_presentation_li: inc_presentation_li,
|
||||
inc_presenter_li: inc_presenter_li,
|
||||
enabled: 'enabled',
|
||||
hidden: 'not_hidden',
|
||||
limit: 150,
|
||||
log_lvl
|
||||
}).then((res) => (location_obj.event_session_obj_li = res))
|
||||
);
|
||||
}
|
||||
|
||||
if (inc_device_li) {
|
||||
tasks.push(load_ae_obj_li__event_device({
|
||||
api_cfg, for_obj_type: 'event_location', for_obj_id: current_location_id,
|
||||
enabled: 'all', limit: 50, log_lvl
|
||||
}).then(res => location_obj.event_device_li = res));
|
||||
tasks.push(
|
||||
load_ae_obj_li__event_device({
|
||||
api_cfg,
|
||||
for_obj_type: 'event_location',
|
||||
for_obj_id: current_location_id,
|
||||
enabled: 'all',
|
||||
limit: 50,
|
||||
log_lvl
|
||||
}).then((res) => (location_obj.event_device_li = res))
|
||||
);
|
||||
}
|
||||
|
||||
if (tasks.length > 0) await Promise.all(tasks);
|
||||
@@ -229,132 +400,183 @@ async function _handle_nested_loads(location_obj: any, { api_cfg, inc_file_li, i
|
||||
|
||||
// Updated 2026-01-20 to V3
|
||||
export async function create_ae_obj__event_location({
|
||||
api_cfg,
|
||||
event_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
api_cfg,
|
||||
event_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
event_id: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_id: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventLocation | null> {
|
||||
const result = await api.create_nested_obj_v3({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_location',
|
||||
fields: { ...data_kv },
|
||||
log_lvl
|
||||
});
|
||||
const result = await api.create_nested_obj({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_location',
|
||||
fields: { ...data_kv },
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_location_props({ obj_li: [result], log_lvl });
|
||||
const processed_obj = processed[0];
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'location',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
return null;
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_location_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj = processed[0];
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'location',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Updated 2026-01-20 to V3
|
||||
export async function delete_ae_obj_id__event_location({
|
||||
api_cfg,
|
||||
event_id,
|
||||
event_location_id,
|
||||
method = 'delete',
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
api_cfg,
|
||||
event_id,
|
||||
event_location_id,
|
||||
method = 'delete',
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
event_id: string;
|
||||
event_location_id: string;
|
||||
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_id: string;
|
||||
event_location_id: string;
|
||||
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
const result = await api.delete_nested_ae_obj_v3({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_location',
|
||||
obj_id: event_location_id,
|
||||
method,
|
||||
log_lvl
|
||||
});
|
||||
if (try_cache) await db_events.location.delete(event_location_id);
|
||||
return result;
|
||||
const result = await api.delete_nested_ae_obj({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_location',
|
||||
obj_id: event_location_id,
|
||||
method,
|
||||
log_lvl
|
||||
});
|
||||
if (try_cache) await db_events.location.delete(event_location_id);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Updated 2026-01-20 to V3
|
||||
export async function update_ae_obj__event_location({
|
||||
api_cfg,
|
||||
event_id,
|
||||
event_location_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
api_cfg,
|
||||
event_id,
|
||||
event_location_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
event_id: string;
|
||||
event_location_id: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_id: string;
|
||||
event_location_id: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventLocation | null> {
|
||||
const result = await api.update_nested_obj_v3({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_location',
|
||||
obj_id: event_location_id,
|
||||
fields: data_kv,
|
||||
log_lvl
|
||||
});
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_location_props({ obj_li: [result], log_lvl });
|
||||
const processed_obj = processed[0];
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'location',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
return null;
|
||||
const result = await api.update_nested_obj({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_location',
|
||||
obj_id: event_location_id,
|
||||
fields: data_kv,
|
||||
log_lvl
|
||||
});
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_location_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj = processed[0];
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'location',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Updated 2026-01-20 to V3
|
||||
export async function search__event_location({
|
||||
api_cfg, event_id, qry_str = '', enabled = 'enabled', hidden = 'not_hidden', view = 'default', limit = 25, offset = 0, order_by_li = [{ sort: 'ASC' }, { name: 'ASC' }], try_cache = true, log_lvl = 0
|
||||
api_cfg,
|
||||
event_id,
|
||||
qry_str = '',
|
||||
enabled = 'enabled',
|
||||
hidden = 'not_hidden',
|
||||
view = 'default',
|
||||
limit = 25,
|
||||
offset = 0,
|
||||
order_by_li = [{ sort: 'ASC' }, { name: 'ASC' }],
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any; event_id: string; qry_str?: string; enabled?: 'enabled' | 'all' | 'not_enabled'; hidden?: 'hidden' | 'all' | 'not_hidden'; view?: string; limit?: number; offset?: number; order_by_li?: any; try_cache?: boolean; log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_id: string;
|
||||
qry_str?: string;
|
||||
enabled?: 'enabled' | 'all' | 'not_enabled';
|
||||
hidden?: 'hidden' | 'all' | 'not_hidden';
|
||||
view?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
order_by_li?: any;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventLocation[]> {
|
||||
const search_query: any = { q: qry_str, and: [{ field: 'event_id', op: 'eq', value: event_id }] };
|
||||
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: true });
|
||||
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: false });
|
||||
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: true });
|
||||
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: false });
|
||||
const search_query: any = {
|
||||
q: qry_str,
|
||||
and: [{ field: 'event_id', op: 'eq', value: event_id }]
|
||||
};
|
||||
if (enabled === 'enabled')
|
||||
search_query.and.push({ field: 'enable', op: 'eq', value: true });
|
||||
else if (enabled === 'not_enabled')
|
||||
search_query.and.push({ field: 'enable', op: 'eq', value: false });
|
||||
if (hidden === 'hidden')
|
||||
search_query.and.push({ field: 'hide', op: 'eq', value: true });
|
||||
else if (hidden === 'not_hidden')
|
||||
search_query.and.push({ field: 'hide', op: 'eq', value: false });
|
||||
|
||||
const result_li = await api.search_ae_obj_v3({ api_cfg, obj_type: 'event_location', search_query, order_by_li, view, limit, offset, log_lvl });
|
||||
const result_li = await api.search_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_location',
|
||||
search_query,
|
||||
order_by_li,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
log_lvl
|
||||
});
|
||||
if (result_li) {
|
||||
const processed = await process_ae_obj__event_location_props({ obj_li: result_li, log_lvl });
|
||||
const processed = await process_ae_obj__event_location_props({
|
||||
obj_li: result_li,
|
||||
log_lvl
|
||||
});
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'location', obj_li: processed, properties_to_save, log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'location',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
@@ -362,10 +584,40 @@ export async function search__event_location({
|
||||
}
|
||||
|
||||
export const properties_to_save = [
|
||||
'id', 'event_location_id', 'event_location_id_random', 'event_id', 'event_id_random', 'external_id', 'code', 'name', 'description', 'passcode', 'enable', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on', 'tmp_sort_1', 'tmp_sort_2', 'event_name'
|
||||
'id',
|
||||
'event_location_id',
|
||||
'event_location_id_random',
|
||||
'event_id',
|
||||
'event_id_random',
|
||||
'external_id',
|
||||
'code',
|
||||
'name',
|
||||
'description',
|
||||
'passcode',
|
||||
'enable',
|
||||
'hide',
|
||||
'priority',
|
||||
'sort',
|
||||
'group',
|
||||
'notes',
|
||||
'created_on',
|
||||
'updated_on',
|
||||
'tmp_sort_1',
|
||||
'tmp_sort_2',
|
||||
'event_name'
|
||||
];
|
||||
|
||||
async function _process_generic_props<T extends Record<string, any>>({ obj_li, obj_type, log_lvl = 0, specific_processor }: { obj_li: T[]; obj_type: string; log_lvl?: number; specific_processor?: (obj: T) => Promise<T> | T; }): Promise<T[]> {
|
||||
async function _process_generic_props<T extends Record<string, any>>({
|
||||
obj_li,
|
||||
obj_type,
|
||||
log_lvl = 0,
|
||||
specific_processor
|
||||
}: {
|
||||
obj_li: T[];
|
||||
obj_type: string;
|
||||
log_lvl?: number;
|
||||
specific_processor?: (obj: T) => Promise<T> | T;
|
||||
}): Promise<T[]> {
|
||||
if (!obj_li || obj_li.length === 0) return [];
|
||||
const processed_obj_li: T[] = [];
|
||||
for (const original_obj of obj_li) {
|
||||
@@ -378,24 +630,42 @@ async function _process_generic_props<T extends Record<string, any>>({ obj_li, o
|
||||
}
|
||||
const randomIdKey = `${obj_type}_id_random`;
|
||||
const baseIdKey = `${obj_type}_id`;
|
||||
if (processed_obj[randomIdKey]) (processed_obj as any).id = processed_obj[randomIdKey];
|
||||
else if (processed_obj[baseIdKey]) (processed_obj as any).id = processed_obj[baseIdKey];
|
||||
if (processed_obj[randomIdKey])
|
||||
(processed_obj as any).id = processed_obj[randomIdKey];
|
||||
else if (processed_obj[baseIdKey])
|
||||
(processed_obj as any).id = processed_obj[baseIdKey];
|
||||
const group = processed_obj.group ?? '0';
|
||||
const priority = processed_obj.priority ? 1 : 0;
|
||||
const sort = processed_obj.sort ?? '0';
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on;
|
||||
const name = processed_obj.name ?? '';
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
if (specific_processor) processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
if (specific_processor)
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
}
|
||||
return processed_obj_li;
|
||||
}
|
||||
|
||||
export async function process_ae_obj__event_location_props({ obj_li, log_lvl = 0 }: { obj_li: any[]; log_lvl?: number; }) {
|
||||
return _process_generic_props({ obj_li, obj_type: 'event_location', log_lvl, specific_processor: (obj) => {
|
||||
if (obj.event_id_random) obj.event_id = obj.event_id_random;
|
||||
return obj;
|
||||
}});
|
||||
}
|
||||
export async function process_ae_obj__event_location_props({
|
||||
obj_li,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
obj_li: any[];
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
return _process_generic_props({
|
||||
obj_li,
|
||||
obj_type: 'event_location',
|
||||
log_lvl,
|
||||
specific_processor: (obj) => {
|
||||
if (obj.event_id_random) obj.event_id = obj.event_id_random;
|
||||
return obj;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -37,41 +37,116 @@ export async function load_ae_obj_id__event_presentation({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventPresentation | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_id__event_presentation() *** id=${event_presentation_id} (SWR)`);
|
||||
console.log(
|
||||
`*** load_ae_obj_id__event_presentation() *** id=${event_presentation_id} (SWR)`
|
||||
);
|
||||
}
|
||||
|
||||
// 1. FAST PATH: Cache hit
|
||||
if (try_cache) {
|
||||
try {
|
||||
const cached = await db_events.presentation.get(event_presentation_id);
|
||||
const cached = await db_events.presentation.get(
|
||||
event_presentation_id
|
||||
);
|
||||
if (cached) {
|
||||
// Background refresh (non-blocking)
|
||||
_refresh_presentation_id_background({ api_cfg, event_presentation_id, view, try_cache, inc_file_li, inc_presenter_li, enabled, hidden, limit, offset, log_lvl: 0 });
|
||||
_refresh_presentation_id_background({
|
||||
api_cfg,
|
||||
event_presentation_id,
|
||||
view,
|
||||
try_cache,
|
||||
inc_file_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
log_lvl: 0
|
||||
});
|
||||
// Await nested loads from cache to return a complete object
|
||||
return await _handle_nested_loads(cached, { api_cfg, inc_file_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl: 0 });
|
||||
return await _handle_nested_loads(cached, {
|
||||
api_cfg,
|
||||
inc_file_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
try_cache,
|
||||
log_lvl: 0
|
||||
});
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// 2. SLOW PATH: Wait for API
|
||||
return await _refresh_presentation_id_background({ api_cfg, event_presentation_id, view, try_cache, inc_file_li, inc_presenter_li, enabled, hidden, limit, offset, log_lvl });
|
||||
return await _refresh_presentation_id_background({
|
||||
api_cfg,
|
||||
event_presentation_id,
|
||||
view,
|
||||
try_cache,
|
||||
inc_file_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal background refresh for a single presentation
|
||||
*/
|
||||
async function _refresh_presentation_id_background({ api_cfg, event_presentation_id, view, try_cache, inc_file_li, inc_presenter_li, enabled, hidden, limit, offset, log_lvl }: any) {
|
||||
async function _refresh_presentation_id_background({
|
||||
api_cfg,
|
||||
event_presentation_id,
|
||||
view,
|
||||
try_cache,
|
||||
inc_file_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
log_lvl
|
||||
}: any) {
|
||||
if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
|
||||
try {
|
||||
const result = await api.get_ae_obj_v3({ api_cfg, obj_type: 'event_presentation', obj_id: event_presentation_id, view, log_lvl });
|
||||
const result = await api.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_presentation',
|
||||
obj_id: event_presentation_id,
|
||||
view,
|
||||
log_lvl
|
||||
});
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_presentation_props({ obj_li: [result], log_lvl });
|
||||
const processed = await process_ae_obj__event_presentation_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj = processed[0];
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'presentation', obj_li: [processed_obj], properties_to_save, log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'presentation',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
// During refresh, we disable child SWR to prevent a request storm
|
||||
return await _handle_nested_loads(processed_obj, { api_cfg, inc_file_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache: false, log_lvl });
|
||||
return await _handle_nested_loads(processed_obj, {
|
||||
api_cfg,
|
||||
inc_file_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
try_cache: false,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
} catch (e) {}
|
||||
return null;
|
||||
@@ -80,23 +155,54 @@ async function _refresh_presentation_id_background({ api_cfg, event_presentation
|
||||
/**
|
||||
* Helper to handle nested collection loads for a presentation
|
||||
*/
|
||||
async function _handle_nested_loads(presentation_obj: any, { api_cfg, inc_file_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl }: any) {
|
||||
const current_presentation_id = presentation_obj.id || presentation_obj.event_presentation_id;
|
||||
async function _handle_nested_loads(
|
||||
presentation_obj: any,
|
||||
{
|
||||
api_cfg,
|
||||
inc_file_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}: any
|
||||
) {
|
||||
const current_presentation_id =
|
||||
presentation_obj.id || presentation_obj.event_presentation_id;
|
||||
if (!current_presentation_id) return presentation_obj;
|
||||
|
||||
const tasks = [];
|
||||
if (inc_file_li) {
|
||||
tasks.push(load_ae_obj_li__event_file({
|
||||
api_cfg, for_obj_type: 'event_presentation', for_obj_id: current_presentation_id,
|
||||
enabled, limit: 25, try_cache, log_lvl
|
||||
}).then(res => presentation_obj.event_file_li = res));
|
||||
tasks.push(
|
||||
load_ae_obj_li__event_file({
|
||||
api_cfg,
|
||||
for_obj_type: 'event_presentation',
|
||||
for_obj_id: current_presentation_id,
|
||||
enabled,
|
||||
limit: 25,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}).then((res) => (presentation_obj.event_file_li = res))
|
||||
);
|
||||
}
|
||||
|
||||
if (inc_presenter_li) {
|
||||
tasks.push(load_ae_obj_li__event_presenter({
|
||||
api_cfg, for_obj_type: 'event_presentation', for_obj_id: current_presentation_id,
|
||||
inc_file_li, enabled, hidden, limit, offset, try_cache, log_lvl
|
||||
}).then(res => presentation_obj.event_presenter_li = res));
|
||||
tasks.push(
|
||||
load_ae_obj_li__event_presenter({
|
||||
api_cfg,
|
||||
for_obj_type: 'event_presentation',
|
||||
for_obj_id: current_presentation_id,
|
||||
inc_file_li,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}).then((res) => (presentation_obj.event_presenter_li = res))
|
||||
);
|
||||
}
|
||||
|
||||
if (tasks.length > 0) await Promise.all(tasks);
|
||||
@@ -140,17 +246,36 @@ export async function load_ae_obj_li__event_presentation({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventPresentation[]> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_li__event_presentation() *** for=${for_obj_type}:${for_obj_id} (SWR)`);
|
||||
console.log(
|
||||
`*** load_ae_obj_li__event_presentation() *** for=${for_obj_type}:${for_obj_id} (SWR)`
|
||||
);
|
||||
}
|
||||
|
||||
// 1. FAST PATH: Cache hit
|
||||
if (try_cache) {
|
||||
try {
|
||||
// String-Only ID Vision: query event_session_id using the string ID
|
||||
const cached_li = await db_events.presentation.where('event_session_id').equals(for_obj_id).toArray();
|
||||
const cached_li = await db_events.presentation
|
||||
.where('event_session_id')
|
||||
.equals(for_obj_id)
|
||||
.toArray();
|
||||
if (cached_li && cached_li.length > 0) {
|
||||
// Background refresh (non-blocking)
|
||||
_refresh_presentation_li_background({ api_cfg, for_obj_type, for_obj_id, inc_file_li, inc_presenter_li, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl: 0 });
|
||||
_refresh_presentation_li_background({
|
||||
api_cfg,
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
inc_file_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
try_cache,
|
||||
log_lvl: 0
|
||||
});
|
||||
|
||||
// Warm cache for nested loads in the background (FIRE AND FORGET)
|
||||
// DEPRECATED Optimization: Don't fire child loads for every item in a list here.
|
||||
@@ -167,23 +292,72 @@ export async function load_ae_obj_li__event_presentation({
|
||||
}
|
||||
|
||||
// 2. SLOW PATH: Wait for API
|
||||
return await _refresh_presentation_li_background({ api_cfg, for_obj_type, for_obj_id, inc_file_li, inc_presenter_li, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl });
|
||||
return await _refresh_presentation_li_background({
|
||||
api_cfg,
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
inc_file_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
try_cache,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
async function _refresh_presentation_li_background({ api_cfg, for_obj_type, for_obj_id, inc_file_li, inc_presenter_li, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl }: any) {
|
||||
async function _refresh_presentation_li_background({
|
||||
api_cfg,
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
inc_file_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}: any) {
|
||||
if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
|
||||
try {
|
||||
const result_li = await api.get_ae_obj_li_v3({ api_cfg, obj_type: 'event_presentation', for_obj_type, for_obj_id, enabled, hidden, view, limit, offset, order_by_li, log_lvl });
|
||||
const result_li = await api.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'event_presentation',
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
log_lvl
|
||||
});
|
||||
if (result_li) {
|
||||
const processed = await process_ae_obj__event_presentation_props({ obj_li: result_li, log_lvl });
|
||||
const processed = await process_ae_obj__event_presentation_props({
|
||||
obj_li: result_li,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
// Ensure the linking ID is set correctly for indexing
|
||||
if (for_obj_type === 'event_session') {
|
||||
processed.forEach(p => p.event_session_id = for_obj_id);
|
||||
processed.forEach((p) => (p.event_session_id = for_obj_id));
|
||||
}
|
||||
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'presentation', obj_li: processed, properties_to_save, log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'presentation',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
// CRITICAL FIX (2026-02-26): Yield to microtask queue so Dexie liveQuery observers
|
||||
// fire before we return. Without this, component-mounted liveQueries may subscribe
|
||||
// to IDB *before* the write completes, causing empty results on cold-start.
|
||||
@@ -194,9 +368,21 @@ async function _refresh_presentation_li_background({ api_cfg, for_obj_type, for_
|
||||
// before presenter data was loaded, causing "refresh twice" bug on cold-start.
|
||||
// Now we await all nested loads AND preserve try_cache so presenters are written to IDB.
|
||||
if (inc_file_li || inc_presenter_li) {
|
||||
await Promise.all(processed.map(p =>
|
||||
_handle_nested_loads(p, { api_cfg, inc_file_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl: 0 })
|
||||
));
|
||||
await Promise.all(
|
||||
processed.map((p) =>
|
||||
_handle_nested_loads(p, {
|
||||
api_cfg,
|
||||
inc_file_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
try_cache,
|
||||
log_lvl: 0
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
@@ -206,42 +392,45 @@ async function _refresh_presentation_li_background({ api_cfg, for_obj_type, for_
|
||||
|
||||
// Updated 2026-01-20 to V3
|
||||
export async function create_ae_obj__event_presentation({
|
||||
api_cfg,
|
||||
event_session_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
api_cfg,
|
||||
event_session_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
event_session_id: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_session_id: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventPresentation | null> {
|
||||
const result = await api.create_nested_obj_v3({
|
||||
api_cfg,
|
||||
for_obj_type: 'event_session',
|
||||
for_obj_id: event_session_id,
|
||||
obj_type: 'event_presentation',
|
||||
fields: { ...data_kv },
|
||||
log_lvl
|
||||
});
|
||||
const result = await api.create_nested_obj({
|
||||
api_cfg,
|
||||
for_obj_type: 'event_session',
|
||||
for_obj_id: event_session_id,
|
||||
obj_type: 'event_presentation',
|
||||
fields: { ...data_kv },
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_presentation_props({ obj_li: [result], log_lvl });
|
||||
const processed_obj = processed[0];
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'presentation',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
return null;
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_presentation_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj = processed[0];
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'presentation',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Updated 2026-01-20 to V3
|
||||
@@ -258,8 +447,12 @@ export async function delete_ae_obj_id__event_presentation({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
const result = await api.delete_ae_obj_v3({
|
||||
api_cfg, obj_type: 'event_presentation', obj_id: event_presentation_id, method, log_lvl
|
||||
const result = await api.delete_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_presentation',
|
||||
obj_id: event_presentation_id,
|
||||
method,
|
||||
log_lvl
|
||||
});
|
||||
if (try_cache) await db_events.presentation.delete(event_presentation_id);
|
||||
return result;
|
||||
@@ -279,14 +472,27 @@ export async function update_ae_obj__event_presentation({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventPresentation | null> {
|
||||
const result = await api.update_ae_obj_v3({
|
||||
api_cfg, obj_type: 'event_presentation', obj_id: event_presentation_id, fields: data_kv, log_lvl
|
||||
const result = await api.update_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_presentation',
|
||||
obj_id: event_presentation_id,
|
||||
fields: data_kv,
|
||||
log_lvl
|
||||
});
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_presentation_props({ obj_li: [result], log_lvl });
|
||||
const processed = await process_ae_obj__event_presentation_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj = processed[0];
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'presentation', obj_li: [processed_obj], properties_to_save, log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'presentation',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
@@ -295,24 +501,74 @@ export async function update_ae_obj__event_presentation({
|
||||
|
||||
// Updated 2026-01-21 to Restore Full Aether Search Logic
|
||||
export async function search__event_presentation({
|
||||
api_cfg, event_id, fulltext_search_qry_str = '', like_search_qry_str = '', enabled = 'enabled', hidden = 'not_hidden', view = 'default', limit = 50, offset = 0, order_by_li = [{ sort: 'ASC' }, { start_datetime: 'ASC' }, { name: 'ASC' }], try_cache = true, log_lvl = 0
|
||||
api_cfg,
|
||||
event_id,
|
||||
fulltext_search_qry_str = '',
|
||||
like_search_qry_str = '',
|
||||
enabled = 'enabled',
|
||||
hidden = 'not_hidden',
|
||||
view = 'default',
|
||||
limit = 50,
|
||||
offset = 0,
|
||||
order_by_li = [{ sort: 'ASC' }, { start_datetime: 'ASC' }, { name: 'ASC' }],
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any; event_id: string; fulltext_search_qry_str?: string; like_search_qry_str?: string; enabled?: 'enabled' | 'all' | 'not_enabled'; hidden?: 'hidden' | 'all' | 'not_hidden'; view?: string; limit?: number; offset?: number; order_by_li?: any; try_cache?: boolean; log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_id: string;
|
||||
fulltext_search_qry_str?: string;
|
||||
like_search_qry_str?: string;
|
||||
enabled?: 'enabled' | 'all' | 'not_enabled';
|
||||
hidden?: 'hidden' | 'all' | 'not_hidden';
|
||||
view?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
order_by_li?: any;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventPresentation[]> {
|
||||
const search_query: any = { q: '', and: [{ field: 'event_id', op: 'eq', value: event_id }] };
|
||||
const search_query: any = {
|
||||
q: '',
|
||||
and: [{ field: 'event_id', op: 'eq', value: event_id }]
|
||||
};
|
||||
const params: key_val = {};
|
||||
if (fulltext_search_qry_str && fulltext_search_qry_str.length > 2) params['ft_qry'] = { 'default_qry_str': fulltext_search_qry_str };
|
||||
if (like_search_qry_str) params['lk_qry'] = { 'default_qry_str': like_search_qry_str };
|
||||
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
|
||||
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 0 });
|
||||
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 1 });
|
||||
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 0 });
|
||||
if (fulltext_search_qry_str && fulltext_search_qry_str.length > 2)
|
||||
params['ft_qry'] = { default_qry_str: fulltext_search_qry_str };
|
||||
if (like_search_qry_str)
|
||||
params['lk_qry'] = { default_qry_str: like_search_qry_str };
|
||||
if (enabled === 'enabled')
|
||||
search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
|
||||
else if (enabled === 'not_enabled')
|
||||
search_query.and.push({ field: 'enable', op: 'eq', value: 0 });
|
||||
if (hidden === 'hidden')
|
||||
search_query.and.push({ field: 'hide', op: 'eq', value: 1 });
|
||||
else if (hidden === 'not_hidden')
|
||||
search_query.and.push({ field: 'hide', op: 'eq', value: 0 });
|
||||
|
||||
const result_li = await api.search_ae_obj_v3({ api_cfg, obj_type: 'event_presentation', search_query, order_by_li, params, view, limit, offset, log_lvl });
|
||||
const result_li = await api.search_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_presentation',
|
||||
search_query,
|
||||
order_by_li,
|
||||
params,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
log_lvl
|
||||
});
|
||||
if (result_li) {
|
||||
const processed = await process_ae_obj__event_presentation_props({ obj_li: result_li, log_lvl });
|
||||
const processed = await process_ae_obj__event_presentation_props({
|
||||
obj_li: result_li,
|
||||
log_lvl
|
||||
});
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'presentation', obj_li: processed, properties_to_save, log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'presentation',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
@@ -322,10 +578,53 @@ export async function search__event_presentation({
|
||||
export const qry__event_presentation = search__event_presentation;
|
||||
|
||||
export const properties_to_save = [
|
||||
'id', 'event_presentation_id', 'event_presentation_id_random', 'external_id', 'code', 'for_type', 'for_id', 'for_id_random', 'type_code', 'event_id', 'event_session_id', 'event_abstract_id', 'event_id_random', 'event_session_id_random', 'event_abstract_id_random', 'abstract_code', 'name', 'description', 'start_datetime', 'end_datetime', 'passcode', 'hide_event_launcher', 'enable', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on', 'tmp_sort_1', 'tmp_sort_2', 'event_session_code', 'event_session_name'
|
||||
'id',
|
||||
'event_presentation_id',
|
||||
'event_presentation_id_random',
|
||||
'external_id',
|
||||
'code',
|
||||
'for_type',
|
||||
'for_id',
|
||||
'for_id_random',
|
||||
'type_code',
|
||||
'event_id',
|
||||
'event_session_id',
|
||||
'event_abstract_id',
|
||||
'event_id_random',
|
||||
'event_session_id_random',
|
||||
'event_abstract_id_random',
|
||||
'abstract_code',
|
||||
'name',
|
||||
'description',
|
||||
'start_datetime',
|
||||
'end_datetime',
|
||||
'passcode',
|
||||
'hide_event_launcher',
|
||||
'enable',
|
||||
'hide',
|
||||
'priority',
|
||||
'sort',
|
||||
'group',
|
||||
'notes',
|
||||
'created_on',
|
||||
'updated_on',
|
||||
'tmp_sort_1',
|
||||
'tmp_sort_2',
|
||||
'event_session_code',
|
||||
'event_session_name'
|
||||
];
|
||||
|
||||
async function _process_generic_props<T extends Record<string, any>>({ obj_li, obj_type, log_lvl = 0, specific_processor }: { obj_li: T[]; obj_type: string; log_lvl?: number; specific_processor?: (obj: T) => Promise<T> | T; }): Promise<T[]> {
|
||||
async function _process_generic_props<T extends Record<string, any>>({
|
||||
obj_li,
|
||||
obj_type,
|
||||
log_lvl = 0,
|
||||
specific_processor
|
||||
}: {
|
||||
obj_li: T[];
|
||||
obj_type: string;
|
||||
log_lvl?: number;
|
||||
specific_processor?: (obj: T) => Promise<T> | T;
|
||||
}): Promise<T[]> {
|
||||
if (!obj_li || obj_li.length === 0) return [];
|
||||
const processed_obj_li: T[] = [];
|
||||
for (const original_obj of obj_li) {
|
||||
@@ -338,26 +637,48 @@ async function _process_generic_props<T extends Record<string, any>>({ obj_li, o
|
||||
}
|
||||
const randomIdKey = `${obj_type}_id_random`;
|
||||
const baseIdKey = `${obj_type}_id`;
|
||||
if (processed_obj[randomIdKey]) (processed_obj as any).id = processed_obj[randomIdKey];
|
||||
else if (processed_obj[baseIdKey]) (processed_obj as any).id = processed_obj[baseIdKey];
|
||||
if (processed_obj[randomIdKey])
|
||||
(processed_obj as any).id = processed_obj[randomIdKey];
|
||||
else if (processed_obj[baseIdKey])
|
||||
(processed_obj as any).id = processed_obj[baseIdKey];
|
||||
const group = processed_obj.group ?? '0';
|
||||
const priority = processed_obj.priority ? 1 : 0;
|
||||
const sort = processed_obj.sort ?? '0';
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on ?? new Date(0).toISOString();
|
||||
const updated =
|
||||
processed_obj.updated_on ??
|
||||
processed_obj.created_on ??
|
||||
new Date(0).toISOString();
|
||||
const name = processed_obj.name ?? '';
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
if (specific_processor) processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
if (specific_processor)
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
}
|
||||
return processed_obj_li;
|
||||
}
|
||||
|
||||
export async function process_ae_obj__event_presentation_props({ obj_li, log_lvl = 0 }: { obj_li: any[]; log_lvl?: number; }) {
|
||||
return _process_generic_props({ obj_li, obj_type: 'event_presentation', log_lvl, specific_processor: (obj) => {
|
||||
// Ensure linking IDs are the string versions for indexing
|
||||
if (obj.event_session_id_random) obj.event_session_id = obj.event_session_id_random;
|
||||
if (obj.event_id_random) obj.event_id = obj.event_id_random;
|
||||
return obj;
|
||||
}});
|
||||
}
|
||||
export async function process_ae_obj__event_presentation_props({
|
||||
obj_li,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
obj_li: any[];
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
return _process_generic_props({
|
||||
obj_li,
|
||||
obj_type: 'event_presentation',
|
||||
log_lvl,
|
||||
specific_processor: (obj) => {
|
||||
// Ensure linking IDs are the string versions for indexing
|
||||
if (obj.event_session_id_random)
|
||||
obj.event_session_id = obj.event_session_id_random;
|
||||
if (obj.event_id_random) obj.event_id = obj.event_id_random;
|
||||
return obj;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -29,7 +29,9 @@ export async function load_ae_obj_id__event_presenter({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventPresenter | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_id__event_presenter() *** [V3] id=${event_presenter_id} (SWR)`);
|
||||
console.log(
|
||||
`*** load_ae_obj_id__event_presenter() *** [V3] id=${event_presenter_id} (SWR)`
|
||||
);
|
||||
}
|
||||
|
||||
// 1. FAST PATH: Cache hit
|
||||
@@ -38,30 +40,71 @@ export async function load_ae_obj_id__event_presenter({
|
||||
const cached = await db_events.presenter.get(event_presenter_id);
|
||||
if (cached) {
|
||||
// Background refresh (non-blocking)
|
||||
_refresh_presenter_id_background({ api_cfg, event_presenter_id, view, try_cache, inc_file_li, log_lvl: 0 });
|
||||
_refresh_presenter_id_background({
|
||||
api_cfg,
|
||||
event_presenter_id,
|
||||
view,
|
||||
try_cache,
|
||||
inc_file_li,
|
||||
log_lvl: 0
|
||||
});
|
||||
return cached;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// 2. SLOW PATH: Wait for API
|
||||
return await _refresh_presenter_id_background({ api_cfg, event_presenter_id, view, try_cache, inc_file_li, log_lvl });
|
||||
return await _refresh_presenter_id_background({
|
||||
api_cfg,
|
||||
event_presenter_id,
|
||||
view,
|
||||
try_cache,
|
||||
inc_file_li,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
async function _refresh_presenter_id_background({ api_cfg, event_presenter_id, view, try_cache, inc_file_li, log_lvl }: any) {
|
||||
async function _refresh_presenter_id_background({
|
||||
api_cfg,
|
||||
event_presenter_id,
|
||||
view,
|
||||
try_cache,
|
||||
inc_file_li,
|
||||
log_lvl
|
||||
}: any) {
|
||||
if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
|
||||
try {
|
||||
const result = await api.get_ae_obj_v3({ api_cfg, obj_type: 'event_presenter', obj_id: event_presenter_id, view, log_lvl });
|
||||
const result = await api.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_presenter',
|
||||
obj_id: event_presenter_id,
|
||||
view,
|
||||
log_lvl
|
||||
});
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_presenter_props({ obj_li: [result], log_lvl });
|
||||
const processed = await process_ae_obj__event_presenter_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj = processed[0];
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'presenter', obj_li: [processed_obj], properties_to_save, log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'presenter',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
if (inc_file_li) {
|
||||
processed_obj.event_file_li = await load_ae_obj_li__event_file({
|
||||
api_cfg, for_obj_type: 'event_presenter', for_obj_id: event_presenter_id,
|
||||
enabled: 'all', limit: 25, try_cache: false, log_lvl
|
||||
api_cfg,
|
||||
for_obj_type: 'event_presenter',
|
||||
for_obj_id: event_presenter_id,
|
||||
enabled: 'all',
|
||||
limit: 25,
|
||||
try_cache: false,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
@@ -104,7 +147,9 @@ export async function load_ae_obj_li__event_presenter({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventPresenter[]> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_li__event_presenter() *** [V3] for=${for_obj_type}:${for_obj_id} (SWR)`);
|
||||
console.log(
|
||||
`*** load_ae_obj_li__event_presenter() *** [V3] for=${for_obj_type}:${for_obj_id} (SWR)`
|
||||
);
|
||||
}
|
||||
|
||||
// 1. FAST PATH: Check cache using specific indices
|
||||
@@ -112,31 +157,94 @@ export async function load_ae_obj_li__event_presenter({
|
||||
try {
|
||||
let cached_li: any[] = [];
|
||||
if (for_obj_type === 'event_presentation') {
|
||||
cached_li = await db_events.presenter.where('event_presentation_id').equals(for_obj_id).toArray();
|
||||
cached_li = await db_events.presenter
|
||||
.where('event_presentation_id')
|
||||
.equals(for_obj_id)
|
||||
.toArray();
|
||||
} else if (for_obj_type === 'event_session') {
|
||||
cached_li = await db_events.presenter.where('event_session_id').equals(for_obj_id).toArray();
|
||||
cached_li = await db_events.presenter
|
||||
.where('event_session_id')
|
||||
.equals(for_obj_id)
|
||||
.toArray();
|
||||
} else if (for_obj_type === 'event') {
|
||||
cached_li = await db_events.presenter.where('event_id').equals(for_obj_id).toArray();
|
||||
cached_li = await db_events.presenter
|
||||
.where('event_id')
|
||||
.equals(for_obj_id)
|
||||
.toArray();
|
||||
}
|
||||
|
||||
if (cached_li && cached_li.length > 0) {
|
||||
// Background refresh (non-blocking)
|
||||
_refresh_presenter_li_background({ api_cfg, for_obj_type, for_obj_id, inc_file_li, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl: 0 });
|
||||
_refresh_presenter_li_background({
|
||||
api_cfg,
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
inc_file_li,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
try_cache,
|
||||
log_lvl: 0
|
||||
});
|
||||
return cached_li;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// 2. SLOW PATH: Wait for API
|
||||
return await _refresh_presenter_li_background({ api_cfg, for_obj_type, for_obj_id, inc_file_li, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl });
|
||||
return await _refresh_presenter_li_background({
|
||||
api_cfg,
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
inc_file_li,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
try_cache,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
async function _refresh_presenter_li_background({ api_cfg, for_obj_type, for_obj_id, inc_file_li, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl }: any) {
|
||||
async function _refresh_presenter_li_background({
|
||||
api_cfg,
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
inc_file_li,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}: any) {
|
||||
if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
|
||||
try {
|
||||
const result_li = await api.get_ae_obj_li_v3({ api_cfg, obj_type: 'event_presenter', for_obj_type, for_obj_id, enabled, hidden, view, limit, offset, order_by_li, log_lvl });
|
||||
const result_li = await api.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'event_presenter',
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
log_lvl
|
||||
});
|
||||
if (result_li) {
|
||||
const processed = await process_ae_obj__event_presenter_props({ obj_li: result_li, log_lvl });
|
||||
const processed = await process_ae_obj__event_presenter_props({
|
||||
obj_li: result_li,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
// String-Only ID Vision: Ensure linking ID is set for indexing
|
||||
processed.forEach((p) => {
|
||||
@@ -155,7 +263,13 @@ async function _refresh_presenter_li_background({ api_cfg, for_obj_type, for_obj
|
||||
});
|
||||
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'presenter', obj_li: processed, properties_to_save, log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'presenter',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
// CRITICAL FIX (2026-02-26): Yield to microtask queue so Dexie liveQuery observers
|
||||
// fire before we return. Without this, component-mounted liveQueries may subscribe
|
||||
// to IDB *before* the write completes, causing empty results on cold-start.
|
||||
@@ -164,10 +278,15 @@ async function _refresh_presenter_li_background({ api_cfg, for_obj_type, for_obj
|
||||
|
||||
// Background nested loads for refreshed items (FIRE AND FORGET)
|
||||
if (inc_file_li) {
|
||||
processed.forEach(p => {
|
||||
processed.forEach((p) => {
|
||||
load_ae_obj_li__event_file({
|
||||
api_cfg, for_obj_type: 'event_presenter', for_obj_id: p.id,
|
||||
enabled: 'all', limit: 25, try_cache: false, log_lvl: 0
|
||||
api_cfg,
|
||||
for_obj_type: 'event_presenter',
|
||||
for_obj_id: p.id,
|
||||
enabled: 'all',
|
||||
limit: 25,
|
||||
try_cache: false,
|
||||
log_lvl: 0
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -180,128 +299,143 @@ async function _refresh_presenter_li_background({ api_cfg, for_obj_type, for_obj
|
||||
|
||||
// Updated 2026-01-20 to V3
|
||||
export async function create_ae_obj__event_presenter({
|
||||
api_cfg,
|
||||
event_presentation_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
api_cfg,
|
||||
event_presentation_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
event_presentation_id?: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_presentation_id?: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventPresenter | null> {
|
||||
if (!event_presentation_id) event_presentation_id = get(events_slct).event_presentation_id;
|
||||
if (!event_presentation_id)
|
||||
event_presentation_id = get(events_slct).event_presentation_id;
|
||||
if (!event_presentation_id) {
|
||||
console.error('create_ae_obj__event_presenter: event_presentation_id is required');
|
||||
console.error(
|
||||
'create_ae_obj__event_presenter: event_presentation_id is required'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
const result = await api.create_nested_obj_v3({
|
||||
api_cfg,
|
||||
for_obj_type: 'event_presentation',
|
||||
for_obj_id: event_presentation_id,
|
||||
obj_type: 'event_presenter',
|
||||
fields: { ...data_kv },
|
||||
log_lvl
|
||||
});
|
||||
const result = await api.create_nested_obj({
|
||||
api_cfg,
|
||||
for_obj_type: 'event_presentation',
|
||||
for_obj_id: event_presentation_id,
|
||||
obj_type: 'event_presenter',
|
||||
fields: { ...data_kv },
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_presenter_props({ obj_li: [result], log_lvl });
|
||||
const processed_obj = processed[0];
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'presenter',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
return null;
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_presenter_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj = processed[0];
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'presenter',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Updated 2026-01-20 to V3
|
||||
export async function delete_ae_obj_id__event_presenter({
|
||||
api_cfg,
|
||||
event_presentation_id,
|
||||
event_presenter_id,
|
||||
method = 'delete',
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
api_cfg,
|
||||
event_presentation_id,
|
||||
event_presenter_id,
|
||||
method = 'delete',
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
event_presentation_id?: string;
|
||||
event_presenter_id: string;
|
||||
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_presentation_id?: string;
|
||||
event_presenter_id: string;
|
||||
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (!event_presentation_id) event_presentation_id = get(events_slct).event_presentation_id;
|
||||
if (!event_presentation_id)
|
||||
event_presentation_id = get(events_slct).event_presentation_id;
|
||||
if (!event_presentation_id) {
|
||||
console.error('delete_ae_obj_id__event_presenter: event_presentation_id is required');
|
||||
console.error(
|
||||
'delete_ae_obj_id__event_presenter: event_presentation_id is required'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
const result = await api.delete_nested_ae_obj_v3({
|
||||
api_cfg,
|
||||
for_obj_type: 'event_presentation',
|
||||
for_obj_id: event_presentation_id,
|
||||
obj_type: 'event_presenter',
|
||||
obj_id: event_presenter_id,
|
||||
method,
|
||||
log_lvl
|
||||
});
|
||||
if (try_cache) await db_events.presenter.delete(event_presenter_id);
|
||||
return result;
|
||||
const result = await api.delete_nested_ae_obj({
|
||||
api_cfg,
|
||||
for_obj_type: 'event_presentation',
|
||||
for_obj_id: event_presentation_id,
|
||||
obj_type: 'event_presenter',
|
||||
obj_id: event_presenter_id,
|
||||
method,
|
||||
log_lvl
|
||||
});
|
||||
if (try_cache) await db_events.presenter.delete(event_presenter_id);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Updated 2026-01-20 to V3
|
||||
export async function update_ae_obj__event_presenter({
|
||||
api_cfg,
|
||||
event_presentation_id,
|
||||
event_presenter_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
api_cfg,
|
||||
event_presentation_id,
|
||||
event_presenter_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
event_presentation_id?: string;
|
||||
event_presenter_id: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_presentation_id?: string;
|
||||
event_presenter_id: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventPresenter | null> {
|
||||
if (!event_presentation_id) event_presentation_id = get(events_slct).event_presentation_id;
|
||||
if (!event_presentation_id)
|
||||
event_presentation_id = get(events_slct).event_presentation_id;
|
||||
if (!event_presentation_id) {
|
||||
console.error('update_ae_obj__event_presenter: event_presentation_id is required');
|
||||
console.error(
|
||||
'update_ae_obj__event_presenter: event_presentation_id is required'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
const result = await api.update_nested_obj_v3({
|
||||
api_cfg,
|
||||
for_obj_type: 'event_presentation',
|
||||
for_obj_id: event_presentation_id,
|
||||
obj_type: 'event_presenter',
|
||||
obj_id: event_presenter_id,
|
||||
fields: data_kv,
|
||||
log_lvl
|
||||
});
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_presenter_props({ obj_li: [result], log_lvl });
|
||||
const processed_obj = processed[0];
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'presenter',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
return null;
|
||||
const result = await api.update_nested_obj({
|
||||
api_cfg,
|
||||
for_obj_type: 'event_presentation',
|
||||
for_obj_id: event_presentation_id,
|
||||
obj_type: 'event_presenter',
|
||||
obj_id: event_presenter_id,
|
||||
fields: data_kv,
|
||||
log_lvl
|
||||
});
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_presenter_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj = processed[0];
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'presenter',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Updated 2026-01-21 to Restore Full Aether Search Logic
|
||||
@@ -320,7 +454,11 @@ export async function search__event_presenter({
|
||||
view = 'default',
|
||||
limit = 25,
|
||||
offset = 0,
|
||||
order_by_li = [{ sort: 'ASC' }, { given_name: 'ASC' }, { family_name: 'ASC' }],
|
||||
order_by_li = [
|
||||
{ sort: 'ASC' },
|
||||
{ given_name: 'ASC' },
|
||||
{ family_name: 'ASC' }
|
||||
],
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
@@ -342,29 +480,71 @@ export async function search__event_presenter({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventPresenter[]> {
|
||||
const search_query: any = { q: '', and: [{ field: 'event_id', op: 'eq', value: event_id }] };
|
||||
const search_query: any = {
|
||||
q: '',
|
||||
and: [{ field: 'event_id', op: 'eq', value: event_id }]
|
||||
};
|
||||
const params: key_val = {};
|
||||
if (fulltext_search_qry_str && fulltext_search_qry_str.length > 2)
|
||||
params['ft_qry'] = { default_qry_str: fulltext_search_qry_str };
|
||||
if (ft_presenter_search_qry_str && ft_presenter_search_qry_str.length > 2)
|
||||
params['ft_qry'] = { ...params['ft_qry'], event_presenter_li_qry_str: ft_presenter_search_qry_str };
|
||||
if (like_search_qry_str) params['lk_qry'] = { default_qry_str: like_search_qry_str };
|
||||
params['ft_qry'] = {
|
||||
...params['ft_qry'],
|
||||
event_presenter_li_qry_str: ft_presenter_search_qry_str
|
||||
};
|
||||
if (like_search_qry_str)
|
||||
params['lk_qry'] = { default_qry_str: like_search_qry_str };
|
||||
if (like_presentation_search_qry_str)
|
||||
params['lk_qry'] = { ...params['lk_qry'], event_presentation_li_qry_str: like_presentation_search_qry_str };
|
||||
params['lk_qry'] = {
|
||||
...params['lk_qry'],
|
||||
event_presentation_li_qry_str: like_presentation_search_qry_str
|
||||
};
|
||||
if (like_presenter_search_qry_str)
|
||||
params['lk_qry'] = { ...params['lk_qry'], event_presenter_li_qry_str: like_presenter_search_qry_str };
|
||||
if (agree !== null) search_query.and.push({ field: 'agree', op: 'eq', value: agree ? 1 : 0 });
|
||||
if (biography === true) search_query.and.push({ field: 'biography', op: 'ne', value: '' });
|
||||
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
|
||||
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 0 });
|
||||
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 1 });
|
||||
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 0 });
|
||||
params['lk_qry'] = {
|
||||
...params['lk_qry'],
|
||||
event_presenter_li_qry_str: like_presenter_search_qry_str
|
||||
};
|
||||
if (agree !== null)
|
||||
search_query.and.push({
|
||||
field: 'agree',
|
||||
op: 'eq',
|
||||
value: agree ? 1 : 0
|
||||
});
|
||||
if (biography === true)
|
||||
search_query.and.push({ field: 'biography', op: 'ne', value: '' });
|
||||
if (enabled === 'enabled')
|
||||
search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
|
||||
else if (enabled === 'not_enabled')
|
||||
search_query.and.push({ field: 'enable', op: 'eq', value: 0 });
|
||||
if (hidden === 'hidden')
|
||||
search_query.and.push({ field: 'hide', op: 'eq', value: 1 });
|
||||
else if (hidden === 'not_hidden')
|
||||
search_query.and.push({ field: 'hide', op: 'eq', value: 0 });
|
||||
|
||||
const result_li = await api.search_ae_obj_v3({ api_cfg, obj_type: 'event_presenter', search_query, order_by_li, params, view, limit, offset, log_lvl });
|
||||
const result_li = await api.search_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_presenter',
|
||||
search_query,
|
||||
order_by_li,
|
||||
params,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
log_lvl
|
||||
});
|
||||
if (result_li) {
|
||||
const processed = await process_ae_obj__event_presenter_props({ obj_li: result_li, log_lvl });
|
||||
const processed = await process_ae_obj__event_presenter_props({
|
||||
obj_li: result_li,
|
||||
log_lvl
|
||||
});
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'presenter', obj_li: processed, properties_to_save, log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'presenter',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
@@ -400,8 +580,16 @@ export async function email_sign_in__event_presenter({
|
||||
session_name?: string | null;
|
||||
presentation_name?: string | null;
|
||||
}) {
|
||||
if (!to_email || !person_id || !person_passcode || !event_id || !event_presenter_id) {
|
||||
console.error('Missing required parameters for email_sign_in__event_presenter');
|
||||
if (
|
||||
!to_email ||
|
||||
!person_id ||
|
||||
!person_passcode ||
|
||||
!event_id ||
|
||||
!event_presenter_id
|
||||
) {
|
||||
console.error(
|
||||
'Missing required parameters for email_sign_in__event_presenter'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
const subject = `Pres Mgmt Hub Sign In Link for Presenter: ${to_name ?? 'Presenter'}`;
|
||||
@@ -420,10 +608,75 @@ export async function email_sign_in__event_presenter({
|
||||
}
|
||||
|
||||
export const properties_to_save = [
|
||||
'id', 'event_presenter_id', 'event_presenter_id_random', 'external_id', 'code', 'event_id', 'event_session_id', 'event_presentation_id', 'event_person_id', 'person_id', 'person_profile_id', 'person_id_random', 'person_profile_id_random', 'pronouns', 'informal_name', 'title_names', 'given_name', 'middle_name', 'family_name', 'designations', 'professional_title', 'full_name', 'affiliations', 'email', 'biography', 'agree', 'comments', 'passcode', 'hide_event_launcher', 'data_json', 'enable', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on', 'tmp_sort_1', 'tmp_sort_2', 'file_count', 'event_session_code', 'event_session_name', 'event_session_start_datetime', 'event_presentation_code', 'event_presentation_name', 'event_presentation_start_datetime', 'person_external_id', 'person_external_sys_id', 'person_given_name', 'person_family_name', 'person_full_name', 'person_professional_title', 'person_affiliations', 'person_primary_email', 'person_passcode'
|
||||
'id',
|
||||
'event_presenter_id',
|
||||
'event_presenter_id_random',
|
||||
'external_id',
|
||||
'code',
|
||||
'event_id',
|
||||
'event_session_id',
|
||||
'event_presentation_id',
|
||||
'event_person_id',
|
||||
'person_id',
|
||||
'person_profile_id',
|
||||
'person_id_random',
|
||||
'person_profile_id_random',
|
||||
'pronouns',
|
||||
'informal_name',
|
||||
'title_names',
|
||||
'given_name',
|
||||
'middle_name',
|
||||
'family_name',
|
||||
'designations',
|
||||
'professional_title',
|
||||
'full_name',
|
||||
'affiliations',
|
||||
'email',
|
||||
'biography',
|
||||
'agree',
|
||||
'comments',
|
||||
'passcode',
|
||||
'hide_event_launcher',
|
||||
'data_json',
|
||||
'enable',
|
||||
'hide',
|
||||
'priority',
|
||||
'sort',
|
||||
'group',
|
||||
'notes',
|
||||
'created_on',
|
||||
'updated_on',
|
||||
'tmp_sort_1',
|
||||
'tmp_sort_2',
|
||||
'file_count',
|
||||
'event_session_code',
|
||||
'event_session_name',
|
||||
'event_session_start_datetime',
|
||||
'event_presentation_code',
|
||||
'event_presentation_name',
|
||||
'event_presentation_start_datetime',
|
||||
'person_external_id',
|
||||
'person_external_sys_id',
|
||||
'person_given_name',
|
||||
'person_family_name',
|
||||
'person_full_name',
|
||||
'person_professional_title',
|
||||
'person_affiliations',
|
||||
'person_primary_email',
|
||||
'person_passcode'
|
||||
];
|
||||
|
||||
async function _process_generic_props<T extends Record<string, any>>({ obj_li, obj_type, log_lvl = 0, specific_processor }: { obj_li: T[]; obj_type: string; log_lvl?: number; specific_processor?: (obj: T) => Promise<T> | T; }): Promise<T[]> {
|
||||
async function _process_generic_props<T extends Record<string, any>>({
|
||||
obj_li,
|
||||
obj_type,
|
||||
log_lvl = 0,
|
||||
specific_processor
|
||||
}: {
|
||||
obj_li: T[];
|
||||
obj_type: string;
|
||||
log_lvl?: number;
|
||||
specific_processor?: (obj: T) => Promise<T> | T;
|
||||
}): Promise<T[]> {
|
||||
if (!obj_li || obj_li.length === 0) return [];
|
||||
const processed_obj_li: T[] = [];
|
||||
for (const original_obj of obj_li) {
|
||||
@@ -439,28 +692,50 @@ async function _process_generic_props<T extends Record<string, any>>({ obj_li, o
|
||||
if (processed_obj[randomIdKey]) {
|
||||
(processed_obj as any).id = processed_obj[randomIdKey];
|
||||
(processed_obj as any)[baseIdKey] = processed_obj[randomIdKey];
|
||||
}
|
||||
else if (processed_obj[baseIdKey]) (processed_obj as any).id = processed_obj[baseIdKey];
|
||||
} else if (processed_obj[baseIdKey])
|
||||
(processed_obj as any).id = processed_obj[baseIdKey];
|
||||
const group = processed_obj.group ?? '0';
|
||||
const priority = processed_obj.priority ? 1 : 0;
|
||||
const sort = processed_obj.sort ?? '0';
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on ?? new Date(0).toISOString();
|
||||
const updated =
|
||||
processed_obj.updated_on ??
|
||||
processed_obj.created_on ??
|
||||
new Date(0).toISOString();
|
||||
const name = processed_obj.name ?? '';
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
if (specific_processor) processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
if (specific_processor)
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
}
|
||||
return processed_obj_li;
|
||||
}
|
||||
|
||||
export async function process_ae_obj__event_presenter_props({ obj_li, log_lvl = 0 }: { obj_li: any[]; log_lvl?: number; }) {
|
||||
return _process_generic_props({ obj_li, obj_type: 'event_presenter', log_lvl, specific_processor: (obj) => {
|
||||
// String-Only ID Vision: Ensure linking IDs are the string versions for indexing
|
||||
if (obj.event_presenter_id_random) obj.event_presenter_id = obj.event_presenter_id_random;
|
||||
if (obj.event_presentation_id_random) obj.event_presentation_id = obj.event_presentation_id_random;
|
||||
if (obj.event_session_id_random) obj.event_session_id = obj.event_session_id_random;
|
||||
if (obj.event_id_random) obj.event_id = obj.event_id_random;
|
||||
return obj;
|
||||
}});
|
||||
export async function process_ae_obj__event_presenter_props({
|
||||
obj_li,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
obj_li: any[];
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
return _process_generic_props({
|
||||
obj_li,
|
||||
obj_type: 'event_presenter',
|
||||
log_lvl,
|
||||
specific_processor: (obj) => {
|
||||
// String-Only ID Vision: Ensure linking IDs are the string versions for indexing
|
||||
if (obj.event_presenter_id_random)
|
||||
obj.event_presenter_id = obj.event_presenter_id_random;
|
||||
if (obj.event_presentation_id_random)
|
||||
obj.event_presentation_id = obj.event_presentation_id_random;
|
||||
if (obj.event_session_id_random)
|
||||
obj.event_session_id = obj.event_session_id_random;
|
||||
if (obj.event_id_random) obj.event_id = obj.event_id_random;
|
||||
return obj;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -44,7 +44,9 @@ export async function load_ae_obj_id__event_session({
|
||||
}): Promise<ae_EventSession | null> {
|
||||
const start_time = performance.now();
|
||||
if (log_lvl) {
|
||||
console.log(`🔎 [Trace] load_ae_obj_id__event_session: START (id=${event_session_id}, try_cache=${try_cache})`);
|
||||
console.log(
|
||||
`🔎 [Trace] load_ae_obj_id__event_session: START (id=${event_session_id}, try_cache=${try_cache})`
|
||||
);
|
||||
}
|
||||
|
||||
// Hierarchy Enforcement: Pulling presenters requires pulling presentations first
|
||||
@@ -56,65 +58,167 @@ export async function load_ae_obj_id__event_session({
|
||||
const cached = await db_events.session.get(event_session_id);
|
||||
if (cached) {
|
||||
const elapsed = (performance.now() - start_time).toFixed(2);
|
||||
if (log_lvl) console.log(`✅ [Trace] load_ae_obj_id: CACHE HIT at ${elapsed}ms. Returning stale shell for id=${event_session_id}`);
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`✅ [Trace] load_ae_obj_id: CACHE HIT at ${elapsed}ms. Returning stale shell for id=${event_session_id}`
|
||||
);
|
||||
|
||||
// Background tasks: refresh parent and warm child caches (non-blocking)
|
||||
_refresh_session_id_background({
|
||||
api_cfg, event_session_id, view, try_cache,
|
||||
inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li,
|
||||
enabled, hidden, limit, offset, log_lvl: log_lvl > 1 ? log_lvl : 0
|
||||
api_cfg,
|
||||
event_session_id,
|
||||
view,
|
||||
try_cache,
|
||||
inc_file_li,
|
||||
inc_all_file_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
log_lvl: log_lvl > 1 ? log_lvl : 0
|
||||
});
|
||||
|
||||
// In SWR mode, we fire child loads in background to warm IDB for the view's LiveQueries
|
||||
_handle_nested_loads(cached, { api_cfg, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl: 0 });
|
||||
_handle_nested_loads(cached, {
|
||||
api_cfg,
|
||||
inc_file_li,
|
||||
inc_all_file_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
try_cache,
|
||||
log_lvl: 0
|
||||
});
|
||||
|
||||
return cached; // Return immediately without awaiting nested loads
|
||||
} else if (log_lvl) {
|
||||
console.log(`⏳ [Trace] load_ae_obj_id: CACHE MISS at ${(performance.now() - start_time).toFixed(2)}ms for id=${event_session_id}`);
|
||||
console.log(
|
||||
`⏳ [Trace] load_ae_obj_id: CACHE MISS at ${(performance.now() - start_time).toFixed(2)}ms for id=${event_session_id}`
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (log_lvl) console.error(`❌ [Trace] load_ae_obj_id: Cache access error:`, e);
|
||||
if (log_lvl)
|
||||
console.error(
|
||||
`❌ [Trace] load_ae_obj_id: Cache access error:`,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. SLOW PATH: Wait for API
|
||||
if (log_lvl) console.log(`🚀 [Trace] load_ae_obj_id: Proceeding to API path for id=${event_session_id}`);
|
||||
return await _refresh_session_id_background({ api_cfg, event_session_id, view, try_cache, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, log_lvl });
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`🚀 [Trace] load_ae_obj_id: Proceeding to API path for id=${event_session_id}`
|
||||
);
|
||||
return await _refresh_session_id_background({
|
||||
api_cfg,
|
||||
event_session_id,
|
||||
view,
|
||||
try_cache,
|
||||
inc_file_li,
|
||||
inc_all_file_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal background refresh for a single session
|
||||
*/
|
||||
async function _refresh_session_id_background({ api_cfg, event_session_id, view, try_cache, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, log_lvl }: any) {
|
||||
async function _refresh_session_id_background({
|
||||
api_cfg,
|
||||
event_session_id,
|
||||
view,
|
||||
try_cache,
|
||||
inc_file_li,
|
||||
inc_all_file_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
log_lvl
|
||||
}: any) {
|
||||
const start_time = performance.now();
|
||||
if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
|
||||
try {
|
||||
if (log_lvl) console.log(`📡 [Trace] _refresh_session_id: API Fetching id=${event_session_id}`);
|
||||
const result = await api.get_ae_obj_v3({ api_cfg, obj_type: 'event_session', obj_id: event_session_id, view, log_lvl });
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`📡 [Trace] _refresh_session_id: API Fetching id=${event_session_id}`
|
||||
);
|
||||
const result = await api.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_session',
|
||||
obj_id: event_session_id,
|
||||
view,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_session_props({ obj_li: [result], log_lvl });
|
||||
const processed = await process_ae_obj__event_session_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj = processed[0];
|
||||
const elapsed = (performance.now() - start_time).toFixed(2);
|
||||
|
||||
if (log_lvl) console.log(`📦 [Trace] _refresh_session_id: Received from API at ${elapsed}ms (id=${processed_obj.id})`);
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`📦 [Trace] _refresh_session_id: Received from API at ${elapsed}ms (id=${processed_obj.id})`
|
||||
);
|
||||
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'session', obj_li: [processed_obj], properties_to_save, log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'session',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
// CRITICAL FIX (2026-02-26): Yield to microtask queue so Dexie liveQuery observers
|
||||
// fire before we return. Without this, component-mounted liveQueries may subscribe
|
||||
// to IDB *before* the write completes, causing empty results on cold-start.
|
||||
await Promise.resolve();
|
||||
if (log_lvl) console.log(`💾 [Trace] _refresh_session_id: Saved to IDB cache.`);
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`💾 [Trace] _refresh_session_id: Saved to IDB cache.`
|
||||
);
|
||||
}
|
||||
// CRITICAL FIX (2026-02-26): Preserve parent's try_cache value when loading nested data.
|
||||
// Previously set to `false`, which meant presentations/presenters were fetched from API
|
||||
// but NEVER written to IndexedDB, causing "refresh twice" bug on cold-start.
|
||||
// Now nested loads inherit parent's caching behavior for deterministic first-render.
|
||||
return await _handle_nested_loads(processed_obj, { api_cfg, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl });
|
||||
return await _handle_nested_loads(processed_obj, {
|
||||
api_cfg,
|
||||
inc_file_li,
|
||||
inc_all_file_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
try_cache,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
if (log_lvl) console.error(`❌ [Trace] _refresh_session_id: API error for id=${event_session_id}:`, e);
|
||||
if (log_lvl)
|
||||
console.error(
|
||||
`❌ [Trace] _refresh_session_id: API error for id=${event_session_id}:`,
|
||||
e
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -122,29 +226,65 @@ async function _refresh_session_id_background({ api_cfg, event_session_id, view,
|
||||
/**
|
||||
* Helper to handle nested collection loads for a session
|
||||
*/
|
||||
async function _handle_nested_loads(session_obj: any, { api_cfg, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl }: any) {
|
||||
async function _handle_nested_loads(
|
||||
session_obj: any,
|
||||
{
|
||||
api_cfg,
|
||||
inc_file_li,
|
||||
inc_all_file_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}: any
|
||||
) {
|
||||
const start_time = performance.now();
|
||||
const current_session_id = session_obj.id || session_obj.event_session_id;
|
||||
if (!current_session_id) return session_obj;
|
||||
|
||||
const tasks = [];
|
||||
if (inc_file_li) {
|
||||
tasks.push(load_ae_obj_li__event_file({
|
||||
api_cfg, for_obj_type: 'event_session', for_obj_id: current_session_id,
|
||||
enabled, limit: 15, try_cache, log_lvl
|
||||
}).then(res => session_obj.event_file_li = res));
|
||||
tasks.push(
|
||||
load_ae_obj_li__event_file({
|
||||
api_cfg,
|
||||
for_obj_type: 'event_session',
|
||||
for_obj_id: current_session_id,
|
||||
enabled,
|
||||
limit: 15,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}).then((res) => (session_obj.event_file_li = res))
|
||||
);
|
||||
}
|
||||
|
||||
if (inc_presentation_li) {
|
||||
tasks.push(load_ae_obj_li__event_presentation({
|
||||
api_cfg, for_obj_type: 'event_session', for_obj_id: current_session_id,
|
||||
inc_file_li: inc_all_file_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl
|
||||
}).then(res => session_obj.event_presentation_li = res));
|
||||
tasks.push(
|
||||
load_ae_obj_li__event_presentation({
|
||||
api_cfg,
|
||||
for_obj_type: 'event_session',
|
||||
for_obj_id: current_session_id,
|
||||
inc_file_li: inc_all_file_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}).then((res) => (session_obj.event_presentation_li = res))
|
||||
);
|
||||
}
|
||||
|
||||
if (tasks.length > 0) {
|
||||
await Promise.all(tasks);
|
||||
if (log_lvl) console.log(`🔗 [Trace] _handle_nested_loads: Finished child collections in ${(performance.now() - start_time).toFixed(2)}ms`);
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`🔗 [Trace] _handle_nested_loads: Finished child collections in ${(performance.now() - start_time).toFixed(2)}ms`
|
||||
);
|
||||
}
|
||||
return session_obj;
|
||||
}
|
||||
@@ -191,7 +331,9 @@ export async function load_ae_obj_li__event_session({
|
||||
}): Promise<ae_EventSession[]> {
|
||||
const start_time = performance.now();
|
||||
if (log_lvl) {
|
||||
console.log(`🔎 [Trace] load_ae_obj_li__event_session: START (for=${for_obj_type}:${for_obj_id}, try_cache=${try_cache})`);
|
||||
console.log(
|
||||
`🔎 [Trace] load_ae_obj_li__event_session: START (for=${for_obj_type}:${for_obj_id}, try_cache=${try_cache})`
|
||||
);
|
||||
}
|
||||
|
||||
// Hierarchy Enforcement: Pulling presenters requires pulling presentations first
|
||||
@@ -201,226 +343,409 @@ export async function load_ae_obj_li__event_session({
|
||||
try {
|
||||
// Robust lookup logic
|
||||
let query;
|
||||
if (for_obj_type === 'event_location') query = db_events.session.where('event_location_id').equals(for_obj_id);
|
||||
else if (for_obj_type === 'event') query = db_events.session.where('event_id').equals(for_obj_id);
|
||||
if (for_obj_type === 'event_location')
|
||||
query = db_events.session
|
||||
.where('event_location_id')
|
||||
.equals(for_obj_id);
|
||||
else if (for_obj_type === 'event')
|
||||
query = db_events.session.where('event_id').equals(for_obj_id);
|
||||
else query = db_events.session.where('for_id').equals(for_obj_id);
|
||||
|
||||
const cached_li = await query.toArray();
|
||||
|
||||
if (cached_li && cached_li.length > 0) {
|
||||
const elapsed = (performance.now() - start_time).toFixed(2);
|
||||
if (log_lvl) console.log(`✅ [Trace] load_ae_obj_li: CACHE HIT at ${elapsed}ms (${cached_li.length} items).`);
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`✅ [Trace] load_ae_obj_li: CACHE HIT at ${elapsed}ms (${cached_li.length} items).`
|
||||
);
|
||||
|
||||
// Background refresh (non-blocking)
|
||||
_refresh_session_li_background({
|
||||
api_cfg, for_obj_type, for_obj_id, view,
|
||||
api_cfg,
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
view,
|
||||
// Optimization: Disable nested loads for list members to prevent request storms
|
||||
inc_file_li: false, inc_all_file_li: false, inc_presentation_li: false, inc_presenter_li: false,
|
||||
enabled, hidden, limit, offset, order_by_li, try_cache,
|
||||
inc_file_li: false,
|
||||
inc_all_file_li: false,
|
||||
inc_presentation_li: false,
|
||||
inc_presenter_li: false,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
try_cache,
|
||||
log_lvl: log_lvl > 1 ? log_lvl : 0
|
||||
});
|
||||
|
||||
return cached_li;
|
||||
} else if (log_lvl) {
|
||||
console.log(`⏳ [Trace] load_ae_obj_li: CACHE MISS at ${(performance.now() - start_time).toFixed(2)}ms for type=${for_obj_type} id=${for_obj_id}`);
|
||||
console.log(
|
||||
`⏳ [Trace] load_ae_obj_li: CACHE MISS at ${(performance.now() - start_time).toFixed(2)}ms for type=${for_obj_type} id=${for_obj_id}`
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (log_lvl) console.error(`❌ [Trace] load_ae_obj_li: Cache access error:`, e);
|
||||
if (log_lvl)
|
||||
console.error(
|
||||
`❌ [Trace] load_ae_obj_li: Cache access error:`,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return await _refresh_session_li_background({ api_cfg, for_obj_type, for_obj_id, view, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, order_by_li, try_cache, log_lvl });
|
||||
return await _refresh_session_li_background({
|
||||
api_cfg,
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
view,
|
||||
inc_file_li,
|
||||
inc_all_file_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
try_cache,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
async function _refresh_session_li_background({ api_cfg, for_obj_type, for_obj_id, view, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, order_by_li, try_cache, log_lvl }: any) {
|
||||
async function _refresh_session_li_background({
|
||||
api_cfg,
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
view,
|
||||
inc_file_li,
|
||||
inc_all_file_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
try_cache,
|
||||
log_lvl
|
||||
}: any) {
|
||||
const start_time = performance.now();
|
||||
if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
|
||||
try {
|
||||
if (log_lvl) console.log(`📡 [Trace] _refresh_session_li: API Fetching for=${for_obj_type}:${for_obj_id} (view=${view})`);
|
||||
const result_li = await api.get_ae_obj_li_v3({ api_cfg, obj_type: 'event_session', for_obj_type, for_obj_id, view, enabled, hidden, limit, offset, order_by_li, log_lvl });
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`📡 [Trace] _refresh_session_li: API Fetching for=${for_obj_type}:${for_obj_id} (view=${view})`
|
||||
);
|
||||
const result_li = await api.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'event_session',
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
view,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (result_li) {
|
||||
const processed = await process_ae_obj__event_session_props({ obj_li: result_li, log_lvl });
|
||||
const processed = await process_ae_obj__event_session_props({
|
||||
obj_li: result_li,
|
||||
log_lvl
|
||||
});
|
||||
const elapsed = (performance.now() - start_time).toFixed(2);
|
||||
if (log_lvl) console.log(`📦 [Trace] _refresh_session_li: Received ${processed.length} items from API at ${elapsed}ms.`);
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`📦 [Trace] _refresh_session_li: Received ${processed.length} items from API at ${elapsed}ms.`
|
||||
);
|
||||
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'session', obj_li: processed, properties_to_save, log_lvl });
|
||||
if (log_lvl) console.log(`💾 [Trace] _refresh_session_li: Saved to IDB cache.`);
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'session',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`💾 [Trace] _refresh_session_li: Saved to IDB cache.`
|
||||
);
|
||||
}
|
||||
|
||||
// Fire nested loads for each session only if explicitly requested (usually only for single objects)
|
||||
if (inc_file_li || inc_presentation_li) {
|
||||
processed.forEach(s => {
|
||||
_handle_nested_loads(s, { api_cfg, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl: 0 });
|
||||
processed.forEach((s) => {
|
||||
_handle_nested_loads(s, {
|
||||
api_cfg,
|
||||
inc_file_li,
|
||||
inc_all_file_li,
|
||||
inc_presentation_li,
|
||||
inc_presenter_li,
|
||||
enabled,
|
||||
hidden,
|
||||
limit,
|
||||
offset,
|
||||
try_cache,
|
||||
log_lvl: 0
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return processed;
|
||||
}
|
||||
} catch (e) {
|
||||
if (log_lvl) console.error(`❌ [Trace] _refresh_session_li: API error:`, e);
|
||||
if (log_lvl)
|
||||
console.error(`❌ [Trace] _refresh_session_li: API error:`, e);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export async function create_ae_obj__event_session({
|
||||
api_cfg,
|
||||
event_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
api_cfg,
|
||||
event_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
event_id?: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_id?: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventSession | null> {
|
||||
if (!event_id) event_id = get(slct).event_id;
|
||||
if (!event_id) {
|
||||
console.error('create_ae_obj__event_session: event_id is required');
|
||||
return null;
|
||||
}
|
||||
const result = await api.create_nested_obj_v3({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_session',
|
||||
fields: { ...data_kv },
|
||||
log_lvl
|
||||
});
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_session_props({ obj_li: [result], log_lvl });
|
||||
const processed_obj = processed[0];
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'session',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
return null;
|
||||
const result = await api.create_nested_obj({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_session',
|
||||
fields: { ...data_kv },
|
||||
log_lvl
|
||||
});
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_session_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj = processed[0];
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'session',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function delete_ae_obj_id__event_session({
|
||||
api_cfg,
|
||||
event_id,
|
||||
event_session_id,
|
||||
method = 'delete',
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
api_cfg,
|
||||
event_id,
|
||||
event_session_id,
|
||||
method = 'delete',
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
event_id?: string;
|
||||
event_session_id: string;
|
||||
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_id?: string;
|
||||
event_session_id: string;
|
||||
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (!event_id) event_id = get(slct).event_id;
|
||||
if (!event_id) {
|
||||
console.error('delete_ae_obj_id__event_session: event_id is required');
|
||||
return null;
|
||||
}
|
||||
const result = await api.delete_nested_ae_obj_v3({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_session',
|
||||
obj_id: event_session_id,
|
||||
method,
|
||||
log_lvl
|
||||
});
|
||||
if (try_cache) await db_events.session.delete(event_session_id);
|
||||
return result;
|
||||
const result = await api.delete_nested_ae_obj({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_session',
|
||||
obj_id: event_session_id,
|
||||
method,
|
||||
log_lvl
|
||||
});
|
||||
if (try_cache) await db_events.session.delete(event_session_id);
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function update_ae_obj__event_session({
|
||||
api_cfg,
|
||||
event_id,
|
||||
event_session_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
api_cfg,
|
||||
event_id,
|
||||
event_session_id,
|
||||
data_kv,
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
event_id?: string;
|
||||
event_session_id: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_id?: string;
|
||||
event_session_id: string;
|
||||
data_kv: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventSession | null> {
|
||||
if (!event_id) event_id = get(slct).event_id;
|
||||
if (!event_id) {
|
||||
console.error('update_ae_obj__event_session: event_id is required');
|
||||
return null;
|
||||
}
|
||||
const result = await api.update_nested_obj_v3({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_session',
|
||||
obj_id: event_session_id,
|
||||
fields: data_kv,
|
||||
log_lvl
|
||||
});
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_session_props({ obj_li: [result], log_lvl });
|
||||
const processed_obj = processed[0];
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'session',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
return null;
|
||||
const result = await api.update_nested_obj({
|
||||
api_cfg,
|
||||
for_obj_type: 'event',
|
||||
for_obj_id: event_id,
|
||||
obj_type: 'event_session',
|
||||
obj_id: event_session_id,
|
||||
fields: data_kv,
|
||||
log_lvl
|
||||
});
|
||||
if (result) {
|
||||
const processed = await process_ae_obj__event_session_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj = processed[0];
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'session',
|
||||
obj_li: [processed_obj],
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed_obj;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function search__event_session({
|
||||
api_cfg, event_id, fulltext_search_qry_str = '', ft_presenter_search_qry_str = '', like_search_qry_str = '', like_presentation_search_qry_str = '', like_presenter_search_qry_str = '', like_poc_name_qry_str = '', location_name = null, qry_files = null, qry_poc_agree = null, qry_poc_kv_json = null, qry_start_datetime = null, enabled = 'enabled', hidden = 'not_hidden', view = 'default', limit = 50, offset = 0, order_by_li = [{ sort: 'ASC' }, { start_datetime: 'ASC' }, { name: 'ASC' }], try_cache = true, log_lvl = 0
|
||||
api_cfg,
|
||||
event_id,
|
||||
fulltext_search_qry_str = '',
|
||||
ft_presenter_search_qry_str = '',
|
||||
like_search_qry_str = '',
|
||||
like_presentation_search_qry_str = '',
|
||||
like_presenter_search_qry_str = '',
|
||||
like_poc_name_qry_str = '',
|
||||
location_name = null,
|
||||
qry_files = null,
|
||||
qry_poc_agree = null,
|
||||
qry_poc_kv_json = null,
|
||||
qry_start_datetime = null,
|
||||
enabled = 'enabled',
|
||||
hidden = 'not_hidden',
|
||||
view = 'default',
|
||||
limit = 50,
|
||||
offset = 0,
|
||||
order_by_li = [{ sort: 'ASC' }, { start_datetime: 'ASC' }, { name: 'ASC' }],
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any; event_id: string; fulltext_search_qry_str?: string; ft_presenter_search_qry_str?: string | null; like_search_qry_str?: string; like_presentation_search_qry_str?: string; like_presenter_search_qry_str?: string; like_poc_name_qry_str?: string; location_name?: null | string; qry_files?: null | boolean; qry_poc_agree?: null | boolean; qry_poc_kv_json?: null | boolean; qry_start_datetime?: string | null; enabled?: 'enabled' | 'all' | 'not_enabled'; hidden?: 'hidden' | 'all' | 'not_hidden'; view?: string; limit?: number; offset?: number; order_by_li?: any; try_cache?: boolean; log_lvl?: number;
|
||||
api_cfg: any;
|
||||
event_id: string;
|
||||
fulltext_search_qry_str?: string;
|
||||
ft_presenter_search_qry_str?: string | null;
|
||||
like_search_qry_str?: string;
|
||||
like_presentation_search_qry_str?: string;
|
||||
like_presenter_search_qry_str?: string;
|
||||
like_poc_name_qry_str?: string;
|
||||
location_name?: null | string;
|
||||
qry_files?: null | boolean;
|
||||
qry_poc_agree?: null | boolean;
|
||||
qry_poc_kv_json?: null | boolean;
|
||||
qry_start_datetime?: string | null;
|
||||
enabled?: 'enabled' | 'all' | 'not_enabled';
|
||||
hidden?: 'hidden' | 'all' | 'not_hidden';
|
||||
view?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
order_by_li?: any;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_EventSession[]> {
|
||||
const search_query: any = { q: '', and: [{ field: 'event_id', op: 'eq', value: event_id }] };
|
||||
const search_query: any = {
|
||||
q: '',
|
||||
and: [{ field: 'event_id', op: 'eq', value: event_id }]
|
||||
};
|
||||
if (fulltext_search_qry_str || ft_presenter_search_qry_str) {
|
||||
const ft: any = {};
|
||||
if (fulltext_search_qry_str && fulltext_search_qry_str.length > 2) ft['default_qry_str'] = fulltext_search_qry_str;
|
||||
if (ft_presenter_search_qry_str && ft_presenter_search_qry_str.length > 2) ft['event_presenter_li_qry_str'] = ft_presenter_search_qry_str;
|
||||
if (fulltext_search_qry_str && fulltext_search_qry_str.length > 2)
|
||||
ft['default_qry_str'] = fulltext_search_qry_str;
|
||||
if (
|
||||
ft_presenter_search_qry_str &&
|
||||
ft_presenter_search_qry_str.length > 2
|
||||
)
|
||||
ft['event_presenter_li_qry_str'] = ft_presenter_search_qry_str;
|
||||
if (Object.keys(ft).length) search_query.params = { ft_qry: ft };
|
||||
}
|
||||
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
|
||||
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 0 });
|
||||
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 1 });
|
||||
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 0 });
|
||||
if (enabled === 'enabled')
|
||||
search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
|
||||
else if (enabled === 'not_enabled')
|
||||
search_query.and.push({ field: 'enable', op: 'eq', value: 0 });
|
||||
if (hidden === 'hidden')
|
||||
search_query.and.push({ field: 'hide', op: 'eq', value: 1 });
|
||||
else if (hidden === 'not_hidden')
|
||||
search_query.and.push({ field: 'hide', op: 'eq', value: 0 });
|
||||
|
||||
if (location_name) {
|
||||
search_query.and.push({ field: 'event_location_name', op: 'eq', value: location_name });
|
||||
search_query.and.push({
|
||||
field: 'event_location_name',
|
||||
op: 'eq',
|
||||
value: location_name
|
||||
});
|
||||
}
|
||||
|
||||
const result_li = await api.search_ae_obj_v3({ api_cfg, obj_type: 'event_session', search_query, order_by_li, view, limit, offset, log_lvl });
|
||||
const result_li = await api.search_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'event_session',
|
||||
search_query,
|
||||
order_by_li,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
// Handle V3 API envelope
|
||||
let valid_result_li: ae_EventSession[] = [];
|
||||
if (Array.isArray(result_li)) {
|
||||
valid_result_li = result_li;
|
||||
} else if (result_li && typeof result_li === 'object' && Array.isArray((result_li as any).data)) {
|
||||
} else if (
|
||||
result_li &&
|
||||
typeof result_li === 'object' &&
|
||||
Array.isArray((result_li as any).data)
|
||||
) {
|
||||
valid_result_li = (result_li as any).data;
|
||||
}
|
||||
|
||||
if (valid_result_li && valid_result_li.length > 0) {
|
||||
const processed = await process_ae_obj__event_session_props({ obj_li: valid_result_li, log_lvl });
|
||||
const processed = await process_ae_obj__event_session_props({
|
||||
obj_li: valid_result_li,
|
||||
log_lvl
|
||||
});
|
||||
if (try_cache) {
|
||||
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'session', obj_li: processed, properties_to_save, log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_events,
|
||||
table_name: 'session',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
@@ -429,18 +754,103 @@ export async function search__event_session({
|
||||
|
||||
export const qry__event_session = search__event_session;
|
||||
|
||||
export async function email_sign_in__event_session({ api_cfg, to_email, to_name, base_url, person_id, person_passcode, event_id, event_session_id, session_name }: { api_cfg: any; to_email: string; to_name: string; base_url: string; person_id: string; person_passcode: string; event_id: string; event_session_id: string; session_name: string; }) {
|
||||
export async function email_sign_in__event_session({
|
||||
api_cfg,
|
||||
to_email,
|
||||
to_name,
|
||||
base_url,
|
||||
person_id,
|
||||
person_passcode,
|
||||
event_id,
|
||||
event_session_id,
|
||||
session_name
|
||||
}: {
|
||||
api_cfg: any;
|
||||
to_email: string;
|
||||
to_name: string;
|
||||
base_url: string;
|
||||
person_id: string;
|
||||
person_passcode: string;
|
||||
event_id: string;
|
||||
event_session_id: string;
|
||||
session_name: string;
|
||||
}) {
|
||||
const subject = `Pres Mgmt Hub Sign In Link for ${session_name}`;
|
||||
const sign_in_url = encodeURI(`${base_url}/events/${event_id}/session/${event_session_id}?person_id=${person_id}&person_pass=${person_passcode}`);
|
||||
const sign_in_url = encodeURI(
|
||||
`${base_url}/events/${event_id}/session/${event_session_id}?person_id=${person_id}&person_pass=${person_passcode}`
|
||||
);
|
||||
const body_html = `<div>${to_name},<p>Your sign-in link for ${session_name}: <a href="${sign_in_url}">${sign_in_url}</a></p></div>`;
|
||||
return await api.send_email({ api_cfg, from_email: 'noreply+presmgmt@oneskyit.com', from_name: 'Aether Pres Mgmt', to_email, subject, body_html });
|
||||
return await api.send_email({
|
||||
api_cfg,
|
||||
from_email: 'noreply+presmgmt@oneskyit.com',
|
||||
from_name: 'Aether Pres Mgmt',
|
||||
to_email,
|
||||
subject,
|
||||
body_html
|
||||
});
|
||||
}
|
||||
|
||||
export const properties_to_save = [
|
||||
'id', 'event_session_id', 'event_session_id_random', 'external_id', 'code', 'for_type', 'for_id', 'for_id_random', 'type_code', 'event_id', 'event_location_id', 'poc_person_id', 'poc_agree', 'poc_kv_json', 'name', 'description', 'start_datetime', 'end_datetime', 'passcode', 'hide_event_launcher', 'alert', 'alert_msg', 'data_json', 'ux_mode', 'enable', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on', 'tmp_sort_1', 'tmp_sort_2', 'file_count', 'file_count_all', 'internal_use_count', 'event_file_id_li_json', 'poc_person_given_name', 'poc_person_family_name', 'poc_person_full_name', 'poc_person_primary_email', 'poc_person_passcode', 'event_name', 'event_location_code', 'event_location_name', 'event_presentation_li'
|
||||
'id',
|
||||
'event_session_id',
|
||||
'event_session_id_random',
|
||||
'external_id',
|
||||
'code',
|
||||
'for_type',
|
||||
'for_id',
|
||||
'for_id_random',
|
||||
'type_code',
|
||||
'event_id',
|
||||
'event_location_id',
|
||||
'poc_person_id',
|
||||
'poc_agree',
|
||||
'poc_kv_json',
|
||||
'name',
|
||||
'description',
|
||||
'start_datetime',
|
||||
'end_datetime',
|
||||
'passcode',
|
||||
'hide_event_launcher',
|
||||
'alert',
|
||||
'alert_msg',
|
||||
'data_json',
|
||||
'ux_mode',
|
||||
'enable',
|
||||
'hide',
|
||||
'priority',
|
||||
'sort',
|
||||
'group',
|
||||
'notes',
|
||||
'created_on',
|
||||
'updated_on',
|
||||
'tmp_sort_1',
|
||||
'tmp_sort_2',
|
||||
'file_count',
|
||||
'file_count_all',
|
||||
'internal_use_count',
|
||||
'event_file_id_li_json',
|
||||
'poc_person_given_name',
|
||||
'poc_person_family_name',
|
||||
'poc_person_full_name',
|
||||
'poc_person_primary_email',
|
||||
'poc_person_passcode',
|
||||
'event_name',
|
||||
'event_location_code',
|
||||
'event_location_name',
|
||||
'event_presentation_li'
|
||||
];
|
||||
|
||||
async function _process_generic_props<T extends Record<string, any>>({ obj_li, obj_type, log_lvl = 0, specific_processor }: { obj_li: T[]; obj_type: string; log_lvl?: number; specific_processor?: (obj: T) => Promise<T> | T; }): Promise<T[]> {
|
||||
async function _process_generic_props<T extends Record<string, any>>({
|
||||
obj_li,
|
||||
obj_type,
|
||||
log_lvl = 0,
|
||||
specific_processor
|
||||
}: {
|
||||
obj_li: T[];
|
||||
obj_type: string;
|
||||
log_lvl?: number;
|
||||
specific_processor?: (obj: T) => Promise<T> | T;
|
||||
}): Promise<T[]> {
|
||||
if (!obj_li || obj_li.length === 0) return [];
|
||||
const processed_obj_li: T[] = [];
|
||||
for (const original_obj of obj_li) {
|
||||
@@ -456,22 +866,40 @@ async function _process_generic_props<T extends Record<string, any>>({ obj_li, o
|
||||
if (processed_obj[randomIdKey]) {
|
||||
(processed_obj as any).id = processed_obj[randomIdKey];
|
||||
(processed_obj as any)[baseIdKey] = processed_obj[randomIdKey];
|
||||
}
|
||||
else if (processed_obj[baseIdKey]) (processed_obj as any).id = processed_obj[baseIdKey];
|
||||
} else if (processed_obj[baseIdKey])
|
||||
(processed_obj as any).id = processed_obj[baseIdKey];
|
||||
|
||||
const group = processed_obj.group ?? '0';
|
||||
const priority = processed_obj.priority ? 1 : 0;
|
||||
const sort = processed_obj.sort ?? '0';
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on ?? new Date(0).toISOString();
|
||||
const updated =
|
||||
processed_obj.updated_on ??
|
||||
processed_obj.created_on ??
|
||||
new Date(0).toISOString();
|
||||
const name = processed_obj.name ?? '';
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
if (specific_processor) processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
if (specific_processor)
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
}
|
||||
return processed_obj_li;
|
||||
}
|
||||
|
||||
export async function process_ae_obj__event_session_props({ obj_li, log_lvl = 0 }: { obj_li: any[]; log_lvl?: number; }) {
|
||||
return _process_generic_props({ obj_li, obj_type: 'event_session', log_lvl });
|
||||
}
|
||||
export async function process_ae_obj__event_session_props({
|
||||
obj_li,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
obj_li: any[];
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
return _process_generic_props({
|
||||
obj_li,
|
||||
obj_type: 'event_session',
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user