Last round of prettier: npx prettier --write src/

This commit is contained in:
Scott Idem
2026-03-24 13:27:40 -04:00
parent 23d25bf65a
commit a8f3c29b9f
146 changed files with 13201 additions and 9277 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "osit-aether-app-svelte", "name": "osit-aether-app-svelte",
"version": "3.00.05", "version": "3.00.06",
"description": "One Sky IT's Aether App created with Svelte, SvelteKit, Tailwind CSS, Lucide, Font Awesome, and Skeleton UI. -Scott Idem", "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/", "homepage": "https://oneskyit.com/",
"private": true, "private": true,

View File

@@ -63,50 +63,50 @@ html[data-theme='AE_Firefly_Indigo'] {
/* --- Color ramps (light mode) copied from dark block so both modes have full ramps --- */ /* --- Color ramps (light mode) copied from dark block so both modes have full ramps --- */
html[data-theme='AE_Firefly_Indigo'] { 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-100: oklch(89.5% 0.072 270deg);
--color-primary-200: oklch(82.5% 0.108 269deg); --color-primary-200: oklch(82.5% 0.108 269deg);
--color-primary-300: oklch(74.5% 0.135 268deg); --color-primary-300: oklch(74.5% 0.135 268deg);
--color-primary-400: oklch(65.0% 0.155 267deg); --color-primary-400: oklch(65% 0.155 267deg);
--color-primary-500: oklch(50.5% 0.160 266deg); --color-primary-500: oklch(50.5% 0.16 266deg);
--color-primary-600: oklch(43.5% 0.152 265deg); --color-primary-600: oklch(43.5% 0.152 265deg);
--color-primary-700: oklch(37.0% 0.138 264deg); --color-primary-700: oklch(37% 0.138 264deg);
--color-primary-800: oklch(30.0% 0.120 263deg); --color-primary-800: oklch(30% 0.12 263deg);
--color-primary-900: oklch(23.0% 0.100 262deg); --color-primary-900: oklch(23% 0.1 262deg);
--color-primary-950: oklch(15.5% 0.080 261deg); --color-primary-950: oklch(15.5% 0.08 261deg);
--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-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-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-300: oklch(78.5% 0.115 292deg);
--color-secondary-400: oklch(70.0% 0.132 291deg); --color-secondary-400: oklch(70% 0.132 291deg);
--color-secondary-500: oklch(60.0% 0.140 290deg); --color-secondary-500: oklch(60% 0.14 290deg);
--color-secondary-600: oklch(52.5% 0.135 289deg); --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-800: oklch(37.5% 0.112 286deg);
--color-secondary-900: oklch(30.0% 0.094 284deg); --color-secondary-900: oklch(30% 0.094 284deg);
--color-secondary-950: oklch(22.0% 0.076 282deg); --color-secondary-950: oklch(22% 0.076 282deg);
--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-light: var(--color-secondary-50);
--color-tertiary-50: oklch(96.5% 0.022 348deg); --color-tertiary-50: oklch(96.5% 0.022 348deg);
--color-tertiary-100: oklch(91.0% 0.042 346deg); --color-tertiary-100: oklch(91% 0.042 346deg);
--color-tertiary-200: oklch(84.5% 0.068 344deg); --color-tertiary-200: oklch(84.5% 0.068 344deg);
--color-tertiary-300: oklch(76.5% 0.095 343deg); --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-500: oklch(57.5% 0.128 341deg);
--color-tertiary-600: oklch(50.0% 0.122 340deg); --color-tertiary-600: oklch(50% 0.122 340deg);
--color-tertiary-700: oklch(43.0% 0.112 339deg); --color-tertiary-700: oklch(43% 0.112 339deg);
--color-tertiary-800: oklch(35.5% 0.098 338deg); --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-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-light: var(--color-tertiary-50);
--color-success-50: oklch(95.77% 0.05 152.69deg); --color-success-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152.00deg); --color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg); --color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg); --color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg); --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-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg); --color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg); --color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.20% 0.06 151.12deg); --color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.20% 0.04 151.44deg); --color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950); --color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50); --color-success-contrast-light: var(--color-success-50);
--color-warning-50: oklch(97.5% 0.065 78deg); --color-warning-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.090 75deg); --color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.120 73deg); --color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg); --color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.160 67deg); --color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77.0% 0.165 65deg); --color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg); --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-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45.0% 0.105 61deg); --color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37.0% 0.088 60deg); --color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950); --color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50); --color-warning-contrast-light: var(--color-warning-50);
--color-error-50: oklch(95.0% 0.040 18deg); --color-error-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88.0% 0.070 20deg); --color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80.0% 0.105 21deg); --color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72.0% 0.140 22deg); --color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.170 23deg); --color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg); --color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg); --color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg); --color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg); --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-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-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-100: oklch(96.5% 0.006 268deg);
--color-surface-200: oklch(92.5% 0.010 266deg); --color-surface-200: oklch(92.5% 0.01 266deg);
--color-surface-300: oklch(87.0% 0.014 265deg); --color-surface-300: oklch(87% 0.014 265deg);
--color-surface-400: oklch(78.5% 0.018 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-600: oklch(54.5% 0.022 269deg);
--color-surface-700: oklch(42.5% 0.024 270deg); --color-surface-700: oklch(42.5% 0.024 270deg);
--color-surface-800: oklch(31.0% 0.026 272deg); --color-surface-800: oklch(31% 0.026 272deg);
--color-surface-900: oklch(20.5% 0.030 274deg); --color-surface-900: oklch(20.5% 0.03 274deg);
--color-surface-950: oklch(13.0% 0.034 276deg); --color-surface-950: oklch(13% 0.034 276deg);
--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-light: var(--color-surface-50);
} }
@@ -182,20 +182,20 @@ html.dark[data-theme='AE_Firefly_Indigo'] {
* maintaining sufficient contrast at mid-range shades. * maintaining sufficient contrast at mid-range shades.
* At 500 (L≈50%): sufficient contrast with primary-50 text (≥4:1). * 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-100: oklch(89.5% 0.072 270deg);
--color-primary-200: oklch(82.5% 0.108 269deg); --color-primary-200: oklch(82.5% 0.108 269deg);
--color-primary-300: oklch(74.5% 0.135 268deg); --color-primary-300: oklch(74.5% 0.135 268deg);
--color-primary-400: oklch(65.0% 0.155 267deg); --color-primary-400: oklch(65% 0.155 267deg);
--color-primary-500: oklch(50.5% 0.160 266deg); --color-primary-500: oklch(50.5% 0.16 266deg);
--color-primary-600: oklch(43.5% 0.152 265deg); --color-primary-600: oklch(43.5% 0.152 265deg);
--color-primary-700: oklch(37.0% 0.138 264deg); --color-primary-700: oklch(37% 0.138 264deg);
--color-primary-800: oklch(30.0% 0.120 263deg); --color-primary-800: oklch(30% 0.12 263deg);
--color-primary-900: oklch(23.0% 0.100 262deg); --color-primary-900: oklch(23% 0.1 262deg);
--color-primary-950: oklch(15.5% 0.080 261deg); --color-primary-950: oklch(15.5% 0.08 261deg);
--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-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-100: var(--color-primary-contrast-dark);
--color-primary-contrast-200: var(--color-primary-contrast-dark); --color-primary-contrast-200: var(--color-primary-contrast-dark);
--color-primary-contrast-300: 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. * remaining clearly distinct from the primary.
* Used for secondary actions, badges, and soft highlights. * 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-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-300: oklch(78.5% 0.115 292deg);
--color-secondary-400: oklch(70.0% 0.132 291deg); --color-secondary-400: oklch(70% 0.132 291deg);
--color-secondary-500: oklch(60.0% 0.140 290deg); --color-secondary-500: oklch(60% 0.14 290deg);
--color-secondary-600: oklch(52.5% 0.135 289deg); --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-800: oklch(37.5% 0.112 286deg);
--color-secondary-900: oklch(30.0% 0.094 284deg); --color-secondary-900: oklch(30% 0.094 284deg);
--color-secondary-950: oklch(22.0% 0.076 282deg); --color-secondary-950: oklch(22% 0.076 282deg);
--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-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-100: var(--color-secondary-contrast-dark);
--color-secondary-contrast-200: var(--color-secondary-contrast-dark); --color-secondary-contrast-200: var(--color-secondary-contrast-dark);
--color-secondary-contrast-300: 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. * breaking against a deep indigo sky.
* Used for location chips, warm accents, tertiary elements. * Used for location chips, warm accents, tertiary elements.
* =================================================================== */ * =================================================================== */
--color-tertiary-50: oklch(96.5% 0.022 348deg); --color-tertiary-50: oklch(96.5% 0.022 348deg);
--color-tertiary-100: oklch(91.0% 0.042 346deg); --color-tertiary-100: oklch(91% 0.042 346deg);
--color-tertiary-200: oklch(84.5% 0.068 344deg); --color-tertiary-200: oklch(84.5% 0.068 344deg);
--color-tertiary-300: oklch(76.5% 0.095 343deg); --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-500: oklch(57.5% 0.128 341deg);
--color-tertiary-600: oklch(50.0% 0.122 340deg); --color-tertiary-600: oklch(50% 0.122 340deg);
--color-tertiary-700: oklch(43.0% 0.112 339deg); --color-tertiary-700: oklch(43% 0.112 339deg);
--color-tertiary-800: oklch(35.5% 0.098 338deg); --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-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-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-100: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-200: var(--color-tertiary-contrast-dark); --color-tertiary-contrast-200: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-300: 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 * Hue: ~152°. Consistent with AE_Firefly for recognizable semantic
* color meaning across OSIT themes. * color meaning across OSIT themes.
* =================================================================== */ * =================================================================== */
--color-success-50: oklch(95.77% 0.05 152.69deg); --color-success-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152.00deg); --color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg); --color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg); --color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg); --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-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg); --color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg); --color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.20% 0.06 151.12deg); --color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.20% 0.04 151.44deg); --color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950); --color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50); --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-100: var(--color-success-contrast-dark);
--color-success-contrast-200: var(--color-success-contrast-dark); --color-success-contrast-200: var(--color-success-contrast-dark);
--color-success-contrast-300: 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 * WARNING — Amber Orange
* Consistent with AE_Firefly for recognizable semantic meaning. * Consistent with AE_Firefly for recognizable semantic meaning.
* =================================================================== */ * =================================================================== */
--color-warning-50: oklch(97.5% 0.065 78deg); --color-warning-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.090 75deg); --color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.120 73deg); --color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg); --color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.160 67deg); --color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77.0% 0.165 65deg); --color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg); --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-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45.0% 0.105 61deg); --color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37.0% 0.088 60deg); --color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950); --color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50); --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-100: var(--color-warning-contrast-dark);
--color-warning-contrast-200: var(--color-warning-contrast-dark); --color-warning-contrast-200: var(--color-warning-contrast-dark);
--color-warning-contrast-300: 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 * ERROR — Soft Coral/Rose
* Consistent with AE_Firefly for recognizable semantic meaning. * Consistent with AE_Firefly for recognizable semantic meaning.
* =================================================================== */ * =================================================================== */
--color-error-50: oklch(95.0% 0.040 18deg); --color-error-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88.0% 0.070 20deg); --color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80.0% 0.105 21deg); --color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72.0% 0.140 22deg); --color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.170 23deg); --color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg); --color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg); --color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg); --color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg); --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-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-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-100: var(--color-error-contrast-dark);
--color-error-contrast-200: var(--color-error-contrast-dark); --color-error-contrast-200: var(--color-error-contrast-dark);
--color-error-contrast-300: 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 * 50 → body-bg light: near-white with ImperceptibleISTIC purple cast
* 950 → body-bg dark: deep midnight with indigo depth * 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-100: oklch(96.5% 0.006 268deg);
--color-surface-200: oklch(92.5% 0.010 266deg); --color-surface-200: oklch(92.5% 0.01 266deg);
--color-surface-300: oklch(87.0% 0.014 265deg); --color-surface-300: oklch(87% 0.014 265deg);
--color-surface-400: oklch(78.5% 0.018 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-600: oklch(54.5% 0.022 269deg);
--color-surface-700: oklch(42.5% 0.024 270deg); --color-surface-700: oklch(42.5% 0.024 270deg);
--color-surface-800: oklch(31.0% 0.026 272deg); --color-surface-800: oklch(31% 0.026 272deg);
--color-surface-900: oklch(20.5% 0.030 274deg); --color-surface-900: oklch(20.5% 0.03 274deg);
--color-surface-950: oklch(13.0% 0.034 276deg); --color-surface-950: oklch(13% 0.034 276deg);
--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-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-100: var(--color-surface-contrast-dark);
--color-surface-contrast-200: var(--color-surface-contrast-dark); --color-surface-contrast-200: var(--color-surface-contrast-dark);
--color-surface-contrast-300: var(--color-surface-contrast-dark); --color-surface-contrast-300: var(--color-surface-contrast-dark);

View File

@@ -63,50 +63,50 @@ html[data-theme='AE_Firefly_Rainbow'] {
/* --- Color ramps (light mode) copied from dark block so both modes have full ramps --- */ /* --- Color ramps (light mode) copied from dark block so both modes have full ramps --- */
html[data-theme='AE_Firefly_Rainbow'] { html[data-theme='AE_Firefly_Rainbow'] {
--color-primary-50: oklch(97.0% 0.020 15deg); --color-primary-50: oklch(97% 0.02 15deg);
--color-primary-100: oklch(92.0% 0.048 14deg); --color-primary-100: oklch(92% 0.048 14deg);
--color-primary-200: oklch(86.0% 0.085 13deg); --color-primary-200: oklch(86% 0.085 13deg);
--color-primary-300: oklch(79.0% 0.125 13deg); --color-primary-300: oklch(79% 0.125 13deg);
--color-primary-400: oklch(71.0% 0.160 13deg); --color-primary-400: oklch(71% 0.16 13deg);
--color-primary-500: oklch(60.0% 0.190 14deg); --color-primary-500: oklch(60% 0.19 14deg);
--color-primary-600: oklch(52.5% 0.178 15deg); --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-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-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-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-100: oklch(92.5% 0.072 150deg);
--color-secondary-200: oklch(87.0% 0.105 149deg); --color-secondary-200: oklch(87% 0.105 149deg);
--color-secondary-300: oklch(81.0% 0.132 148deg); --color-secondary-300: oklch(81% 0.132 148deg);
--color-secondary-400: oklch(74.5% 0.152 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-600: oklch(53.5% 0.148 148deg);
--color-secondary-700: oklch(45.5% 0.132 147deg); --color-secondary-700: oklch(45.5% 0.132 147deg);
--color-secondary-800: oklch(37.5% 0.112 146deg); --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-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-light: var(--color-secondary-50);
--color-tertiary-50: oklch(96.5% 0.030 299deg); --color-tertiary-50: oklch(96.5% 0.03 299deg);
--color-tertiary-100: oklch(91.0% 0.058 297deg); --color-tertiary-100: oklch(91% 0.058 297deg);
--color-tertiary-200: oklch(84.5% 0.092 296deg); --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-400: oklch(68.5% 0.148 295deg);
--color-tertiary-500: oklch(57.0% 0.158 295deg); --color-tertiary-500: oklch(57% 0.158 295deg);
--color-tertiary-600: oklch(49.5% 0.150 294deg); --color-tertiary-600: oklch(49.5% 0.15 294deg);
--color-tertiary-700: oklch(42.5% 0.138 293deg); --color-tertiary-700: oklch(42.5% 0.138 293deg);
--color-tertiary-800: oklch(35.5% 0.122 292deg); --color-tertiary-800: oklch(35.5% 0.122 292deg);
--color-tertiary-900: oklch(28.5% 0.102 291deg); --color-tertiary-900: oklch(28.5% 0.102 291deg);
--color-tertiary-950: oklch(21.0% 0.080 290deg); --color-tertiary-950: oklch(21% 0.08 290deg);
--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-light: var(--color-tertiary-50);
--color-success-50: oklch(95.77% 0.05 152.69deg); --color-success-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152.00deg); --color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg); --color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg); --color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg); --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-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg); --color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg); --color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.20% 0.06 151.12deg); --color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.20% 0.04 151.44deg); --color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950); --color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50); --color-success-contrast-light: var(--color-success-50);
--color-warning-50: oklch(97.5% 0.065 78deg); --color-warning-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.090 75deg); --color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.120 73deg); --color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg); --color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.160 67deg); --color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77.0% 0.165 65deg); --color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg); --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-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45.0% 0.105 61deg); --color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37.0% 0.088 60deg); --color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950); --color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50); --color-warning-contrast-light: var(--color-warning-50);
--color-error-50: oklch(95.0% 0.040 18deg); --color-error-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88.0% 0.070 20deg); --color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80.0% 0.105 21deg); --color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72.0% 0.140 22deg); --color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.170 23deg); --color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg); --color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg); --color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg); --color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg); --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-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-light: var(--color-error-50);
--color-surface-50: oklch(99.2% 0.004 75deg); --color-surface-50: oklch(99.2% 0.004 75deg);
--color-surface-100: oklch(97.0% 0.007 72deg); --color-surface-100: oklch(97% 0.007 72deg);
--color-surface-200: oklch(93.5% 0.010 70deg); --color-surface-200: oklch(93.5% 0.01 70deg);
--color-surface-300: oklch(88.5% 0.013 68deg); --color-surface-300: oklch(88.5% 0.013 68deg);
--color-surface-400: oklch(81.5% 0.016 66deg); --color-surface-400: oklch(81.5% 0.016 66deg);
--color-surface-500: oklch(70.5% 0.018 64deg); --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-700: oklch(47.5% 0.018 58deg);
--color-surface-800: oklch(35.5% 0.020 55deg); --color-surface-800: oklch(35.5% 0.02 55deg);
--color-surface-900: oklch(24.5% 0.020 52deg); --color-surface-900: oklch(24.5% 0.02 52deg);
--color-surface-950: oklch(15.5% 0.022 48deg); --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-light: var(--color-surface-50);
} }
@@ -180,20 +180,20 @@ html.dark[data-theme='AE_Firefly_Rainbow'] {
* Kept within sRGB gamut across the full ramp. * Kept within sRGB gamut across the full ramp.
* At 500 (L≈60%): sufficient contrast with primary-50 text (≥4:1). * At 500 (L≈60%): sufficient contrast with primary-50 text (≥4:1).
* =================================================================== */ * =================================================================== */
--color-primary-50: oklch(97.0% 0.020 15deg); --color-primary-50: oklch(97% 0.02 15deg);
--color-primary-100: oklch(92.0% 0.048 14deg); --color-primary-100: oklch(92% 0.048 14deg);
--color-primary-200: oklch(86.0% 0.085 13deg); --color-primary-200: oklch(86% 0.085 13deg);
--color-primary-300: oklch(79.0% 0.125 13deg); --color-primary-300: oklch(79% 0.125 13deg);
--color-primary-400: oklch(71.0% 0.160 13deg); --color-primary-400: oklch(71% 0.16 13deg);
--color-primary-500: oklch(60.0% 0.190 14deg); --color-primary-500: oklch(60% 0.19 14deg);
--color-primary-600: oklch(52.5% 0.178 15deg); --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-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-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-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-100: var(--color-primary-contrast-dark);
--color-primary-contrast-200: var(--color-primary-contrast-dark); --color-primary-contrast-200: var(--color-primary-contrast-dark);
--color-primary-contrast-300: 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. * it bridges the warm red primary and the cool violet tertiary.
* Used for secondary actions, success-adjacent highlights, badges. * 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-100: oklch(92.5% 0.072 150deg);
--color-secondary-200: oklch(87.0% 0.105 149deg); --color-secondary-200: oklch(87% 0.105 149deg);
--color-secondary-300: oklch(81.0% 0.132 148deg); --color-secondary-300: oklch(81% 0.132 148deg);
--color-secondary-400: oklch(74.5% 0.152 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-600: oklch(53.5% 0.148 148deg);
--color-secondary-700: oklch(45.5% 0.132 147deg); --color-secondary-700: oklch(45.5% 0.132 147deg);
--color-secondary-800: oklch(37.5% 0.112 146deg); --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-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-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-100: var(--color-secondary-contrast-dark);
--color-secondary-contrast-200: var(--color-secondary-contrast-dark); --color-secondary-contrast-200: var(--color-secondary-contrast-dark);
--color-secondary-contrast-300: 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. * brand color slots. Creates striking contrast with the warm primary.
* Used for location chips, deep accents, tertiary elements. * Used for location chips, deep accents, tertiary elements.
* =================================================================== */ * =================================================================== */
--color-tertiary-50: oklch(96.5% 0.030 299deg); --color-tertiary-50: oklch(96.5% 0.03 299deg);
--color-tertiary-100: oklch(91.0% 0.058 297deg); --color-tertiary-100: oklch(91% 0.058 297deg);
--color-tertiary-200: oklch(84.5% 0.092 296deg); --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-400: oklch(68.5% 0.148 295deg);
--color-tertiary-500: oklch(57.0% 0.158 295deg); --color-tertiary-500: oklch(57% 0.158 295deg);
--color-tertiary-600: oklch(49.5% 0.150 294deg); --color-tertiary-600: oklch(49.5% 0.15 294deg);
--color-tertiary-700: oklch(42.5% 0.138 293deg); --color-tertiary-700: oklch(42.5% 0.138 293deg);
--color-tertiary-800: oklch(35.5% 0.122 292deg); --color-tertiary-800: oklch(35.5% 0.122 292deg);
--color-tertiary-900: oklch(28.5% 0.102 291deg); --color-tertiary-900: oklch(28.5% 0.102 291deg);
--color-tertiary-950: oklch(21.0% 0.080 290deg); --color-tertiary-950: oklch(21% 0.08 290deg);
--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-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-100: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-200: var(--color-tertiary-contrast-dark); --color-tertiary-contrast-200: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-300: 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 * Hue: ~152°. Consistent with AE_Firefly for recognizable semantic
* color meaning across OSIT themes. * color meaning across OSIT themes.
* =================================================================== */ * =================================================================== */
--color-success-50: oklch(95.77% 0.05 152.69deg); --color-success-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152.00deg); --color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg); --color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg); --color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg); --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-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg); --color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg); --color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.20% 0.06 151.12deg); --color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.20% 0.04 151.44deg); --color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950); --color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50); --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-100: var(--color-success-contrast-dark);
--color-success-contrast-200: var(--color-success-contrast-dark); --color-success-contrast-200: var(--color-success-contrast-dark);
--color-success-contrast-300: 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 * WARNING — Amber Orange
* Consistent with AE_Firefly for recognizable semantic meaning. * Consistent with AE_Firefly for recognizable semantic meaning.
* =================================================================== */ * =================================================================== */
--color-warning-50: oklch(97.5% 0.065 78deg); --color-warning-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.090 75deg); --color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.120 73deg); --color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg); --color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.160 67deg); --color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77.0% 0.165 65deg); --color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg); --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-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45.0% 0.105 61deg); --color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37.0% 0.088 60deg); --color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950); --color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50); --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-100: var(--color-warning-contrast-dark);
--color-warning-contrast-200: var(--color-warning-contrast-dark); --color-warning-contrast-200: var(--color-warning-contrast-dark);
--color-warning-contrast-300: 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 * ERROR — Soft Coral/Rose
* Consistent with AE_Firefly for recognizable semantic meaning. * Consistent with AE_Firefly for recognizable semantic meaning.
* =================================================================== */ * =================================================================== */
--color-error-50: oklch(95.0% 0.040 18deg); --color-error-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88.0% 0.070 20deg); --color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80.0% 0.105 21deg); --color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72.0% 0.140 22deg); --color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.170 23deg); --color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg); --color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg); --color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg); --color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg); --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-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-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-100: var(--color-error-contrast-dark);
--color-error-contrast-200: var(--color-error-contrast-dark); --color-error-contrast-200: var(--color-error-contrast-dark);
--color-error-contrast-300: 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 * 50 → body-bg light: warm near-white, like morning paper
* 950 → body-bg dark: deep warm charcoal, like a dim theatre * 950 → body-bg dark: deep warm charcoal, like a dim theatre
* =================================================================== */ * =================================================================== */
--color-surface-50: oklch(99.2% 0.004 75deg); --color-surface-50: oklch(99.2% 0.004 75deg);
--color-surface-100: oklch(97.0% 0.007 72deg); --color-surface-100: oklch(97% 0.007 72deg);
--color-surface-200: oklch(93.5% 0.010 70deg); --color-surface-200: oklch(93.5% 0.01 70deg);
--color-surface-300: oklch(88.5% 0.013 68deg); --color-surface-300: oklch(88.5% 0.013 68deg);
--color-surface-400: oklch(81.5% 0.016 66deg); --color-surface-400: oklch(81.5% 0.016 66deg);
--color-surface-500: oklch(70.5% 0.018 64deg); --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-700: oklch(47.5% 0.018 58deg);
--color-surface-800: oklch(35.5% 0.020 55deg); --color-surface-800: oklch(35.5% 0.02 55deg);
--color-surface-900: oklch(24.5% 0.020 52deg); --color-surface-900: oklch(24.5% 0.02 52deg);
--color-surface-950: oklch(15.5% 0.022 48deg); --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-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-100: var(--color-surface-contrast-dark);
--color-surface-contrast-200: var(--color-surface-contrast-dark); --color-surface-contrast-200: var(--color-surface-contrast-dark);
--color-surface-contrast-300: var(--color-surface-contrast-dark); --color-surface-contrast-300: var(--color-surface-contrast-dark);

View File

@@ -60,50 +60,50 @@ html[data-theme='AE_Firefly_SteelBlue'] {
--radius-base: 0.375rem; --radius-base: 0.375rem;
--radius-container: 0.875rem; --radius-container: 0.875rem;
/* --- Color ramps (light mode) copied from dark block so both modes have full ramps --- */ /* --- 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-50: oklch(96.5% 0.022 214deg);
--color-primary-100: oklch(91.0% 0.045 213deg); --color-primary-100: oklch(91% 0.045 213deg);
--color-primary-200: oklch(84.5% 0.072 212deg); --color-primary-200: oklch(84.5% 0.072 212deg);
--color-primary-300: oklch(76.5% 0.097 212deg); --color-primary-300: oklch(76.5% 0.097 212deg);
--color-primary-400: oklch(67.0% 0.115 213deg); --color-primary-400: oklch(67% 0.115 213deg);
--color-primary-500: oklch(56.0% 0.115 214deg); --color-primary-500: oklch(56% 0.115 214deg);
--color-primary-600: oklch(49.0% 0.112 214deg); --color-primary-600: oklch(49% 0.112 214deg);
--color-primary-700: oklch(41.5% 0.105 213deg); --color-primary-700: oklch(41.5% 0.105 213deg);
--color-primary-800: oklch(34.0% 0.095 212deg); --color-primary-800: oklch(34% 0.095 212deg);
--color-primary-900: oklch(26.5% 0.080 211deg); --color-primary-900: oklch(26.5% 0.08 211deg);
--color-primary-950: oklch(18.5% 0.065 210deg); --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-light: var(--color-primary-50);
--color-secondary-50: oklch(97.5% 0.055 56deg); --color-secondary-50: oklch(97.5% 0.055 56deg);
--color-secondary-100: oklch(93.5% 0.090 55deg); --color-secondary-100: oklch(93.5% 0.09 55deg);
--color-secondary-200: oklch(89.5% 0.120 54deg); --color-secondary-200: oklch(89.5% 0.12 54deg);
--color-secondary-300: oklch(85.5% 0.148 53deg); --color-secondary-300: oklch(85.5% 0.148 53deg);
--color-secondary-400: oklch(81.5% 0.162 52deg); --color-secondary-400: oklch(81.5% 0.162 52deg);
--color-secondary-500: oklch(76.5% 0.162 51deg); --color-secondary-500: oklch(76.5% 0.162 51deg);
--color-secondary-600: oklch(68.5% 0.152 50deg); --color-secondary-600: oklch(68.5% 0.152 50deg);
--color-secondary-700: oklch(60.5% 0.138 49deg); --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-900: oklch(43.5% 0.102 47deg);
--color-secondary-950: oklch(35.0% 0.084 46deg); --color-secondary-950: oklch(35% 0.084 46deg);
--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-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-100: oklch(89.5% 0.048 231deg);
--color-tertiary-200: oklch(82.5% 0.072 230deg); --color-tertiary-200: oklch(82.5% 0.072 230deg);
--color-tertiary-300: oklch(74.5% 0.095 229deg); --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-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-700: oklch(39.5% 0.122 229deg);
--color-tertiary-800: oklch(32.0% 0.108 228deg); --color-tertiary-800: oklch(32% 0.108 228deg);
--color-tertiary-900: oklch(25.0% 0.090 227deg); --color-tertiary-900: oklch(25% 0.09 227deg);
--color-tertiary-950: oklch(17.5% 0.072 226deg); --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-light: var(--color-tertiary-50);
--color-success-50: oklch(95.77% 0.05 152.69deg); --color-success-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152.00deg); --color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg); --color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg); --color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg); --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-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg); --color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg); --color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.20% 0.06 151.12deg); --color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.20% 0.04 151.44deg); --color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950); --color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50); --color-success-contrast-light: var(--color-success-50);
--color-warning-50: oklch(97.5% 0.065 78deg); --color-warning-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.090 75deg); --color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.120 73deg); --color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg); --color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.160 67deg); --color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77.0% 0.165 65deg); --color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg); --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-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45.0% 0.105 61deg); --color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37.0% 0.088 60deg); --color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950); --color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50); --color-warning-contrast-light: var(--color-warning-50);
--color-error-50: oklch(95.0% 0.040 18deg); --color-error-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88.0% 0.070 20deg); --color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80.0% 0.105 21deg); --color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72.0% 0.140 22deg); --color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.170 23deg); --color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg); --color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg); --color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg); --color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg); --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-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-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-100: oklch(96.5% 0.008 218deg);
--color-surface-200: oklch(92.5% 0.012 217deg); --color-surface-200: oklch(92.5% 0.012 217deg);
--color-surface-300: oklch(87.0% 0.016 216deg); --color-surface-300: oklch(87% 0.016 216deg);
--color-surface-400: oklch(78.5% 0.020 215deg); --color-surface-400: oklch(78.5% 0.02 215deg);
--color-surface-500: oklch(66.5% 0.022 217deg); --color-surface-500: oklch(66.5% 0.022 217deg);
--color-surface-600: oklch(54.5% 0.025 220deg); --color-surface-600: oklch(54.5% 0.025 220deg);
--color-surface-700: oklch(42.5% 0.028 223deg); --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-900: oklch(20.5% 0.035 228deg);
--color-surface-950: oklch(13.0% 0.040 232deg); --color-surface-950: oklch(13% 0.04 232deg);
--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-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°). * Approx: #4682B4 (CSS SteelBlue) sits at oklch(56%, 0.113, 214°).
* At 500 (L≈56%): sufficient contrast with primary-50 text (≥4:1). * At 500 (L≈56%): sufficient contrast with primary-50 text (≥4:1).
* =================================================================== */ * =================================================================== */
--color-primary-50: oklch(96.5% 0.022 214deg); --color-primary-50: oklch(96.5% 0.022 214deg);
--color-primary-100: oklch(91.0% 0.045 213deg); --color-primary-100: oklch(91% 0.045 213deg);
--color-primary-200: oklch(84.5% 0.072 212deg); --color-primary-200: oklch(84.5% 0.072 212deg);
--color-primary-300: oklch(76.5% 0.097 212deg); --color-primary-300: oklch(76.5% 0.097 212deg);
--color-primary-400: oklch(67.0% 0.115 213deg); --color-primary-400: oklch(67% 0.115 213deg);
--color-primary-500: oklch(56.0% 0.115 214deg); --color-primary-500: oklch(56% 0.115 214deg);
--color-primary-600: oklch(49.0% 0.112 214deg); --color-primary-600: oklch(49% 0.112 214deg);
--color-primary-700: oklch(41.5% 0.105 213deg); --color-primary-700: oklch(41.5% 0.105 213deg);
--color-primary-800: oklch(34.0% 0.095 212deg); --color-primary-800: oklch(34% 0.095 212deg);
--color-primary-900: oklch(26.5% 0.080 211deg); --color-primary-900: oklch(26.5% 0.08 211deg);
--color-primary-950: oklch(18.5% 0.065 210deg); --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-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-100: var(--color-primary-contrast-dark);
--color-primary-contrast-200: var(--color-primary-contrast-dark); --color-primary-contrast-200: var(--color-primary-contrast-dark);
--color-primary-contrast-300: 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 — * of steel blue. The classic "metal on metal" contrast pairing —
* used for secondary actions, badges, and call-to-action highlights. * used for secondary actions, badges, and call-to-action highlights.
* =================================================================== */ * =================================================================== */
--color-secondary-50: oklch(97.5% 0.055 56deg); --color-secondary-50: oklch(97.5% 0.055 56deg);
--color-secondary-100: oklch(93.5% 0.090 55deg); --color-secondary-100: oklch(93.5% 0.09 55deg);
--color-secondary-200: oklch(89.5% 0.120 54deg); --color-secondary-200: oklch(89.5% 0.12 54deg);
--color-secondary-300: oklch(85.5% 0.148 53deg); --color-secondary-300: oklch(85.5% 0.148 53deg);
--color-secondary-400: oklch(81.5% 0.162 52deg); --color-secondary-400: oklch(81.5% 0.162 52deg);
--color-secondary-500: oklch(76.5% 0.162 51deg); --color-secondary-500: oklch(76.5% 0.162 51deg);
--color-secondary-600: oklch(68.5% 0.152 50deg); --color-secondary-600: oklch(68.5% 0.152 50deg);
--color-secondary-700: oklch(60.5% 0.138 49deg); --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-900: oklch(43.5% 0.102 47deg);
--color-secondary-950: oklch(35.0% 0.084 46deg); --color-secondary-950: oklch(35% 0.084 46deg);
--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-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-100: var(--color-secondary-contrast-dark);
--color-secondary-contrast-200: var(--color-secondary-contrast-dark); --color-secondary-contrast-200: var(--color-secondary-contrast-dark);
--color-secondary-contrast-300: 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. * like the heavy cobalt-blue depths under polished chrome.
* Used for accents, location chips, and depth elements. * 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-100: oklch(89.5% 0.048 231deg);
--color-tertiary-200: oklch(82.5% 0.072 230deg); --color-tertiary-200: oklch(82.5% 0.072 230deg);
--color-tertiary-300: oklch(74.5% 0.095 229deg); --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-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-700: oklch(39.5% 0.122 229deg);
--color-tertiary-800: oklch(32.0% 0.108 228deg); --color-tertiary-800: oklch(32% 0.108 228deg);
--color-tertiary-900: oklch(25.0% 0.090 227deg); --color-tertiary-900: oklch(25% 0.09 227deg);
--color-tertiary-950: oklch(17.5% 0.072 226deg); --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-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-100: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-200: var(--color-tertiary-contrast-dark); --color-tertiary-contrast-200: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-300: 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 * Hue: ~152°. Consistent with AE_Firefly for recognizable semantic
* color meaning across OSIT themes. * color meaning across OSIT themes.
* =================================================================== */ * =================================================================== */
--color-success-50: oklch(95.77% 0.05 152.69deg); --color-success-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152.00deg); --color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg); --color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg); --color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg); --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-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg); --color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg); --color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.20% 0.06 151.12deg); --color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.20% 0.04 151.44deg); --color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950); --color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50); --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-100: var(--color-success-contrast-dark);
--color-success-contrast-200: var(--color-success-contrast-dark); --color-success-contrast-200: var(--color-success-contrast-dark);
--color-success-contrast-300: 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 * WARNING — Amber Orange
* Consistent with AE_Firefly for recognizable semantic meaning. * Consistent with AE_Firefly for recognizable semantic meaning.
* =================================================================== */ * =================================================================== */
--color-warning-50: oklch(97.5% 0.065 78deg); --color-warning-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.090 75deg); --color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.120 73deg); --color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg); --color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.160 67deg); --color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77.0% 0.165 65deg); --color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg); --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-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45.0% 0.105 61deg); --color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37.0% 0.088 60deg); --color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950); --color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50); --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-100: var(--color-warning-contrast-dark);
--color-warning-contrast-200: var(--color-warning-contrast-dark); --color-warning-contrast-200: var(--color-warning-contrast-dark);
--color-warning-contrast-300: 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 * ERROR — Soft Coral/Rose
* Consistent with AE_Firefly for recognizable semantic meaning. * Consistent with AE_Firefly for recognizable semantic meaning.
* =================================================================== */ * =================================================================== */
--color-error-50: oklch(95.0% 0.040 18deg); --color-error-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88.0% 0.070 20deg); --color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80.0% 0.105 21deg); --color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72.0% 0.140 22deg); --color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.170 23deg); --color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg); --color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg); --color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg); --color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg); --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-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-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-100: var(--color-error-contrast-dark);
--color-error-contrast-200: var(--color-error-contrast-dark); --color-error-contrast-200: var(--color-error-contrast-dark);
--color-error-contrast-300: 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 * 50 → body-bg light: brilliant near-white with a chrome whisper
* 950 → body-bg dark: deep gunmetal with subtle cool-blue depth * 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-100: oklch(96.5% 0.008 218deg);
--color-surface-200: oklch(92.5% 0.012 217deg); --color-surface-200: oklch(92.5% 0.012 217deg);
--color-surface-300: oklch(87.0% 0.016 216deg); --color-surface-300: oklch(87% 0.016 216deg);
--color-surface-400: oklch(78.5% 0.020 215deg); --color-surface-400: oklch(78.5% 0.02 215deg);
--color-surface-500: oklch(66.5% 0.022 217deg); --color-surface-500: oklch(66.5% 0.022 217deg);
--color-surface-600: oklch(54.5% 0.025 220deg); --color-surface-600: oklch(54.5% 0.025 220deg);
--color-surface-700: oklch(42.5% 0.028 223deg); --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-900: oklch(20.5% 0.035 228deg);
--color-surface-950: oklch(13.0% 0.040 232deg); --color-surface-950: oklch(13% 0.04 232deg);
--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-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-100: var(--color-surface-contrast-dark);
--color-surface-contrast-200: var(--color-surface-contrast-dark); --color-surface-contrast-200: var(--color-surface-contrast-dark);
--color-surface-contrast-300: var(--color-surface-contrast-dark); --color-surface-contrast-300: var(--color-surface-contrast-dark);

View File

@@ -63,50 +63,50 @@ html[data-theme='AE_Firefly'] {
/* --- Color ramps (light mode) copied from dark block so both modes have full ramps --- */ /* --- Color ramps (light mode) copied from dark block so both modes have full ramps --- */
html[data-theme='AE_Firefly'] { html[data-theme='AE_Firefly'] {
--color-primary-50: oklch(96.5% 0.025 192deg); --color-primary-50: oklch(96.5% 0.025 192deg);
--color-primary-100: oklch(91.0% 0.050 190deg); --color-primary-100: oklch(91% 0.05 190deg);
--color-primary-200: oklch(84.5% 0.078 188deg); --color-primary-200: oklch(84.5% 0.078 188deg);
--color-primary-300: oklch(76.5% 0.105 186deg); --color-primary-300: oklch(76.5% 0.105 186deg);
--color-primary-400: oklch(67.5% 0.125 185deg); --color-primary-400: oklch(67.5% 0.125 185deg);
--color-primary-500: oklch(50.5% 0.130 184deg); --color-primary-500: oklch(50.5% 0.13 184deg);
--color-primary-600: oklch(44.0% 0.125 183deg); --color-primary-600: oklch(44% 0.125 183deg);
--color-primary-700: oklch(37.5% 0.115 182deg); --color-primary-700: oklch(37.5% 0.115 182deg);
--color-primary-800: oklch(30.5% 0.105 181deg); --color-primary-800: oklch(30.5% 0.105 181deg);
--color-primary-900: oklch(23.5% 0.090 180deg); --color-primary-900: oklch(23.5% 0.09 180deg);
--color-primary-950: oklch(16.0% 0.075 179deg); --color-primary-950: oklch(16% 0.075 179deg);
--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-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-100: oklch(93.5% 0.095 100deg);
--color-secondary-200: oklch(89.5% 0.128 98deg); --color-secondary-200: oklch(89.5% 0.128 98deg);
--color-secondary-300: oklch(85.5% 0.155 95deg); --color-secondary-300: oklch(85.5% 0.155 95deg);
--color-secondary-400: oklch(81.0% 0.170 93deg); --color-secondary-400: oklch(81% 0.17 93deg);
--color-secondary-500: oklch(76.0% 0.170 90deg); --color-secondary-500: oklch(76% 0.17 90deg);
--color-secondary-600: oklch(68.5% 0.160 87deg); --color-secondary-600: oklch(68.5% 0.16 87deg);
--color-secondary-700: oklch(60.5% 0.145 85deg); --color-secondary-700: oklch(60.5% 0.145 85deg);
--color-secondary-800: oklch(52.0% 0.130 83deg); --color-secondary-800: oklch(52% 0.13 83deg);
--color-secondary-900: oklch(43.5% 0.110 81deg); --color-secondary-900: oklch(43.5% 0.11 81deg);
--color-secondary-950: oklch(35.0% 0.090 79deg); --color-secondary-950: oklch(35% 0.09 79deg);
--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-light: var(--color-secondary-50);
--color-tertiary-50: oklch(95.5% 0.042 283deg); --color-tertiary-50: oklch(95.5% 0.042 283deg);
--color-tertiary-100: oklch(89.0% 0.068 281deg); --color-tertiary-100: oklch(89% 0.068 281deg);
--color-tertiary-200: oklch(81.5% 0.092 279deg); --color-tertiary-200: oklch(81.5% 0.092 279deg);
--color-tertiary-300: oklch(73.5% 0.112 278deg); --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-500: oklch(55.5% 0.142 276deg);
--color-tertiary-600: oklch(48.5% 0.138 275deg); --color-tertiary-600: oklch(48.5% 0.138 275deg);
--color-tertiary-700: oklch(41.5% 0.128 274deg); --color-tertiary-700: oklch(41.5% 0.128 274deg);
--color-tertiary-800: oklch(34.5% 0.112 273deg); --color-tertiary-800: oklch(34.5% 0.112 273deg);
--color-tertiary-900: oklch(27.5% 0.098 272deg); --color-tertiary-900: oklch(27.5% 0.098 272deg);
--color-tertiary-950: oklch(20.0% 0.082 271deg); --color-tertiary-950: oklch(20% 0.082 271deg);
--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-light: var(--color-tertiary-50);
--color-success-50: oklch(95.77% 0.05 152.69deg); --color-success-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152.00deg); --color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg); --color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg); --color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg); --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-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg); --color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg); --color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.20% 0.06 151.12deg); --color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.20% 0.04 151.44deg); --color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950); --color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50); --color-success-contrast-light: var(--color-success-50);
--color-warning-50: oklch(97.5% 0.065 78deg); --color-warning-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.090 75deg); --color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.120 73deg); --color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg); --color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.160 67deg); --color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77.0% 0.165 65deg); --color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg); --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-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45.0% 0.105 61deg); --color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37.0% 0.088 60deg); --color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950); --color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50); --color-warning-contrast-light: var(--color-warning-50);
--color-error-50: oklch(95.0% 0.040 18deg); --color-error-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88.0% 0.070 20deg); --color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80.0% 0.105 21deg); --color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72.0% 0.140 22deg); --color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.170 23deg); --color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg); --color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg); --color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg); --color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg); --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-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-light: var(--color-error-50);
--color-surface-50: oklch(99.2% 0.003 220deg); --color-surface-50: oklch(99.2% 0.003 220deg);
--color-surface-100: oklch(97.0% 0.006 217deg); --color-surface-100: oklch(97% 0.006 217deg);
--color-surface-200: oklch(93.5% 0.009 215deg); --color-surface-200: oklch(93.5% 0.009 215deg);
--color-surface-300: oklch(88.5% 0.012 213deg); --color-surface-300: oklch(88.5% 0.012 213deg);
--color-surface-400: oklch(81.5% 0.015 212deg); --color-surface-400: oklch(81.5% 0.015 212deg);
--color-surface-500: oklch(70.5% 0.016 215deg); --color-surface-500: oklch(70.5% 0.016 215deg);
--color-surface-600: oklch(59.0% 0.018 218deg); --color-surface-600: oklch(59% 0.018 218deg);
--color-surface-700: oklch(47.5% 0.020 222deg); --color-surface-700: oklch(47.5% 0.02 222deg);
--color-surface-800: oklch(30.5% 0.022 226deg); --color-surface-800: oklch(30.5% 0.022 226deg);
--color-surface-900: oklch(24.5% 0.025 229deg); --color-surface-900: oklch(24.5% 0.025 229deg);
--color-surface-950: oklch(15.5% 0.028 233deg); --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-light: var(--color-surface-50);
} }
@@ -174,20 +174,20 @@ html.dark[data-theme='AE_Firefly'] {
* PRIMARY — Luminescent Firefly Teal * PRIMARY — Luminescent Firefly Teal
* ...existing code... * ...existing code...
*/ */
--color-primary-50: oklch(96.5% 0.025 192deg); --color-primary-50: oklch(96.5% 0.025 192deg);
--color-primary-100: oklch(91.0% 0.050 190deg); --color-primary-100: oklch(91% 0.05 190deg);
--color-primary-200: oklch(84.5% 0.078 188deg); --color-primary-200: oklch(84.5% 0.078 188deg);
--color-primary-300: oklch(76.5% 0.105 186deg); --color-primary-300: oklch(76.5% 0.105 186deg);
--color-primary-400: oklch(67.5% 0.125 185deg); --color-primary-400: oklch(67.5% 0.125 185deg);
--color-primary-500: oklch(50.5% 0.130 184deg); --color-primary-500: oklch(50.5% 0.13 184deg);
--color-primary-600: oklch(44.0% 0.125 183deg); --color-primary-600: oklch(44% 0.125 183deg);
--color-primary-700: oklch(37.5% 0.115 182deg); --color-primary-700: oklch(37.5% 0.115 182deg);
--color-primary-800: oklch(30.5% 0.105 181deg); --color-primary-800: oklch(30.5% 0.105 181deg);
--color-primary-900: oklch(23.5% 0.090 180deg); --color-primary-900: oklch(23.5% 0.09 180deg);
--color-primary-950: oklch(16.0% 0.075 179deg); --color-primary-950: oklch(16% 0.075 179deg);
--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-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-100: var(--color-primary-contrast-dark);
--color-primary-contrast-200: var(--color-primary-contrast-dark); --color-primary-contrast-200: var(--color-primary-contrast-dark);
--color-primary-contrast-300: 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); --color-primary-contrast-950: var(--color-primary-contrast-light);
/* ...existing code for secondary, tertiary, success, warning, error, surface... */ /* ...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-100: oklch(93.5% 0.095 100deg);
--color-secondary-200: oklch(89.5% 0.128 98deg); --color-secondary-200: oklch(89.5% 0.128 98deg);
--color-secondary-300: oklch(85.5% 0.155 95deg); --color-secondary-300: oklch(85.5% 0.155 95deg);
--color-secondary-400: oklch(81.0% 0.170 93deg); --color-secondary-400: oklch(81% 0.17 93deg);
--color-secondary-500: oklch(76.0% 0.170 90deg); --color-secondary-500: oklch(76% 0.17 90deg);
--color-secondary-600: oklch(68.5% 0.160 87deg); --color-secondary-600: oklch(68.5% 0.16 87deg);
--color-secondary-700: oklch(60.5% 0.145 85deg); --color-secondary-700: oklch(60.5% 0.145 85deg);
--color-secondary-800: oklch(52.0% 0.130 83deg); --color-secondary-800: oklch(52% 0.13 83deg);
--color-secondary-900: oklch(43.5% 0.110 81deg); --color-secondary-900: oklch(43.5% 0.11 81deg);
--color-secondary-950: oklch(35.0% 0.090 79deg); --color-secondary-950: oklch(35% 0.09 79deg);
--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-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-100: var(--color-secondary-contrast-dark);
--color-secondary-contrast-200: var(--color-secondary-contrast-dark); --color-secondary-contrast-200: var(--color-secondary-contrast-dark);
--color-secondary-contrast-300: 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-900: var(--color-secondary-contrast-light);
--color-secondary-contrast-950: 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-50: oklch(95.5% 0.042 283deg);
--color-tertiary-100: oklch(89.0% 0.068 281deg); --color-tertiary-100: oklch(89% 0.068 281deg);
--color-tertiary-200: oklch(81.5% 0.092 279deg); --color-tertiary-200: oklch(81.5% 0.092 279deg);
--color-tertiary-300: oklch(73.5% 0.112 278deg); --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-500: oklch(55.5% 0.142 276deg);
--color-tertiary-600: oklch(48.5% 0.138 275deg); --color-tertiary-600: oklch(48.5% 0.138 275deg);
--color-tertiary-700: oklch(41.5% 0.128 274deg); --color-tertiary-700: oklch(41.5% 0.128 274deg);
--color-tertiary-800: oklch(34.5% 0.112 273deg); --color-tertiary-800: oklch(34.5% 0.112 273deg);
--color-tertiary-900: oklch(27.5% 0.098 272deg); --color-tertiary-900: oklch(27.5% 0.098 272deg);
--color-tertiary-950: oklch(20.0% 0.082 271deg); --color-tertiary-950: oklch(20% 0.082 271deg);
--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-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-100: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-200: var(--color-tertiary-contrast-dark); --color-tertiary-contrast-200: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-300: 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-900: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-950: 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-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152.00deg); --color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg); --color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg); --color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg); --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-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg); --color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg); --color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.20% 0.06 151.12deg); --color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.20% 0.04 151.44deg); --color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950); --color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50); --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-100: var(--color-success-contrast-dark);
--color-success-contrast-200: var(--color-success-contrast-dark); --color-success-contrast-200: var(--color-success-contrast-dark);
--color-success-contrast-300: 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-900: var(--color-success-contrast-light);
--color-success-contrast-950: 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-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.090 75deg); --color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.120 73deg); --color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg); --color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.160 67deg); --color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77.0% 0.165 65deg); --color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg); --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-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45.0% 0.105 61deg); --color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37.0% 0.088 60deg); --color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950); --color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50); --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-100: var(--color-warning-contrast-dark);
--color-warning-contrast-200: var(--color-warning-contrast-dark); --color-warning-contrast-200: var(--color-warning-contrast-dark);
--color-warning-contrast-300: 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-900: var(--color-warning-contrast-light);
--color-warning-contrast-950: 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-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88.0% 0.070 20deg); --color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80.0% 0.105 21deg); --color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72.0% 0.140 22deg); --color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.170 23deg); --color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg); --color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg); --color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg); --color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg); --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-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-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-100: var(--color-error-contrast-dark);
--color-error-contrast-200: var(--color-error-contrast-dark); --color-error-contrast-200: var(--color-error-contrast-dark);
--color-error-contrast-300: 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-900: var(--color-error-contrast-light);
--color-error-contrast-950: 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-50: oklch(99.2% 0.003 220deg);
--color-surface-100: oklch(97.0% 0.006 217deg); --color-surface-100: oklch(97% 0.006 217deg);
--color-surface-200: oklch(93.5% 0.009 215deg); --color-surface-200: oklch(93.5% 0.009 215deg);
--color-surface-300: oklch(88.5% 0.012 213deg); --color-surface-300: oklch(88.5% 0.012 213deg);
--color-surface-400: oklch(81.5% 0.015 212deg); --color-surface-400: oklch(81.5% 0.015 212deg);
--color-surface-500: oklch(70.5% 0.016 215deg); --color-surface-500: oklch(70.5% 0.016 215deg);
--color-surface-600: oklch(59.0% 0.018 218deg); --color-surface-600: oklch(59% 0.018 218deg);
--color-surface-700: oklch(47.5% 0.020 222deg); --color-surface-700: oklch(47.5% 0.02 222deg);
--color-surface-800: oklch(35.5% 0.022 226deg); --color-surface-800: oklch(35.5% 0.022 226deg);
--color-surface-900: oklch(24.5% 0.025 229deg); --color-surface-900: oklch(24.5% 0.025 229deg);
--color-surface-950: oklch(15.5% 0.028 233deg); --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-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-100: var(--color-surface-contrast-dark);
--color-surface-contrast-200: var(--color-surface-contrast-dark); --color-surface-contrast-200: var(--color-surface-contrast-dark);
--color-surface-contrast-300: var(--color-surface-contrast-dark); --color-surface-contrast-300: var(--color-surface-contrast-dark);

View File

@@ -10,12 +10,15 @@
/* Sync native browser control rendering (select dropdowns, scrollbars, etc.) /* Sync native browser control rendering (select dropdowns, scrollbars, etc.)
with the app's dark/light mode toggle. Without this, native controls follow 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>. */ the OS theme rather than the app's .dark/.light class on <html>. */
html.dark { color-scheme: dark; } html.dark {
html.light { color-scheme: light; } color-scheme: dark;
}
html.light {
color-scheme: light;
}
@import '@skeletonlabs/skeleton'; @import '@skeletonlabs/skeleton';
/* Register Preset Themes */ /* Register Preset Themes */
/* @import '@skeletonlabs/skeleton/themes/{theme-name}'; */ /* @import '@skeletonlabs/skeleton/themes/{theme-name}'; */
@import '@skeletonlabs/skeleton/themes/cerberus'; @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 .input:not([type='checkbox']):not([type='radio']):not([type='range']),
.dark .select, .dark .select,
.dark .textarea { .dark .textarea {
color: rgb(243 244 246); /* gray-100 */ color: rgb(243 244 246); /* gray-100 */
background-color: rgb(55 65 81); /* gray-700 */ 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 .input::placeholder,
.dark .textarea::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 */ /* Option elements in dark selects — forces browser native dark chrome */
.dark .select option { .dark .select option {
@@ -198,8 +201,12 @@ body {
/* Font size accessibility modes — cycled via the font size button in the sys menu. /* 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. Applied as a class on <html> by the layout DOM effect.
The 'default' mode has no class (browser default, typically 16px). */ The 'default' mode has no class (browser default, typically 16px). */
html.font-size-larger { font-size: 112.5%; } /* ~18px base */ html.font-size-larger {
html.font-size-smaller { font-size: 87.5%; } /* ~14px base */ font-size: 112.5%;
} /* ~18px base */
html.font-size-smaller {
font-size: 87.5%;
} /* ~14px base */
html.super_access #appShell { html.super_access #appShell {
background-color: hsla(0, 100%, 50%, 0.5); 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; */ /* @apply preset-tonal-primary border border-primary-500 transition-all; */
} }
.ae_btn_secondary { .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 */ /* hover:preset-filled-secondary-500 */
} }
.ae_btn_tertiary { .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 { .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 { .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 { .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 { .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 */ /* Buttons customized for Aether using Skeleton Tailwind preset classes */
.ae_btn_info { .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 */ /* Buttons are for filled and outlined presets */
.ae_btn_secondary_filled { .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 */ /* hover:preset-filled-secondary-500 */
} }
.ae_btn_secondary_outlined { .ae_btn_secondary_outlined {
@apply preset-outlined-secondary-200-800 hover:preset-filled-secondary-400-600 text-secondary-950-50 transition-all; @apply preset-outlined-secondary-200-800 hover:preset-filled-secondary-400-600 text-secondary-950-50 transition-all;
} }
.ae_btn_success_filled { .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 { .ae_btn_success_outlined {
@apply preset-outlined-success-200-800 hover:preset-filled-success-400-600 text-success-950-50 transition-all; @apply preset-outlined-success-200-800 hover:preset-filled-success-400-600 text-success-950-50 transition-all;
} }
.ae_btn_warning_filled { .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 { .ae_btn_warning_outlined {
@apply preset-outlined-warning-200-800 hover:preset-filled-warning-400-600 text-warning-950-50 transition-all; @apply preset-outlined-warning-200-800 hover:preset-filled-warning-400-600 text-warning-950-50 transition-all;
} }
.ae_btn_surface_filled { .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 { .ae_btn_surface_outlined {
@apply preset-outlined-surface-200-800 hover:preset-filled-surface-400-600 text-surface-950-50 transition-all; @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; @apply preset-outlined-error-200-800 hover:preset-filled-error-400-600 text-error-950-50 transition-all;
} }
.ae_btn_info_filled { .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 { .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 */ /* Containers customized for Aether using Skeleton Tailwind preset classes */
@@ -436,7 +443,7 @@ html.trusted_access #appShell {
.ae_module_header { .ae_module_header {
/* LCI request 3a5997 */ /* LCI request 3a5997 */
/* bg-gray-300 */ /* 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 { [data-theme='AE_c_LCI'] .ae_module_header {
@@ -453,32 +460,18 @@ html.trusted_access #appShell {
@apply container; @apply container;
} }
.ae_container_module_menu { .ae_container_module_menu {
@apply w-full max-w-7xl flex flex-col items-center justify-center gap-1 p-1 @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;
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;
} }
.ae_container_module_options { .ae_container_module_options {
@apply text-cyan-950 dark:text-cyan-50 @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;
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;
} }
.ae_container_module_help { .ae_container_module_help {
@apply text-yellow-950 dark:text-yellow-50 @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-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;
/* bg-yellow-100 border border-yellow-400 p-2 rounded-md max-w-xl */ /* bg-yellow-100 border border-yellow-400 p-2 rounded-md max-w-xl */
} }
.ae_container_actions { .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 { .ae_container_results {
@apply container; @apply container;
@@ -500,23 +493,11 @@ html.trusted_access #appShell {
@apply container; @apply container;
} }
.ae_container_help { .ae_container_help {
@apply text-yellow-950 dark:text-yellow-50 @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-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;
/* bg-yellow-100 border border-yellow-400 p-2 rounded-md max-w-xl */ /* bg-yellow-100 border border-yellow-400 p-2 rounded-md max-w-xl */
} }
.ae_container_info { .ae_container_info {
@apply text-cyan-950 dark:text-cyan-50 @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;
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;
} }
.ae_container_msg { .ae_container_msg {
@apply container; @apply container;

View File

@@ -12,16 +12,13 @@
<link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.googleapis.com" />
<link <link
href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,400;0,700;1,400;1,700&display=swap" 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 <link
href="https://fonts.googleapis.com/css2?family=Noto+Serif:ital,wght@0,400;0,700;1,400;1,700&display=swap" 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 <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" 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"> --> <!-- <link href="app.css" rel="stylesheet"> -->

View File

@@ -43,7 +43,9 @@ export const delete_object = async function delete_object({
// Construct the URL with query parameters // Construct the URL with query parameters
const url = new URL(endpoint, api_cfg['base_url']); const url = new URL(endpoint, api_cfg['base_url']);
if (params) { 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 // 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 // Auto-inject Authorization header if JWT is present but header is missing
const jwt = headers_cleaned['jwt'] || headers_cleaned['JWT'] || api_cfg['jwt']; const jwt =
if (jwt && !headers_cleaned['Authorization'] && !headers_cleaned['authorization']) { headers_cleaned['jwt'] || headers_cleaned['JWT'] || api_cfg['jwt'];
if (
jwt &&
!headers_cleaned['Authorization'] &&
!headers_cleaned['authorization']
) {
headers_cleaned['Authorization'] = `Bearer ${jwt}`; headers_cleaned['Authorization'] = `Bearer ${jwt}`;
} }
@@ -93,20 +100,26 @@ export const delete_object = async function delete_object({
try { try {
const controller = new AbortController(); const controller = new AbortController();
const timeoutId = setTimeout(() => { 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(); controller.abort();
}, timeout); }, timeout);
const fetchOptions: RequestInit = { const fetchOptions: RequestInit = {
method: 'DELETE', method: 'DELETE',
headers: headers_cleaned, 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 signal: controller.signal
}; };
const response = await fetch_method(url.toString(), fetchOptions).catch(function ( const response = await fetch_method(
error: any url.toString(),
) { fetchOptions
).catch(function (error: any) {
console.log( console.log(
'API DELETE Object *fetch* request was aborted or failed in an unexpected way.', 'API DELETE Object *fetch* request was aborted or failed in an unexpected way.',
error error
@@ -121,7 +134,9 @@ export const delete_object = async function delete_object({
} }
if (log_lvl) { if (log_lvl) {
console.log(`Response: status=${response.status} attempt=${attempt}`); console.log(
`Response: status=${response.status} attempt=${attempt}`
);
} }
if (!response.ok) { if (!response.ok) {
@@ -131,13 +146,18 @@ export const delete_object = async function delete_object({
} }
const errorBody = await response.text(); 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) { if (response.status >= 400 && response.status < 404) {
return false; 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(); const json = await response.json();
@@ -148,7 +168,11 @@ export const delete_object = async function delete_object({
// Return the response data or metadata // Return the response data or metadata
// Robustly handle V3 response envelopes // 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) { } catch (error) {
console.error(`API DELETE error on attempt ${attempt}:`, error); console.error(`API DELETE error on attempt ${attempt}:`, error);

View File

@@ -44,7 +44,9 @@ export async function get_ae_obj_id_crud({
log_lvl?: number; log_lvl?: number;
}) { }) {
if (log_lvl) { 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 // V3 Standard: Unified endpoint for all objects
@@ -77,7 +79,10 @@ export async function get_ae_obj_id_crud({
log_lvl: log_lvl, log_lvl: log_lvl,
return_meta: return_meta return_meta: return_meta
}).catch(function (error: any) { }).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; return false;
}); });

View File

@@ -95,7 +95,10 @@ interface GetAeObjLiV3Params {
view?: string; view?: string;
limit?: number; limit?: number;
offset?: 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; delay_ms?: number;
params?: key_val; params?: key_val;
headers?: key_val; headers?: key_val;
@@ -162,7 +165,10 @@ interface GetNestedObjLiV3Params {
view?: string; view?: string;
limit?: number; limit?: number;
offset?: 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; delay_ms?: number;
log_lvl?: number; log_lvl?: number;
} }

View File

@@ -24,7 +24,9 @@ export async function get_data_store({
log_lvl = 0 log_lvl = 0
}: GetDataStoreV3Params): Promise<any> { }: GetDataStoreV3Params): Promise<any> {
if (log_lvl) { if (log_lvl) {
console.log(`*** get_data_store() *** 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}`; const endpoint = `/v3/data_store/code/${code}`;

View File

@@ -41,7 +41,9 @@ export const get_object = async function get_object({
retry_count?: number; retry_count?: number;
}) { }) {
if (log_lvl) { 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); console.log('Params:', params);
if (log_lvl > 1) { if (log_lvl > 1) {
console.log('Data:', data); 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 // FAIL FAST: Check if we are explicitly offline to avoid long browser timeouts
if (typeof navigator !== 'undefined' && !navigator.onLine) { 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; return false;
} }
@@ -64,7 +69,9 @@ export const get_object = async function get_object({
} }
const url = new URL(endpoint, api_cfg['base_url']); 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 controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout); const timeoutId = setTimeout(() => controller.abort(), timeout);
@@ -96,14 +103,19 @@ export const get_object = async function get_object({
} }
// Handle "Bootstrap Paradox" for unauthenticated requests // Handle "Bootstrap Paradox" for unauthenticated requests
const bypass_val = merged_headers['x-no-account-id'] || merged_headers['x_no_account_id']; const bypass_val =
const is_valid_bypass = bypass_val === 'bypass' || merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
bypass_val === 'Nothing to See Here' || const is_valid_bypass =
params['key'] || bypass_val === 'bypass' ||
bypass_val === 'direct-download'; bypass_val === 'Nothing to See Here' ||
params['key'] ||
bypass_val === 'direct-download';
if (is_valid_bypass) { 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'];
delete merged_headers['x_account_id']; delete merged_headers['x_account_id'];
} else { } 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 // Auto-inject Authorization header if JWT is present but header is missing
let jwt = headers_cleaned['jwt'] || let jwt =
headers_cleaned['JWT'] || headers_cleaned['jwt'] ||
api_cfg['jwt'] || headers_cleaned['JWT'] ||
api_cfg['headers']?.['jwt'] || api_cfg['jwt'] ||
api_cfg['headers']?.['JWT']; api_cfg['headers']?.['jwt'] ||
api_cfg['headers']?.['JWT'];
// Final Fallback: Direct check of primary ae_loc key // Final Fallback: Direct check of primary ae_loc key
if (!jwt && typeof localStorage !== 'undefined') { 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}`; 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++) { for (let attempt = 1; attempt <= retry_count; attempt++) {
// FAIL FAST: Check if we are explicitly offline to avoid long browser timeouts // FAIL FAST: Check if we are explicitly offline to avoid long browser timeouts
if (typeof navigator !== 'undefined' && !navigator.onLine) { 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; return false;
} }
try { try {
const response = await fetch_method(url.toString(), fetchOptions).catch(function ( const response = await fetch_method(
error: any url.toString(),
) { fetchOptions
).catch(function (error: any) {
// SILENCE NOISE: Aborted requests (common in SWR/Background loads) shouldn't spam logs // 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) { 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 return error; // Return error to be handled below
} }
@@ -199,11 +227,19 @@ export const get_object = async function get_object({
clearTimeout(timeoutId); clearTimeout(timeoutId);
// Check if we should stop due to abort or network failure // 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 it was an explicit abort, definitely stop
if (response.name === 'AbortError') return false; 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; return false;
} }
@@ -231,24 +267,45 @@ export const get_object = async function get_object({
if (!response.ok) { if (!response.ok) {
if (response.status === 404) { if (response.status === 404) {
if (log_lvl) { 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; return null;
} }
// FAIL FAST (Section 2D): Do not retry on Auth or Client errors (400, 401, 403, 422) // 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 (
if (log_lvl) console.error(`API Client Failure (${response.status}). Failing fast.`); 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) { if (response.status === 401 || response.status === 403) {
console.warn(`AUTH DIAGNOSTICS: Headers sent for ${endpoint}:`, { console.warn(
has_auth: !!headers_cleaned['Authorization'], `AUTH DIAGNOSTICS: Headers sent for ${endpoint}:`,
has_api_key: !!headers_cleaned['x-aether-api-key'], {
has_account_id: !!headers_cleaned['x-account-id'], has_auth: !!headers_cleaned['Authorization'],
jwt_preview: jwt ? `${jwt.slice(0, 8)}...` : 'MISSING' 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. // 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 // Structured Error Handling (V3): Attempt to get rich error metadata
@@ -259,7 +316,11 @@ export const get_object = async function get_object({
// Not JSON // 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) { if (error_json?.meta?.details) {
return error_json; return error_json;
@@ -273,7 +334,10 @@ export const get_object = async function get_object({
status_code: response.status, status_code: response.status,
details: { details: {
category: 'validation', 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 raw: error_json.detail
} }
} }
@@ -307,7 +371,9 @@ export const get_object = async function get_object({
chunks.push(value); chunks.push(value);
receivedLength += value.length; receivedLength += value.length;
const percent_completed = Math.round((receivedLength * 100) / contentLength); const percent_completed = Math.round(
(receivedLength * 100) / contentLength
);
if (log_lvl > 1) { if (log_lvl > 1) {
console.log( console.log(
'GET Blob Progress:', 'GET Blob Progress:',
@@ -359,7 +425,10 @@ export const get_object = async function get_object({
} }
} }
} catch (error) { } 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) { if (attempt === retry_count) {
console.log('Max retry attempts reached. Returning false.'); console.log('Max retry attempts reached. Returning false.');

View File

@@ -45,7 +45,9 @@ export const patch_object = async function patch_object({
// Construct the URL with query parameters // Construct the URL with query parameters
const url = new URL(endpoint, api_cfg['base_url']); const url = new URL(endpoint, api_cfg['base_url']);
if (params) { 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 // 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 // Handle "Bootstrap Paradox" for unauthenticated requests
const bypass_val = merged_headers['x-no-account-id'] || merged_headers['x_no_account_id']; const bypass_val =
const is_valid_bypass = bypass_val === 'bypass' || merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
bypass_val === 'Nothing to See Here' || const is_valid_bypass =
params['key'] || bypass_val === 'bypass' ||
bypass_val === 'direct-download'; bypass_val === 'Nothing to See Here' ||
params['key'] ||
bypass_val === 'direct-download';
if (is_valid_bypass) { 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'];
delete merged_headers['x_account_id']; delete merged_headers['x_account_id'];
} else { } 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 // Auto-inject Authorization header if JWT is present but header is missing
let jwt = headers_cleaned['jwt'] || let jwt =
headers_cleaned['JWT'] || headers_cleaned['jwt'] ||
api_cfg['jwt'] || headers_cleaned['JWT'] ||
api_cfg['headers']?.['jwt'] || api_cfg['jwt'] ||
api_cfg['headers']?.['JWT']; api_cfg['headers']?.['jwt'] ||
api_cfg['headers']?.['JWT'];
// Final Fallback: Direct check of primary ae_loc key // Final Fallback: Direct check of primary ae_loc key
if (!jwt && typeof localStorage !== 'undefined') { 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}`; headers_cleaned['Authorization'] = `Bearer ${jwt}`;
} }
@@ -145,7 +157,9 @@ export const patch_object = async function patch_object({
try { try {
const controller = new AbortController(); const controller = new AbortController();
const timeoutId = setTimeout(() => { 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(); controller.abort();
}, timeout); }, timeout);
@@ -156,9 +170,10 @@ export const patch_object = async function patch_object({
signal: controller.signal signal: controller.signal
}; };
const response = await fetch_method(url.toString(), fetchOptions).catch(function ( const response = await fetch_method(
error: any url.toString(),
) { fetchOptions
).catch(function (error: any) {
console.log( console.log(
'API PATCH Object *fetch* request was aborted or failed in an unexpected way.', 'API PATCH Object *fetch* request was aborted or failed in an unexpected way.',
error error
@@ -173,30 +188,53 @@ export const patch_object = async function patch_object({
} }
if (log_lvl) { if (log_lvl) {
console.log(`Response: status=${response.status} attempt=${attempt}`); console.log(
`Response: status=${response.status} attempt=${attempt}`
);
} }
if (!response.ok) { if (!response.ok) {
if (response.status === 404) { if (response.status === 404) {
if (log_lvl) { 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; return null;
} }
// FAIL FAST (Section 2D): Do not retry on Auth or Client errors (400, 401, 403, 422) // 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 (
if (log_lvl) console.error(`API Client Failure (${response.status}). Failing fast.`); 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) { if (response.status === 401 || response.status === 403) {
console.warn(`AUTH DIAGNOSTICS (PATCH): Headers sent for ${endpoint}:`, { console.warn(
has_auth: !!headers_cleaned['Authorization'], `AUTH DIAGNOSTICS (PATCH): Headers sent for ${endpoint}:`,
has_api_key: !!headers_cleaned['x-aether-api-key'], {
has_account_id: !!headers_cleaned['x-account-id'], has_auth: !!headers_cleaned['Authorization'],
jwt_preview: jwt ? `${jwt.slice(0, 8)}...` : 'MISSING' 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. // 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 // Structured Error Handling (V3): Attempt to get rich error metadata
@@ -207,7 +245,11 @@ export const patch_object = async function patch_object({
// Not JSON // 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) { if (error_json?.meta?.details) {
return error_json; return error_json;
@@ -221,7 +263,10 @@ export const patch_object = async function patch_object({
status_code: response.status, status_code: response.status,
details: { details: {
category: 'validation', 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 raw: error_json.detail
} }
} }
@@ -242,7 +287,11 @@ export const patch_object = async function patch_object({
// Return the response data or metadata // Return the response data or metadata
// Robustly handle V3 response envelopes // 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) { } catch (error) {
console.error(`API PATCH error on attempt ${attempt}:`, error); console.error(`API PATCH error on attempt ${attempt}:`, error);

View File

@@ -33,7 +33,11 @@ export async function create_ae_obj({
// Standard Aether Pattern: Auto-serialize any key ending in _json // Standard Aether Pattern: Auto-serialize any key ending in _json
const cleaned_fields = { ...fields }; const cleaned_fields = { ...fields };
for (const key in cleaned_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}`); if (log_lvl) console.log(`Auto-serializing field: ${key}`);
cleaned_fields[key] = JSON.stringify(cleaned_fields[key]); cleaned_fields[key] = JSON.stringify(cleaned_fields[key]);
} }
@@ -94,7 +98,11 @@ export async function create_nested_obj({
// Standard Aether Pattern: Auto-serialize any key ending in _json // Standard Aether Pattern: Auto-serialize any key ending in _json
const cleaned_fields = { ...fields }; const cleaned_fields = { ...fields };
for (const key in cleaned_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]); cleaned_fields[key] = JSON.stringify(cleaned_fields[key]);
} }
} }
@@ -140,7 +148,11 @@ export async function update_ae_obj({
// Standard Aether Pattern: Auto-serialize any key ending in _json // Standard Aether Pattern: Auto-serialize any key ending in _json
const cleaned_fields = { ...fields }; const cleaned_fields = { ...fields };
for (const key in cleaned_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}`); if (log_lvl > 1) console.log(`Auto-serializing field: ${key}`);
cleaned_fields[key] = JSON.stringify(cleaned_fields[key]); cleaned_fields[key] = JSON.stringify(cleaned_fields[key]);
} }
@@ -202,7 +214,11 @@ export async function update_nested_obj({
// Standard Aether Pattern: Auto-serialize any key ending in _json // Standard Aether Pattern: Auto-serialize any key ending in _json
const cleaned_fields = { ...fields }; const cleaned_fields = { ...fields };
for (const key in cleaned_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]); cleaned_fields[key] = JSON.stringify(cleaned_fields[key]);
} }
} }

View File

@@ -10,7 +10,10 @@ interface SearchAeObjV3Params {
view?: string; view?: string;
for_obj_type?: string; for_obj_type?: string;
for_obj_id?: 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; limit?: number;
offset?: number; offset?: number;
delay_ms?: number; delay_ms?: number;
@@ -55,7 +58,10 @@ export async function search_ae_obj({
// Serialize any complex objects in the query params (e.g. ft_qry, lk_qry) // Serialize any complex objects in the query params (e.g. ft_qry, lk_qry)
for (const key in query_params) { 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]); query_params[key] = JSON.stringify(query_params[key]);
} }
} }

View File

@@ -41,7 +41,9 @@ export const post_object = async function post_object({
retry_count?: number; retry_count?: number;
}) { }) {
if (log_lvl) { 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); console.log('Params:', params);
if (log_lvl > 1) { if (log_lvl > 1) {
console.log('Data:', data); console.log('Data:', data);
@@ -65,7 +67,9 @@ export const post_object = async function post_object({
// Construct the URL with query parameters // Construct the URL with query parameters
const url = new URL(endpoint, api_cfg['base_url']); const url = new URL(endpoint, api_cfg['base_url']);
if (params) { 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 // Clean and merge headers
@@ -95,14 +99,19 @@ export const post_object = async function post_object({
} }
// Handle "Bootstrap Paradox" for unauthenticated requests // Handle "Bootstrap Paradox" for unauthenticated requests
const bypass_val = merged_headers['x-no-account-id'] || merged_headers['x_no_account_id']; const bypass_val =
const is_valid_bypass = bypass_val === 'bypass' || merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
bypass_val === 'Nothing to See Here' || const is_valid_bypass =
params['key'] || bypass_val === 'bypass' ||
bypass_val === 'direct-download'; bypass_val === 'Nothing to See Here' ||
params['key'] ||
bypass_val === 'direct-download';
if (is_valid_bypass) { 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'];
delete merged_headers['x_account_id']; delete merged_headers['x_account_id'];
} else { } 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 // Auto-inject Authorization header if JWT is present but header is missing
let jwt = headers_cleaned['jwt'] || let jwt =
headers_cleaned['JWT'] || headers_cleaned['jwt'] ||
api_cfg['jwt'] || headers_cleaned['JWT'] ||
api_cfg['headers']?.['jwt'] || api_cfg['jwt'] ||
api_cfg['headers']?.['JWT']; api_cfg['headers']?.['jwt'] ||
api_cfg['headers']?.['JWT'];
// Final Fallback: Direct check of primary ae_loc key // Final Fallback: Direct check of primary ae_loc key
if (!jwt && typeof localStorage !== 'undefined') { 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}`; headers_cleaned['Authorization'] = `Bearer ${jwt}`;
} }
@@ -186,13 +200,21 @@ export const post_object = async function post_object({
console.log('Fetch Options:', fetchOptions); console.log('Fetch Options:', fetchOptions);
} }
const response = await fetch_method(url.toString(), fetchOptions).catch(function ( const response = await fetch_method(
error: any url.toString(),
) { fetchOptions
).catch(function (error: any) {
// SILENCE NOISE: Aborted requests shouldn't spam logs at log_lvl 0 // 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) { 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; return error;
} }
@@ -206,9 +228,17 @@ export const post_object = async function post_object({
clearTimeout(timeoutId); clearTimeout(timeoutId);
// Check if we should stop due to abort or network failure // 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 (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; return false;
} }
@@ -219,30 +249,53 @@ export const post_object = async function post_object({
} }
if (log_lvl) { if (log_lvl) {
console.log(`Response: status=${response.status} attempt=${attempt}`); console.log(
`Response: status=${response.status} attempt=${attempt}`
);
} }
if (!response.ok) { if (!response.ok) {
if (response.status === 404) { if (response.status === 404) {
if (log_lvl) { 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; return null;
} }
// FAIL FAST (Section 2D): Do not retry on Auth or Client errors (400, 401, 403, 422) // 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 (
if (log_lvl) console.error(`API Client Failure (${response.status}). Failing fast.`); 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) { if (response.status === 401 || response.status === 403) {
console.warn(`AUTH DIAGNOSTICS (POST): Headers sent for ${endpoint}:`, { console.warn(
has_auth: !!headers_cleaned['Authorization'], `AUTH DIAGNOSTICS (POST): Headers sent for ${endpoint}:`,
has_api_key: !!headers_cleaned['x-aether-api-key'], {
has_account_id: !!headers_cleaned['x-account-id'], has_auth: !!headers_cleaned['Authorization'],
jwt_preview: jwt ? `${jwt.slice(0, 8)}...` : 'MISSING' 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. // 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 // Structured Error Handling (V3): Attempt to get rich error metadata
@@ -253,7 +306,11 @@ export const post_object = async function post_object({
// Not JSON // 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) { if (error_json?.meta?.details) {
return error_json; return error_json;
@@ -267,7 +324,10 @@ export const post_object = async function post_object({
status_code: response.status, status_code: response.status,
details: { details: {
category: 'validation', 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 raw: error_json.detail
} }
} }
@@ -311,7 +371,11 @@ export const post_object = async function post_object({
// Return the response data or metadata // Return the response data or metadata
// Robustly handle V3 response envelopes // 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 { } else {
const blob = await response.blob(); const blob = await response.blob();

View File

@@ -36,7 +36,9 @@ export async function load_ae_obj_id__archive({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_Archive | null> { }): Promise<ae_Archive | null> {
if (log_lvl) { 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 ae_promises.load__archive_obj = await api
@@ -52,10 +54,11 @@ export async function load_ae_obj_id__archive({
.then(async function (archive_obj_get_result) { .then(async function (archive_obj_get_result) {
if (archive_obj_get_result) { if (archive_obj_get_result) {
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__archive_props({ const processed_obj_li =
obj_li: [archive_obj_get_result], await process_ae_obj__archive_props({
log_lvl: log_lvl obj_li: [archive_obj_get_result],
}); log_lvl: log_lvl
});
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_archives, db_instance: db_archives,
table_name: 'archive', table_name: 'archive',
@@ -76,19 +79,21 @@ export async function load_ae_obj_id__archive({
if (inc_content_li && ae_promises.load__archive_obj) { if (inc_content_li && ae_promises.load__archive_obj) {
// Load the contents for the archive // Load the contents for the archive
const load_archive_content_obj_li = await load_ae_obj_li__archive_content({ const load_archive_content_obj_li =
api_cfg: api_cfg, await load_ae_obj_li__archive_content({
for_obj_type: 'archive', api_cfg: api_cfg,
for_obj_id: archive_id, for_obj_type: 'archive',
enabled: enabled, for_obj_id: archive_id,
hidden: hidden, enabled: enabled,
limit: limit, hidden: hidden,
offset: offset, limit: limit,
params: params, offset: offset,
try_cache: try_cache, params: params,
log_lvl: log_lvl try_cache: try_cache,
}); log_lvl: log_lvl
ae_promises.load__archive_obj.archive_content_li = load_archive_content_obj_li; });
ae_promises.load__archive_obj.archive_content_li =
load_archive_content_obj_li;
} }
return ae_promises.load__archive_obj; return ae_promises.load__archive_obj;
@@ -125,7 +130,9 @@ export async function load_ae_obj_li__archive({
view?: string; view?: string;
limit?: number; limit?: number;
offset?: 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; params?: key_val;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
@@ -138,7 +145,9 @@ export async function load_ae_obj_li__archive({
// DEBUG: Trace massive content loads // DEBUG: Trace massive content loads
if (inc_content_li) { if (inc_content_li) {
console.warn(`load_ae_obj_li__archive: Loading content for ALL archives in list! Limit: ${limit}`); console.warn(
`load_ae_obj_li__archive: Loading content for ALL archives in list! Limit: ${limit}`
);
// console.trace(); // console.trace();
} }
@@ -159,10 +168,11 @@ export async function load_ae_obj_li__archive({
.then(async function (archive_obj_li_get_result) { .then(async function (archive_obj_li_get_result) {
if (archive_obj_li_get_result) { if (archive_obj_li_get_result) {
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__archive_props({ const processed_obj_li =
obj_li: archive_obj_li_get_result, await process_ae_obj__archive_props({
log_lvl: log_lvl obj_li: archive_obj_li_get_result,
}); log_lvl: log_lvl
});
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_archives, db_instance: db_archives,
table_name: 'archive', table_name: 'archive',
@@ -220,7 +230,9 @@ export async function create_ae_obj__archive({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_Archive | null> { }): Promise<ae_Archive | null> {
if (log_lvl) { 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({ const result = await api.create_ae_obj({
@@ -268,7 +280,9 @@ export async function delete_ae_obj_id__archive({
log_lvl?: number; log_lvl?: number;
}) { }) {
if (log_lvl) { 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({ const result = await api.delete_ae_obj({
@@ -304,7 +318,10 @@ export async function update_ae_obj__archive({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_Archive | null> { }): Promise<ae_Archive | null> {
if (log_lvl) { 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({ const result = await api.update_ae_obj({
@@ -366,7 +383,11 @@ export async function qry__archive({
const search_query: any = { and: [] }; const search_query: any = { and: [] };
if (account_id) { if (account_id) {
search_query.and.push({ field: 'account_id_random', op: 'eq', value: account_id }); search_query.and.push({
field: 'account_id_random',
op: 'eq',
value: account_id
});
} }
if (qry_str) { if (qry_str) {
@@ -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 updated = processed_obj.updated_on ?? processed_obj.created_on;
const name = processed_obj.name ?? ''; const name = processed_obj.name ?? '';
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`; (processed_obj as any).tmp_sort_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
(processed_obj as any).tmp_sort_2 =
`${group}_${priority}_${sort}_${name}_${updated}`;
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);

View File

@@ -41,10 +41,11 @@ export async function load_ae_obj_id__archive_content({
.then(async function (archive_content_obj_get_result) { .then(async function (archive_content_obj_get_result) {
if (archive_content_obj_get_result) { if (archive_content_obj_get_result) {
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__archive_content_props({ const processed_obj_li =
obj_li: [archive_content_obj_get_result], await process_ae_obj__archive_content_props({
log_lvl: log_lvl obj_li: [archive_content_obj_get_result],
}); log_lvl: log_lvl
});
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_archives, db_instance: db_archives,
table_name: 'content', table_name: 'content',
@@ -96,7 +97,9 @@ export async function load_ae_obj_li__archive_content({
view?: string; view?: string;
limit?: number; limit?: number;
offset?: 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; params?: key_val;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
@@ -124,10 +127,11 @@ export async function load_ae_obj_li__archive_content({
.then(async function (archive_content_obj_li_get_result) { .then(async function (archive_content_obj_li_get_result) {
if (archive_content_obj_li_get_result) { if (archive_content_obj_li_get_result) {
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__archive_content_props({ const processed_obj_li =
obj_li: archive_content_obj_li_get_result, await process_ae_obj__archive_content_props({
log_lvl: log_lvl obj_li: archive_content_obj_li_get_result,
}); log_lvl: log_lvl
});
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_archives, db_instance: db_archives,
table_name: 'content', table_name: 'content',
@@ -162,11 +166,15 @@ export async function create_ae_obj__archive_content({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_ArchiveContent | null> { }): Promise<ae_ArchiveContent | null> {
if (log_lvl) { 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) { 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; return null;
} }
@@ -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 updated = processed_obj.updated_on ?? processed_obj.created_on;
const name = processed_obj.name ?? ''; const name = processed_obj.name ?? '';
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`; (processed_obj as any).tmp_sort_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
(processed_obj as any).tmp_sort_2 =
`${group}_${priority}_${sort}_${name}_${updated}`;
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);

View File

@@ -1,188 +1,191 @@
<script lang="ts"> <script lang="ts">
// Imports // Imports
// Import components and elements // Import components and elements
// import Element_input_files_tbl from '$lib/element_input_files_tbl.svelte'; // import Element_input_files_tbl from '$lib/element_input_files_tbl.svelte';
// Import storage, functions, and libraries // Import storage, functions, and libraries
import type { key_val } from '$lib/stores/ae_stores'; import type { key_val } from '$lib/stores/ae_stores';
import { api } from '$lib/api/api'; import { api } from '$lib/api/api';
import { ae_util } from '$lib/ae_utils/ae_utils'; import { ae_util } from '$lib/ae_utils/ae_utils';
import { import {
ae_snip, ae_snip,
ae_loc, ae_loc,
ae_sess, ae_sess,
ae_api, ae_api,
ae_trig, ae_trig,
slct, slct,
slct_trigger slct_trigger
} from '$lib/stores/ae_stores'; } from '$lib/stores/ae_stores';
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte'; 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'; import {
// Exports Check,
Download,
LoaderCircle,
MinusCircle,
Scissors
} from '@lucide/svelte';
// Exports
// export let input_name = 'file_list'; // export let input_name = 'file_list';
// export let multiple: boolean = true; // export let multiple: boolean = true;
// export let required: 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 { interface Props {
log_lvl?: number; log_lvl?: number;
// Expecting these for link_to_type: 'event', 'event_location', 'archive_content', etc // Expecting these for link_to_type: 'event', 'event_location', 'archive_content', etc
link_to_type: string; link_to_type: string;
link_to_id: 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'; // 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_default?: string;
class_li?: string; class_li?: string;
// export let table_class_li: string[] = ['table', 'table-sm', 'table-striped', 'table-hover' , 'text-sm']; // export let table_class_li: string[] = ['table', 'table-sm', 'table-striped', 'table-hover' , 'text-sm'];
clip_complete?: boolean; clip_complete?: boolean;
// export let upload_complete: boolean = false; // export let upload_complete: boolean = false;
submit_status?: null | string; submit_status?: null | string;
// hosted_file_id_li?: string[]; // hosted_file_id_li?: string[];
// hosted_file_obj_li?: any[]; // hosted_file_obj_li?: any[];
hosted_file_obj_kv?: key_val; hosted_file_obj_kv?: key_val;
video_clip_file_kv?: key_val; video_clip_file_kv?: key_val;
} }
let { let {
log_lvl = $bindable(0), log_lvl = $bindable(0),
link_to_type = $bindable(), link_to_type = $bindable(),
link_to_id = $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_default = 'flex flex-col gap-1 items-center justify-center w-full max-w-2xl mx-auto my-1',
class_li = $bindable(''), class_li = $bindable(''),
clip_complete = $bindable(false), clip_complete = $bindable(false),
submit_status = $bindable(null), submit_status = $bindable(null),
// hosted_file_id_li = [], // hosted_file_id_li = [],
// hosted_file_obj_li = [], // hosted_file_obj_li = [],
hosted_file_obj_kv = $bindable({}), hosted_file_obj_kv = $bindable({}),
video_clip_file_kv = $bindable({}) video_clip_file_kv = $bindable({})
}: Props = $props(); }: Props = $props();
// Local Variables // Local Variables
let task_id = link_to_id; let task_id = link_to_id;
// let input_file_list: any = null; // let input_file_list: any = null;
let ae_promises: key_val = $state({}); let ae_promises: key_val = $state({});
// let ae_promises_clipping: key_val = {}; // let ae_promises_clipping: key_val = {};
// let ae_triggers: 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 = { // let form_kv: key_val = {
// start_time: null, // start_time: null,
// end_time: null, // end_time: null,
// reencode: null, // reencode: null,
// video_file: null, // video_file: null,
// }; // };
// let download_clip_src: string; // let download_clip_src: string;
// let download_clip_filename: string; // let download_clip_filename: string;
$ae_sess.files.obj = { $ae_sess.files.obj = {
obj: null 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 = `/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 ae_promises[hosted_file_id] = {};
function prevent_default<T extends Event>(fn: (event: T) => void) { // .convert__hosted_file_obj
return function (event: T) { ae_promises[hosted_file_id] = api
event.preventDefault(); .get_object({
fn(event); 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) { video_clip_file_kv[result.hosted_file_id] = {};
console.log('*** handle_clip_video() ***'); video_clip_file_kv[result.hosted_file_id] = result;
submit_status = 'clipping'; // $ae_loc.files.video_clip_file_kv[result.hosted_file_id] = {};
clip_complete = false; // $ae_loc.files.video_clip_file_kv[result.hosted_file_id] = result;
const form = event.target as HTMLFormElement; $ae_sess.files.processed_file_kv[hosted_file_id].submit_status =
const formData = new FormData(form); 'clipped';
$ae_sess.files.processed_file_kv[hosted_file_id].clip_complete =
true;
let hosted_file_id = formData.get('hosted_file_id') as string; $ae_loc.files.processed_file_kv[hosted_file_id].submit_status =
let start_time = formData.get('start_time') as string; 'clipped';
let end_time = formData.get('end_time') as string; $ae_loc.files.processed_file_kv[hosted_file_id].clip_complete =
let reencode = formData.get('reencode') as string; true;
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] = {}; submit_status = 'clipped';
$ae_sess.files.processed_file_kv[hosted_file_id].submit_status = clip_complete = true;
'clipping';
$ae_sess.files.processed_file_kv[hosted_file_id].clip_complete = false;
// $ae_sess.files.disable_submit__hosted_file_obj = true; // let file_blob = new Blob([result.data]);
$ae_loc.files.processed_file_kv[hosted_file_id] = {}; // // console.log(file_blob);
$ae_loc.files.processed_file_kv[hosted_file_id].submit_status = // let file_obj_url = window.URL.createObjectURL(file_blob); // The img src
'clipping'; // // const url = window.URL.createObjectURL(new Blob([result.data]));
$ae_loc.files.processed_file_kv[hosted_file_id].start_time = start_time; // download_clip_src = file_obj_url;
$ae_loc.files.processed_file_kv[hosted_file_id].end_time = end_time; // // download_filename = file_obj_url;
$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 = `/hosted_file/${hosted_file_id}/clip_video`; return true;
});
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;
});
}
</script> </script>
<section class="{class_li_default} {class_li}"> <section class="{class_li_default} {class_li}">
@@ -191,11 +194,11 @@
</h3> </h3>
{#each Object.entries(hosted_file_obj_kv) as [hosted_file_id, hosted_file_obj] (hosted_file_id)} {#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) --> <!-- Download Button (Standardized) -->
<div <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 --> <!-- Remove from uploaded file kv list -->
<button <button
type="button" type="button"
@@ -219,8 +222,7 @@
); );
}} }}
class="btn btn-sm preset-tonal-warning hover:preset-filled-warning-500" 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" /> <MinusCircle size="1em" class="m-1" />
<span class="">Remove</span> <span class="">Remove</span>
</button> </button>
@@ -234,60 +236,49 @@
variant="tonal" variant="tonal"
classes="novi_btn btn-sm lg:btn-md min-w-72 lg:min-w-96 !justify-start" classes="novi_btn btn-sm lg:btn-md min-w-72 lg:min-w-96 !justify-start"
show_divider={true} show_divider={true}
max_filename={30} max_filename={30} />
/>
</div> </div>
<span <span
>{ae_util.shorten_filename({ >{ae_util.shorten_filename({
filename: hosted_file_obj?.filename, filename: hosted_file_obj?.filename,
max_length: 30 max_length: 30
})}</span })}</span>
>
<span> <span>
<span class="text-sm font-bold"> File ID: </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>
<span class="text-sm font-bold"> Type: </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> --> <!-- <span>{hosted_file_obj.filename}</span> -->
</div> </div>
<form <form
onsubmit={prevent_default(handle_clip_video)} 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'} --> <!-- {$ae_sess?.files[hosted_file_obj?.hosted_file_id ?? 'obj'].submit_status ?? 'not set'} -->
<input <input
type="hidden" type="hidden"
name="hosted_file_id" name="hosted_file_id"
value={hosted_file_obj.hosted_file_id} value={hosted_file_obj.hosted_file_id} />
/>
<div <div
class="flex flex-row gap-1 justify-center items-center w-full" class="flex w-full flex-row items-center justify-center gap-1">
> <span class="w-32 text-xs font-bold">New Filename:</span>
<span class="text-xs font-bold w-32">New Filename:</span>
<input <input
type="text" type="text"
class="input w-full text-sm variant-filled-surface" class="input variant-filled-surface w-full text-sm"
name="new_filename" name="new_filename"
value={hosted_file_obj.filename} value={hosted_file_obj.filename} />
/>
</div> </div>
<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 <label
class="label w-48" 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" <span class="text-xs font-bold"
>Start time (HH:MM:SS)</span >Start time (HH:MM:SS)</span>
>
<input <input
type="text" type="text"
name="start_time" name="start_time"
@@ -300,17 +291,14 @@
].start_time ].start_time
: '00:00:00'} : '00:00:00'}
placeholder="HH:MM:SS (00:01:30)" placeholder="HH:MM:SS (00:01:30)"
class="input w-32 variant-filled-surface" class="input variant-filled-surface w-32" />
/>
</label> </label>
<label <label
class="label w-48" 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" <span class="text-xs font-bold"
>End time (HH:MM:SS)</span >End time (HH:MM:SS)</span>
>
<input <input
type="text" type="text"
name="end_time" name="end_time"
@@ -323,14 +311,12 @@
].end_time ].end_time
: '00:45:59'} : '00:45:59'}
placeholder="HH:MM:SS (01:05:25)" placeholder="HH:MM:SS (01:05:25)"
class="input w-32 variant-filled-surface" class="input variant-filled-surface w-32" />
/>
</label> </label>
<span <span
class="flex flex-col gap-1 items-center justify-center" 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." 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> <span class="text-xs font-bold"> Re-encode? </span>
<label class="inline-block"> <label class="inline-block">
<input <input
@@ -338,8 +324,7 @@
name="reencode" name="reencode"
value="true" value="true"
class="radio" class="radio"
checked checked />
/>
True True
</label> </label>
<label class="inline-block"> <label class="inline-block">
@@ -347,16 +332,14 @@
type="radio" type="radio"
name="reencode" name="reencode"
value="false" value="false"
class="radio" class="radio" />
/>
False False
</label> </label>
</span> </span>
<span <span
class="flex flex-col gap-1 items-center justify-center" 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." 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> <span class="text-xs font-bold"> Scale down? </span>
<label class="inline-block"> <label class="inline-block">
<input <input
@@ -364,8 +347,7 @@
name="scale_down" name="scale_down"
value="true" value="true"
class="radio" class="radio"
checked checked />
/>
True True
</label> </label>
<label class="inline-block"> <label class="inline-block">
@@ -373,8 +355,7 @@
type="radio" type="radio"
name="scale_down" name="scale_down"
value="false" value="false"
class="radio" class="radio" />
/>
False False
</label> </label>
</span> </span>
@@ -382,9 +363,8 @@
<button <button
type="submit" type="submit"
class="btn btn-lg btn-primary preset-tonal-primary border border-primary-500 hover:preset-filled-primary-500 transition-colors" class="btn btn-lg btn-primary preset-tonal-primary border-primary-500 hover:preset-filled-primary-500 border transition-colors"
disabled={submit_status == 'clipping'} disabled={submit_status == 'clipping'}>
>
<!-- {#await ae_promises[hosted_file_id]} --> <!-- {#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'} {#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" /> <LoaderCircle size="1em" class="m-1 animate-spin" />
@@ -407,8 +387,7 @@
{#await ae_promises[hosted_file_id]} {#await ae_promises[hosted_file_id]}
<LoaderCircle size="1em" class="m-1 animate-spin" /> <LoaderCircle size="1em" class="m-1 animate-spin" />
<span class="highlight" <span class="highlight"
>Processing... This may take a few minutes.</span >Processing... This may take a few minutes.</span>
>
{:then} {:then}
{#if ae_promises[hosted_file_id]} {#if ae_promises[hosted_file_id]}
<Download size="1em" /> Ready to download below! <Download size="1em" /> Ready to download below!

View File

@@ -1,48 +1,48 @@
<script lang="ts"> <script lang="ts">
// Imports // Imports
// Import components and elements // Import components and elements
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte'; import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';
// Import storage, functions, and libraries // Import storage, functions, and libraries
import type { key_val } from '$lib/stores/ae_stores'; import type { key_val } from '$lib/stores/ae_stores';
import { api } from '$lib/api/api'; import { api } from '$lib/api/api';
import { ae_util } from '$lib/ae_utils/ae_utils'; import { ae_util } from '$lib/ae_utils/ae_utils';
import { import {
ae_snip, ae_snip,
ae_loc, ae_loc,
ae_sess, ae_sess,
ae_api, ae_api,
ae_trig, ae_trig,
slct, slct,
slct_trigger slct_trigger
} from '$lib/stores/ae_stores'; } from '$lib/stores/ae_stores';
// Exports // Exports
// export let hosted_file_id_li: string[] = []; // export let hosted_file_id_li: string[] = [];
// export let hosted_file_obj_li: any[] = []; // export let hosted_file_obj_li: any[] = [];
interface Props { interface Props {
log_lvl?: number; log_lvl?: number;
// export let hosted_file_obj_kv: key_val = {}; // export let hosted_file_obj_kv: key_val = {};
video_clip_file_kv?: key_val; video_clip_file_kv?: key_val;
class_li_default?: string; class_li_default?: string;
class_li?: string; class_li?: string;
link_to_type: string; link_to_type: string;
link_to_id: string; link_to_id: string;
} }
let { let {
log_lvl = 0, log_lvl = 0,
video_clip_file_kv = $bindable({}), 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_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 = '', class_li = '',
link_to_type, link_to_type,
link_to_id link_to_id
}: Props = $props(); }: Props = $props();
let ae_promises: key_val = $state({}); let ae_promises: key_val = $state({});
</script> </script>
<h3 class="h3">{Object.entries(video_clip_file_kv).length}× files clipped</h3> <h3 class="h3">{Object.entries(video_clip_file_kv).length}× files clipped</h3>
@@ -54,7 +54,6 @@
max_filename={30} 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" 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_type={link_to_type}
linked_to_id={link_to_id} linked_to_id={link_to_id} />
/>
{/each} {/each}
</div> </div>

View File

@@ -1,243 +1,275 @@
<script lang="ts"> <script lang="ts">
// *** Import Svelte specific // *** Import Svelte specific
import * as Lucide from 'lucide-svelte'; import * as Lucide from 'lucide-svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
// *** Import Aether specific variables and functions // *** Import Aether specific variables and functions
import type { key_val } from '$lib/stores/ae_stores'; import type { key_val } from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils'; import { ae_util } from '$lib/ae_utils/ae_utils';
import { download_ae_obj_id__hosted_file } from '$lib/ae_core/core__hosted_files'; import { download_ae_obj_id__hosted_file } from '$lib/ae_core/core__hosted_files';
import { import { ae_loc, ae_sess, ae_api } from '$lib/stores/ae_stores';
ae_loc,
ae_sess,
ae_api
} from '$lib/stores/ae_stores';
interface Props { interface Props {
log_lvl?: number; log_lvl?: number;
hosted_file_id: null | string; hosted_file_id: null | string;
hosted_file_obj: null | key_val; hosted_file_obj: null | key_val;
filename?: null | string; filename?: null | string;
max_filename?: number; max_filename?: number;
auto_download?: boolean; auto_download?: boolean;
linked_to_type?: null | string; linked_to_type?: null | string;
linked_to_id?: null | string; linked_to_id?: null | string;
download_complete?: null | boolean; download_complete?: null | boolean;
download_percent?: number; download_percent?: number;
download_status_msg?: string; download_status_msg?: string;
variant?: 'tonal' | 'filled' | 'outline' | 'ghost'; variant?: 'tonal' | 'filled' | 'outline' | 'ghost';
color?: 'primary' | 'secondary' | 'tertiary' | 'success' | 'warning' | 'error' | 'surface'; color?:
show_divider?: boolean; | 'primary'
show_direct_download?: boolean; | 'secondary'
require_auth?: boolean; | 'tertiary'
classes?: string; | 'success'
click?: () => void | Promise<any>; | 'warning'
label?: import('svelte').Snippet; | '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 { return () => {
log_lvl = 0, if (status_interval) {
hosted_file_id, clearInterval(status_interval);
hosted_file_obj, status_interval = null;
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(() => { let final_filename = $derived(
const base = 'btn btn-sm lg:btn-md min-w-48 transition-all overflow-hidden px-3'; filename ?? hosted_file_obj?.filename ?? 'unknown'
const style = color_map[color]?.[variant] || color_map.primary.tonal; );
return `${base} ${style} ${classes}`.trim(); let shortened_filename = $derived(
}); ae_util.shorten_filename({
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({
filename: final_filename, filename: final_filename,
max_length: max_filename max_length: max_filename
})); })
);
let direct_download_url = $derived.by(() => { let direct_download_url = $derived.by(() => {
if (!show_direct_download || !hosted_file_obj) return ''; 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. // 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. // 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 file_id =
const obj_type_path = hosted_file_obj.event_file_id ? 'event_file' : 'hosted_file'; hosted_file_obj.event_file_id ||
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}`; 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() { async function handle_click() {
const file_id = hosted_file_obj?.id || hosted_file_obj?.hosted_file_id || hosted_file_id; const file_id =
download_complete = undefined; hosted_file_obj?.id ||
download_status_msg = 'Downloading...'; hosted_file_obj?.hosted_file_id ||
hosted_file_id;
download_complete = undefined;
download_status_msg = 'Downloading...';
if (click) { if (click) {
const result = click(); const result = click();
// If the override returns a promise, track it so the UI shows progress // If the override returns a promise, track it so the UI shows progress
if (result instanceof Promise) { if (result instanceof Promise) {
ae_promises[file_id] = result; ae_promises[file_id] = result;
}
return;
} }
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;
});
} }
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> </script>
{#snippet content()} {#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]} {#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 <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} /> <Lucide.LoaderCircle class="animate-spin" size={18} />
</div> </div>
<div class="grow relative text-left h-full"> <div class="relative h-full grow text-left">
{#if show_filename_view} {#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"> <span class="truncate">
{shortened_filename} {shortened_filename}
</span> </span>
</div> </div>
{:else} {: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"> <span class="font-bold whitespace-nowrap">
Downloading: Downloading:
{#if $ae_sess.api_download_kv[file_id]} {#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} {:else}
... ...
{/if} {/if}
@@ -250,18 +282,22 @@
{#if label} {#if label}
{@render label()} {@render label()}
{:else} {:else}
{@const IconComp = ae_util.file_extension_icon_lucide(hosted_file_obj?.extension)} {@const IconComp = ae_util.file_extension_icon_lucide(
<div class="flex items-center w-full"> hosted_file_obj?.extension
)}
<div class="flex w-full items-center">
<div <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} /> <IconComp size={18} />
</div> </div>
<span class="grow truncate text-left"> <span class="grow truncate text-left">
{shortened_filename} {shortened_filename}
</span> </span>
{#if hosted_file_obj?.file_purpose || hosted_file_obj?.group} {#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} {hosted_file_obj.file_purpose || hosted_file_obj.group}
</span> </span>
{/if} {/if}
@@ -270,22 +306,25 @@
{/await} {/await}
{#if download_complete === null} {#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} {: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} {/if}
{/snippet} {/snippet}
{#if hosted_file_id && hosted_file_obj} {#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} {#if show_direct_download}
<a <a
href={direct_download_url} href={direct_download_url}
download={ae_util.clean_filename(final_filename)} download={ae_util.clean_filename(final_filename)}
class={variant_classes} 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()} {@render content()}
</a> </a>
{:else} {:else}
@@ -294,17 +333,21 @@
disabled={require_auth && !$ae_loc.authenticated_access} disabled={require_auth && !$ae_loc.authenticated_access}
class={variant_classes} class={variant_classes}
onclick={handle_click} 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()} {@render content()}
</button> </button>
{/if} {/if}
{:else} {:else}
<button type="button" disabled class={variant_classes} title="No file selected"> <button
<div class="flex items-center w-full"> type="button"
disabled
class={variant_classes}
title="No file selected">
<div class="flex w-full items-center">
<div <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} /> <Lucide.FileX size={18} />
</div> </div>
<span class="grow text-left"> No file info </span> <span class="grow text-left"> No file info </span>

View File

@@ -1,281 +1,286 @@
<script lang="ts"> <script lang="ts">
// untrack import removed — task_id sync now uses direct $effect (no untrack needed) // untrack import removed — task_id sync now uses direct $effect (no untrack needed)
// Imports // Imports
// Import components and elements // Import components and elements
import * as Lucide from 'lucide-svelte'; import * as Lucide from 'lucide-svelte';
import Element_input_files_tbl from '$lib/elements/element_input_files_tbl.svelte'; import Element_input_files_tbl from '$lib/elements/element_input_files_tbl.svelte';
// Import storage, functions, and libraries // Import storage, functions, and libraries
import type { key_val } from '$lib/stores/ae_stores'; import type { key_val } from '$lib/stores/ae_stores';
import { api } from '$lib/api/api'; import { api } from '$lib/api/api';
import { import {
ae_snip, ae_snip,
ae_loc, ae_loc,
ae_sess, ae_sess,
ae_api, ae_api,
ae_trig, ae_trig,
slct, slct,
slct_trigger slct_trigger
} from '$lib/stores/ae_stores'; } from '$lib/stores/ae_stores';
// Exports // Exports
interface Props { interface Props {
log_lvl?: number; log_lvl?: number;
// Expecting these for link_to_type: 'event', 'event_location', 'archive_content', etc // Expecting these for link_to_type: 'event', 'event_location', 'archive_content', etc
link_to_type: string; link_to_type: string;
link_to_id: string; link_to_id: string;
input_name?: string; input_name?: string;
multiple?: boolean; multiple?: boolean;
required?: boolean; required?: boolean;
accept?: string; accept?: string;
class_li_default?: string; class_li_default?: string;
class_li?: string; class_li?: string;
input_class_li?: string[]; input_class_li?: string[];
table_class_li?: string[]; table_class_li?: string[];
upload_complete?: boolean; upload_complete?: boolean;
submit_status?: null | string; submit_status?: null | string;
hosted_file_id_li?: string[]; hosted_file_id_li?: string[];
hosted_file_obj_li?: any[]; hosted_file_obj_li?: any[];
hosted_file_obj_kv?: key_val; hosted_file_obj_kv?: key_val;
label?: import('svelte').Snippet; 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 { $ae_sess.files.disable_submit__hosted_file_obj = true;
log_lvl = 0, $ae_sess.files.submit_status = 'saving';
link_to_type, submit_status = 'saving';
link_to_id, upload_complete = false;
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 hosted_file_id_li = [];
let task_id: string = $state(''); hosted_file_obj_li = [];
let input_file_list: any = $state(null); hosted_file_obj_kv = {};
let ae_promises: key_val = $state({}); // Promise<any>;
let ae_triggers: key_val = {};
let input_element_id = 'ae_comp__hosted_files_upload__input'; let hosted_file_results;
$effect(() => { const target = event.currentTarget as HTMLFormElement;
if (log_lvl) { const file_input = target
console.log(`*** ae_comp__hosted_files_upload.svelte ***`); ? (target[input_element_id] as HTMLInputElement)
console.log(`link_to_type: ${link_to_type} link_to_id: ${link_to_id}`); : null;
}
});
$effect(() => { if (
// Sync task_id with link_to_id prop so it resets when navigating to a different object. target &&
task_id = link_to_id; 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 // Loop through each file and upload them individually in event.target[input_element_id].files
async function handle_submit_form_files(event: SubmitEvent) { // The task_id should be the file hash.
console.log('*** handle_submit_form() ***'); // processed_file_list[i] has the file hash_sha256, hash_sha256_match, warnings, uploaded, uploaded_bytes, filename, and file_size_bytes.
event.preventDefault(); for (let i = 0; i < file_input.files.length; i++) {
let tmp_file = file_input.files[i];
if (!event) { task_id = $ae_sess.files.processed_file_list[i].hash_sha256;
return;
}
$ae_sess.files.disable_submit__hosted_file_obj = true; // hosted_file_results = await handle_input_upload_files([tmp_file], task_id);
$ae_sess.files.submit_status = 'saving'; hosted_file_results = await handle_input_upload_files({
submit_status = 'saving'; input_upload_files: [tmp_file],
upload_complete = false; task_id: task_id
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';
}); });
if (log_lvl) { if (hosted_file_results) {
console.log(`Waiting for upload__hosted_file_obj promise...`); 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 = '/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';
});
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> </script>
<!-- class:hidden={!$ae_loc.trusted_access} --> <!-- class:hidden={!$ae_loc.trusted_access} -->
<form onsubmit={handle_submit_form_files} class="{class_li_default} {class_li}"> <form onsubmit={handle_submit_form_files} class="{class_li_default} {class_li}">
{#await ae_promises.upload__hosted_file_obj} {#await ae_promises.upload__hosted_file_obj}
<div class="text-lg flex flex-row gap-1 items-center justify-center"> <div class="flex flex-row items-center justify-center gap-1 text-lg">
<Lucide.LoaderCircle class="animate-spin m-1" /> <Lucide.LoaderCircle class="m-1 animate-spin" />
<span class=""> <span class="">
Uploading Uploading
{#if $ae_sess.api_upload_kv[task_id]} {#if $ae_sess.api_upload_kv[task_id]}
@@ -288,14 +293,14 @@
<label <label
for="ae_comp__hosted_files_upload__input" for="ae_comp__hosted_files_upload__input"
class="svelte_input_file_label text-center" 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} {#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" /> <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> </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 /> <strong>Supported formats</strong><br />
(PowerPoint, Keynote, PDF, Media, etc) (PowerPoint, Keynote, PDF, Media, etc)
</span> </span>
@@ -313,33 +318,30 @@
class=" class="
svelte_input_file_element svelte_input_file_element
file-dropzone-input file-dropzone-input
px-1
block w-full text-lg
preset-filled-surface-50-950 preset-filled-surface-50-950
text-surface-900 dark:text-surface-100 text-surface-900 dark:text-surface-100 border-surface-300
border border-surface-300 dark:border-surface-700 rounded-lg dark:border-surface-700
cursor-pointer focus:ring-primary-500 block
focus:outline-hidden focus:ring-2 focus:ring-primary-500 w-full cursor-pointer rounded-lg border
px-1
text-lg focus:ring-2 focus:outline-hidden
{input_class_li.join(' ')} {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 <Element_input_files_tbl
bind:input_file_list bind:input_file_list
bind:file_list_status={$ae_sess.files.status__file_list} bind:file_list_status={$ae_sess.files.status__file_list}
bind:processed_file_list={$ae_sess.files.processed_file_list} bind:processed_file_list={$ae_sess.files.processed_file_list}
{table_class_li} {table_class_li} />
/>
<button <button
type="submit" 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 || 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} {#await ae_promises.upload__hosted_file_obj}
<Lucide.LoaderCircle class="animate-spin m-1" /> <Lucide.LoaderCircle class="m-1 animate-spin" />
<span class=""> <span class="">
{#if $ae_sess.api_upload_kv[task_id]} {#if $ae_sess.api_upload_kv[task_id]}
{$ae_sess.api_upload_kv[task_id].percent_completed}% {$ae_sess.api_upload_kv[task_id].percent_completed}%
@@ -350,9 +352,12 @@
{:then} {:then}
<Lucide.UploadCloud class="m-1" size={20} /> <Lucide.UploadCloud class="m-1" size={20} />
<span class="text-sm"> Upload </span> <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} {#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} {:else}
<span class="text-xs"> 0 </span> <span class="text-xs"> 0 </span>
{/if} {/if}

View File

@@ -1,235 +1,358 @@
<script lang="ts"> <script lang="ts">
import { untrack } from 'svelte'; import { untrack } from 'svelte';
/** /**
* AE_Comp_Site_Config_Editor.svelte * AE_Comp_Site_Config_Editor.svelte
* Specialized UI for managing site.cfg_json settings. * Specialized UI for managing site.cfg_json settings.
* Supports General, AI, Performance, and IDAA-specific configurations. * Supports General, AI, Performance, and IDAA-specific configurations.
*/ */
import { Modal } from 'flowbite-svelte'; import { Modal } from 'flowbite-svelte';
import { Brain, CodeXml, ExternalLink, Globe, Mail, Minus, Palette, Plus, Save, ShieldCheck, Timer } from '@lucide/svelte'; import {
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte'; Brain,
import { ae_loc } from '$lib/stores/ae_stores'; 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 { interface Props {
cfg_json: any; cfg_json: any;
on_save?: () => void; 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) // Ensure we have a valid object (handle strings/nulls)
$effect(() => { $effect(() => {
if (typeof cfg_json === 'string') { if (typeof cfg_json === 'string') {
try { try {
cfg_json = JSON.parse(cfg_json); cfg_json = JSON.parse(cfg_json);
} catch (e) { } catch (e) {
cfg_json = {}; 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 = {}; if (!cfg_json) cfg_json = {};
});
function add_to_list(key: string) { // Internal State
if (!cfg_json[key]) cfg_json[key] = []; let active_tab: 'visuals' | 'email' | 'ai' | 'refresh' | 'idaa' | 'raw' =
const val = prompt('Enter Novi UUID:'); $state('visuals');
if (val) cfg_json[key].push(val); 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) { // Update cfg_json when raw string changes
cfg_json[key].splice(index, 1); $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> </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 --> <!-- Tab Navigation -->
<div class="flex flex-wrap gap-1 p-1 bg-surface-500/10 rounded-lg max-w-fit"> <div
<button class="btn btn-sm transition-all {active_tab === 'visuals' ? 'variant-filled-primary' : 'variant-soft-surface'}" onclick={() => active_tab = 'visuals'}> 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 <Palette size="1.1em" class="mr-1" /> Visuals
</button> </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 <Mail size="1.1em" class="mr-1" /> Email
</button> </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 <Brain size="1.1em" class="mr-1" /> AI/LLM
</button> </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 <Timer size="1.1em" class="mr-1" /> Refresh
</button> </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 <ShieldCheck size="1.1em" class="mr-1" /> IDAA
</button> </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 <CodeXml size="1.1em" class="mr-1" /> Raw JSON
</button> </button>
</div> </div>
<!-- Scrollable Content Area --> <!-- 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'} {#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"> <label class="label">
<span class="text-xs font-bold uppercase opacity-50">Theme Name</span> <span class="text-xs font-bold uppercase opacity-50"
<input type="text" bind:value={cfg_json.theme_name} class="input variant-form-material" placeholder="e.g. AE_OSIT_default" /> >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>
<label class="label"> <label class="label">
<span class="text-xs font-bold uppercase opacity-50">Theme Mode</span> <span class="text-xs font-bold uppercase opacity-50"
<select bind:value={cfg_json.theme_mode} class="select variant-form-material"> >Theme Mode</span>
<select
bind:value={cfg_json.theme_mode}
class="select variant-form-material">
<option value="light">Light</option> <option value="light">Light</option>
<option value="dark">Dark</option> <option value="dark">Dark</option>
<option value="auto">Auto (System)</option> <option value="auto">Auto (System)</option>
</select> </select>
</label> </label>
<label class="label md:col-span-2"> <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"> <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} {#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} {/if}
</div> </div>
</label> </label>
</div> </div>
{:else if active_tab === 'email'} {:else if active_tab === 'email'}
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 animate-in fade-in duration-200"> <div
<section class="space-y-4 border-r border-surface-500/10 pr-4"> class="animate-in fade-in grid grid-cols-1 gap-4 duration-200 md:grid-cols-2">
<h4 class="text-sm font-black text-primary-500">Admin Contact</h4> <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"> <label class="label">
<span class="text-xs font-bold uppercase opacity-50">Admin Name</span> <span class="text-xs font-bold uppercase opacity-50"
<input type="text" bind:value={cfg_json.admin_name} class="input variant-form-material" /> >Admin Name</span>
<input
type="text"
bind:value={cfg_json.admin_name}
class="input variant-form-material" />
</label> </label>
<label class="label"> <label class="label">
<span class="text-xs font-bold uppercase opacity-50">Admin Email</span> <span class="text-xs font-bold uppercase opacity-50"
<input type="email" bind:value={cfg_json.admin_email} class="input variant-form-material" /> >Admin Email</span>
<input
type="email"
bind:value={cfg_json.admin_email}
class="input variant-form-material" />
</label> </label>
</section> </section>
<section class="space-y-4"> <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"> <label class="label">
<span class="text-xs font-bold uppercase opacity-50">No-Reply Name</span> <span class="text-xs font-bold uppercase opacity-50"
<input type="text" bind:value={cfg_json.noreply_name} class="input variant-form-material" /> >No-Reply Name</span>
<input
type="text"
bind:value={cfg_json.noreply_name}
class="input variant-form-material" />
</label> </label>
<label class="label"> <label class="label">
<span class="text-xs font-bold uppercase opacity-50">No-Reply Email</span> <span class="text-xs font-bold uppercase opacity-50"
<input type="email" bind:value={cfg_json.noreply_email} class="input variant-form-material" /> >No-Reply Email</span>
<input
type="email"
bind:value={cfg_json.noreply_email}
class="input variant-form-material" />
</label> </label>
</section> </section>
</div> </div>
{:else if active_tab === 'ai'} {:else if active_tab === 'ai'}
<div class="space-y-4 animate-in fade-in duration-200"> <div class="animate-in fade-in space-y-4 duration-200">
<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"> <label class="label">
<span class="text-xs font-bold uppercase opacity-50">LLM API Base URL</span> <span class="text-xs font-bold uppercase opacity-50"
<input type="text" bind:value={cfg_json.llm__api_base_url} class="input variant-form-material" /> >LLM API Base URL</span>
<input
type="text"
bind:value={cfg_json.llm__api_base_url}
class="input variant-form-material" />
</label> </label>
<label class="label"> <label class="label">
<span class="text-xs font-bold uppercase opacity-50">LLM Model</span> <span class="text-xs font-bold uppercase opacity-50"
<input type="text" bind:value={cfg_json.llm__api_model} class="input variant-form-material" /> >LLM Model</span>
<input
type="text"
bind:value={cfg_json.llm__api_model}
class="input variant-form-material" />
</label> </label>
</div> </div>
<label class="label"> <label class="label">
<span class="text-xs font-bold uppercase opacity-50">API Token</span> <span class="text-xs font-bold uppercase opacity-50"
<input type="password" bind:value={cfg_json.llm__api_token} class="input variant-form-material font-mono" /> >API Token</span>
<input
type="password"
bind:value={cfg_json.llm__api_token}
class="input variant-form-material font-mono" />
</label> </label>
<label class="label"> <label class="label">
<span class="text-xs font-bold uppercase opacity-50">System Prompt</span> <span class="text-xs font-bold uppercase opacity-50"
<textarea bind:value={cfg_json.llm__system_prompt} class="textarea variant-form-material h-24 text-sm"></textarea> >System Prompt</span>
<textarea
bind:value={cfg_json.llm__system_prompt}
class="textarea variant-form-material h-24 text-sm"
></textarea>
</label> </label>
<label class="flex items-center space-x-2"> <label class="flex items-center space-x-2">
<input type="checkbox" bind:checked={cfg_json.llm__api_dangerous_browser} class="checkbox" /> <input
<span class="text-xs font-bold uppercase opacity-50">Allow Browser Fetch (Dangerously)</span> 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> </label>
</div> </div>
{:else if active_tab === 'refresh'} {: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"> <label class="label">
<span class="text-xs font-bold uppercase opacity-50">Default (Minutes)</span> <span class="text-xs font-bold uppercase opacity-50"
<input type="number" bind:value={cfg_json.default_refresh_minutes} class="input variant-form-material" /> >Default (Minutes)</span>
<input
type="number"
bind:value={cfg_json.default_refresh_minutes}
class="input variant-form-material" />
</label> </label>
<label class="label"> <label class="label">
<span class="text-xs font-bold uppercase opacity-50">Authenticated (Minutes)</span> <span class="text-xs font-bold uppercase opacity-50"
<input type="number" bind:value={cfg_json.authenticated_refresh_time} class="input variant-form-material" /> >Authenticated (Minutes)</span>
<input
type="number"
bind:value={cfg_json.authenticated_refresh_time}
class="input variant-form-material" />
</label> </label>
<label class="label"> <label class="label">
<span class="text-xs font-bold uppercase opacity-50">Trusted (Minutes)</span> <span class="text-xs font-bold uppercase opacity-50"
<input type="number" bind:value={cfg_json.trusted_refresh_minutes} class="input variant-form-material" /> >Trusted (Minutes)</span>
<input
type="number"
bind:value={cfg_json.trusted_refresh_minutes}
class="input variant-form-material" />
</label> </label>
<label class="label"> <label class="label">
<span class="text-xs font-bold uppercase opacity-50">Manager (Minutes)</span> <span class="text-xs font-bold uppercase opacity-50"
<input type="number" bind:value={cfg_json.manager_refresh_minutes} class="input variant-form-material" /> >Manager (Minutes)</span>
<input
type="number"
bind:value={cfg_json.manager_refresh_minutes}
class="input variant-form-material" />
</label> </label>
</div> </div>
{:else if active_tab === 'idaa'} {: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 --> <!-- Novi API -->
<section class="space-y-4 p-4 bg-surface-500/5 rounded-xl border border-surface-500/10"> <section
<h4 class="text-sm font-black flex items-center gap-2"> 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 <Globe size="1.1em" class="text-primary-500" /> Novi API Connection
</h4> </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"> <label class="label">
<span class="text-xs font-bold uppercase opacity-50">Root URL</span> <span class="text-xs font-bold uppercase opacity-50"
<input type="text" bind:value={cfg_json.novi_api_root_url} class="input variant-form-material" /> >Root URL</span>
<input
type="text"
bind:value={cfg_json.novi_api_root_url}
class="input variant-form-material" />
</label> </label>
<label class="label"> <label class="label">
<span class="text-xs font-bold uppercase opacity-50">API Key</span> <span class="text-xs font-bold uppercase opacity-50"
<input type="password" bind:value={cfg_json.novi_idaa_api_key} class="input variant-form-material font-mono" /> >API Key</span>
<input
type="password"
bind:value={cfg_json.novi_idaa_api_key}
class="input variant-form-material font-mono" />
</label> </label>
</div> </div>
</section> </section>
<!-- UUID Lists --> <!-- UUID Lists -->
<section class="grid grid-cols-1 md:grid-cols-2 gap-4"> <section class="grid grid-cols-1 gap-4 md:grid-cols-2">
{#each [ {#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)}
{ key: 'novi_admin_li', label: 'Novi Admins', color: 'text-error-500' }, <div class="bg-surface-500/5 space-y-2 rounded-lg p-3">
{ key: 'novi_trusted_li', label: 'Novi Trusted', color: 'text-warning-500' }, <header class="flex items-center justify-between">
{ key: 'novi_jitsi_mod_li', label: 'Jitsi Moderators', color: 'text-primary-500' }, <span
{ key: 'novi_idaa_group_guid_li', label: 'Member Group GUIDs', color: 'text-secondary-500' } class="text-[10px] font-black tracking-wider uppercase {list.color}"
] as list (list.key)} >{list.label}</span>
<div class="space-y-2 p-3 bg-surface-500/5 rounded-lg"> <button
<header class="flex justify-between items-center"> class="btn btn-icon btn-icon-sm variant-soft-primary"
<span class="text-[10px] font-black uppercase tracking-wider {list.color}">{list.label}</span> onclick={() => add_to_list(list.key)}>
<button class="btn btn-icon btn-icon-sm variant-soft-primary" onclick={() => add_to_list(list.key)}>
<Plus size="12" /> <Plus size="12" />
</button> </button>
</header> </header>
<div class="space-y-1"> <div class="space-y-1">
{#each cfg_json[list.key] ?? [] as uuid, i (uuid)} {#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]"> <div
<span class="grow truncate">{uuid}</span> class="bg-surface-500/10 flex items-center gap-1 rounded p-1 font-mono text-[10px]">
<button class="text-error-500 hover:scale-110 transition-transform" onclick={() => remove_from_list(list.key, i)}> <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" /> <Minus size="12" />
</button> </button>
</div> </div>
@@ -240,62 +363,96 @@
</section> </section>
<!-- Notifications --> <!-- 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"> <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"> <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> <span>Notify Staff (New)</span>
</label> </label>
<label class="flex items-center space-x-2 text-xs"> <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> <span>Notify Staff (Update)</span>
</label> </label>
<label class="flex items-center space-x-2 text-xs"> <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> <span>Notify Poster</span>
</label> </label>
<label class="flex items-center space-x-2 text-xs"> <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> <span>Notify Commenters</span>
</label> </label>
</div> </div>
<div class="space-y-2"> <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"> <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> <span>Notify Staff (New)</span>
</label> </label>
<label class="flex items-center space-x-2 text-xs"> <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> <span>Notify Staff (Update)</span>
</label> </label>
</div> </div>
</section> </section>
</div> </div>
{:else if active_tab === 'raw'} {: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 <AE_Comp_Editor_CodeMirror
content={raw_json_str} content={raw_json_str}
bind:new_content={raw_json_str} bind:new_content={raw_json_str}
language="json" language="json"
theme_mode={$ae_loc.theme_mode} 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> </div>
{/if} {/if}
</div> </div>
<!-- Action Bar --> <!-- 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"> <div class="flex items-center gap-2">
<label class="flex items-center space-x-2 cursor-pointer"> <label class="flex cursor-pointer items-center space-x-2">
<input type="checkbox" bind:checked={cfg_json.test} class="checkbox" /> <input
<span class="text-xs font-bold uppercase text-warning-500">Test Mode</span> type="checkbox"
bind:checked={cfg_json.test}
class="checkbox" />
<span class="text-warning-500 text-xs font-bold uppercase"
>Test Mode</span>
</label> </label>
</div> </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 <Save size="1.1em" class="mr-2" /> Save Config
</button> </button>
</div> </div>

View File

@@ -24,7 +24,9 @@ export async function load_ae_obj_id__account({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_Account | null> { }): Promise<ae_Account | null> {
if (log_lvl) { 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 ae_promises.load__account_obj = await api
@@ -39,10 +41,11 @@ export async function load_ae_obj_id__account({
.then(async function (account_obj_get_result) { .then(async function (account_obj_get_result) {
if (account_obj_get_result) { if (account_obj_get_result) {
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__account_props({ const processed_obj_li =
obj_li: [account_obj_get_result], await process_ae_obj__account_props({
log_lvl: log_lvl obj_li: [account_obj_get_result],
}); log_lvl: log_lvl
});
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_core, db_instance: db_core,
table_name: 'account', table_name: 'account',
@@ -114,10 +117,11 @@ export async function load_ae_obj_li__account({
.then(async function (account_obj_li_get_result) { .then(async function (account_obj_li_get_result) {
if (account_obj_li_get_result) { if (account_obj_li_get_result) {
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__account_props({ const processed_obj_li =
obj_li: account_obj_li_get_result, await process_ae_obj__account_props({
log_lvl: log_lvl obj_li: account_obj_li_get_result,
}); log_lvl: log_lvl
});
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_core, db_instance: db_core,
table_name: 'account', table_name: 'account',
@@ -164,10 +168,11 @@ export async function create_ae_obj__account({
.then(async function (account_obj_create_result) { .then(async function (account_obj_create_result) {
if (account_obj_create_result) { if (account_obj_create_result) {
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__account_props({ const processed_obj_li =
obj_li: [account_obj_create_result], await process_ae_obj__account_props({
log_lvl: log_lvl obj_li: [account_obj_create_result],
}); log_lvl: log_lvl
});
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_core, db_instance: db_core,
table_name: 'account', table_name: 'account',
@@ -206,7 +211,10 @@ export async function update_ae_obj__account({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_Account | null> { }): Promise<ae_Account | null> {
if (log_lvl) { 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({ const result = await api.update_ae_obj({
@@ -256,7 +264,9 @@ export async function delete_ae_obj_id__account({
log_lvl?: number; log_lvl?: number;
}) { }) {
if (log_lvl) { 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 ae_promises.delete__account_obj = await api
@@ -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 updated = processed_obj.updated_on ?? processed_obj.created_on;
const name = processed_obj.name ?? ''; const name = processed_obj.name ?? '';
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`; (processed_obj as any).tmp_sort_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
(processed_obj as any).tmp_sort_2 =
`${group}_${priority}_${sort}_${name}_${updated}`;
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);

View File

@@ -19,7 +19,9 @@ export async function load_ae_obj_id__activity_log({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_ActivityLog | null> { }): Promise<ae_ActivityLog | null> {
if (log_lvl) { 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({ ae_promises.load__activity_log_obj = await api.get_ae_obj({
@@ -61,7 +63,9 @@ export async function load_ae_obj_li__activity_log({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_ActivityLog[]> { }): Promise<ae_ActivityLog[]> {
if (log_lvl) { 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({ ae_promises.load__activity_log_obj_li = await api.get_ae_obj_li({
@@ -96,11 +100,15 @@ export async function create_ae_obj__activity_log({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_ActivityLog | null> { }): Promise<ae_ActivityLog | null> {
if (log_lvl) { 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) { 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; return null;
} }
@@ -133,7 +141,9 @@ export async function update_ae_obj__activity_log({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_ActivityLog | null> { }): Promise<ae_ActivityLog | null> {
if (log_lvl) { 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({ ae_promises.update__activity_log_obj = await api.update_ae_obj({
@@ -151,7 +161,6 @@ export async function update_ae_obj__activity_log({
// Updated 2026-01-07 // Updated 2026-01-07
export async function qry__activity_log({ export async function qry__activity_log({
api_cfg, api_cfg,
account_id, account_id,
@@ -173,9 +182,7 @@ export async function qry__activity_log({
order_by_li = { created_on: 'DESC' }, order_by_li = { created_on: 'DESC' },
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
account_id: string; account_id: string;
@@ -197,49 +204,36 @@ export async function qry__activity_log({
order_by_li?: Record<string, 'ASC' | 'DESC'>; order_by_li?: Record<string, 'ASC' | 'DESC'>;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_ActivityLog[]> { }): Promise<ae_ActivityLog[]> {
const search_query: any = {}; const search_query: any = {};
const filters: any[] = []; const filters: any[] = [];
if (account_id) { if (account_id) {
filters.push({
filters.push({ field: 'account_id_random', op: 'eq', value: account_id }); field: 'account_id_random',
op: 'eq',
value: account_id
});
} }
if (qry_person_id) { if (qry_person_id) {
filters.push({
filters.push({ field: 'person_id_random', op: 'eq', value: qry_person_id }); field: 'person_id_random',
op: 'eq',
value: qry_person_id
});
} }
if (filters.length > 0) { if (filters.length > 0) {
search_query.and = filters; search_query.and = filters;
} }
if (qry_str) { if (qry_str) {
search_query.q = qry_str; search_query.q = qry_str;
} }
ae_promises.load__activity_log_obj_li = await api.search_ae_obj({ ae_promises.load__activity_log_obj_li = await api.search_ae_obj({
api_cfg, api_cfg,
obj_type: 'activity_log', obj_type: 'activity_log',
@@ -259,13 +253,9 @@ export async function qry__activity_log({
order_by_li, order_by_li,
log_lvl log_lvl
}); });
return ae_promises.load__activity_log_obj_li; return ae_promises.load__activity_log_obj_li;
} }
// Updated 2026-02-16 // 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 group = processed_obj.group ?? '0';
const priority = processed_obj.priority ? 1 : 0; const priority = processed_obj.priority ? 1 : 0;
const sort = processed_obj.sort ?? '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 ?? ''; const name = processed_obj.name ?? '';
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`; (processed_obj as any).tmp_sort_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
(processed_obj as any).tmp_sort_2 =
`${group}_${priority}_${sort}_${name}_${updated}`;
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);
@@ -367,5 +364,3 @@ export async function process_ae_obj__activity_log_props({
log_lvl log_lvl
}); });
} }

View File

@@ -23,29 +23,34 @@ export async function load_ae_obj_id__address({
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_Address | null> { }): Promise<ae_Address | null> {
ae_promises.load__address_obj = await api.get_ae_obj({ ae_promises.load__address_obj = await api
api_cfg, .get_ae_obj({
obj_type: 'address', api_cfg,
obj_id: address_id, obj_type: 'address',
view, obj_id: address_id,
params, view,
log_lvl params,
}).then(async (result) => { log_lvl
if (result) { })
if (try_cache) { .then(async (result) => {
const processed = await process_ae_obj__address_props({ obj_li: [result], log_lvl }); if (result) {
await db_save_ae_obj_li__ae_obj({ if (try_cache) {
db_instance: db_core, const processed = await process_ae_obj__address_props({
table_name: 'address', obj_li: [result],
obj_li: processed, log_lvl
properties_to_save, });
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; return ae_promises.load__address_obj;
} }
@@ -77,34 +82,39 @@ export async function load_ae_obj_li__address({
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_Address[]> { }): Promise<ae_Address[]> {
ae_promises.load__address_obj_li = await api.get_ae_obj_li({ ae_promises.load__address_obj_li = await api
api_cfg, .get_ae_obj_li({
obj_type: 'address', api_cfg,
for_obj_type, obj_type: 'address',
for_obj_id, for_obj_type,
enabled, for_obj_id,
hidden, enabled,
view, hidden,
limit, view,
offset, limit,
order_by_li, offset,
log_lvl order_by_li,
}).then(async (result) => { log_lvl
if (result && Array.isArray(result)) { })
if (try_cache) { .then(async (result) => {
const processed = await process_ae_obj__address_props({ obj_li: result, log_lvl }); if (result && Array.isArray(result)) {
await db_save_ae_obj_li__ae_obj({ if (try_cache) {
db_instance: db_core, const processed = await process_ae_obj__address_props({
table_name: 'address', obj_li: result,
obj_li: processed, log_lvl
properties_to_save, });
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; return ae_promises.load__address_obj_li;
} }
@@ -136,7 +146,10 @@ export async function create_ae_obj__address({
}); });
if (result && try_cache) { 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({ await db_save_ae_obj_li__ae_obj({
db_instance: db_core, db_instance: db_core,
table_name: 'address', table_name: 'address',
@@ -174,7 +187,10 @@ export async function update_ae_obj__address({
}); });
if (result && try_cache) { 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({ await db_save_ae_obj_li__ae_obj({
db_instance: db_core, db_instance: db_core,
table_name: 'address', table_name: 'address',
@@ -269,7 +285,9 @@ async function _process_generic_props<T extends Record<string, any>>({
} }
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);

View File

@@ -23,29 +23,34 @@ export async function load_ae_obj_id__contact({
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_Contact | null> { }): Promise<ae_Contact | null> {
ae_promises.load__contact_obj = await api.get_ae_obj({ ae_promises.load__contact_obj = await api
api_cfg, .get_ae_obj({
obj_type: 'contact', api_cfg,
obj_id: contact_id, obj_type: 'contact',
view, obj_id: contact_id,
params, view,
log_lvl params,
}).then(async (result) => { log_lvl
if (result) { })
if (try_cache) { .then(async (result) => {
const processed = await process_ae_obj__contact_props({ obj_li: [result], log_lvl }); if (result) {
await db_save_ae_obj_li__ae_obj({ if (try_cache) {
db_instance: db_core, const processed = await process_ae_obj__contact_props({
table_name: 'contact', obj_li: [result],
obj_li: processed, log_lvl
properties_to_save, });
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; return ae_promises.load__contact_obj;
} }
@@ -75,34 +80,39 @@ export async function load_ae_obj_li__contact({
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_Contact[]> { }): Promise<ae_Contact[]> {
ae_promises.load__contact_obj_li = await api.get_ae_obj_li({ ae_promises.load__contact_obj_li = await api
api_cfg, .get_ae_obj_li({
obj_type: 'contact', api_cfg,
for_obj_type, obj_type: 'contact',
for_obj_id, for_obj_type,
enabled, for_obj_id,
hidden, enabled,
view, hidden,
limit, view,
offset, limit,
order_by_li, offset,
log_lvl order_by_li,
}).then(async (result) => { log_lvl
if (result && Array.isArray(result)) { })
if (try_cache) { .then(async (result) => {
const processed = await process_ae_obj__contact_props({ obj_li: result, log_lvl }); if (result && Array.isArray(result)) {
await db_save_ae_obj_li__ae_obj({ if (try_cache) {
db_instance: db_core, const processed = await process_ae_obj__contact_props({
table_name: 'contact', obj_li: result,
obj_li: processed, log_lvl
properties_to_save, });
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; return ae_promises.load__contact_obj_li;
} }
@@ -134,7 +144,10 @@ export async function create_ae_obj__contact({
}); });
if (result && try_cache) { 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({ await db_save_ae_obj_li__ae_obj({
db_instance: db_core, db_instance: db_core,
table_name: 'contact', table_name: 'contact',
@@ -172,7 +185,10 @@ export async function update_ae_obj__contact({
}); });
if (result && try_cache) { 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({ await db_save_ae_obj_li__ae_obj({
db_instance: db_core, db_instance: db_core,
table_name: 'contact', table_name: 'contact',
@@ -267,7 +283,9 @@ async function _process_generic_props<T extends Record<string, any>>({
} }
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);

View File

@@ -39,10 +39,12 @@ export async function load_ae_obj_id__person({
.then(async function (result) { .then(async function (result) {
if (result) { if (result) {
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__person_props({ const processed_obj_li = await process_ae_obj__person_props(
obj_li: [result], {
log_lvl obj_li: [result],
}); log_lvl
}
);
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_core, db_instance: db_core,
table_name: 'person', table_name: 'person',
@@ -98,7 +100,9 @@ export async function load_ae_obj_li__person({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_Person[]> { }): Promise<ae_Person[]> {
if (log_lvl) { 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; let promise;
@@ -109,11 +113,19 @@ export async function load_ae_obj_li__person({
}; };
if (qry_user_id) { 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) { 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) { if (for_obj_id) {
@@ -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) { ae_promises.load__person_obj_li = await promise.then(
if (result_li) { async function (result_li) {
if (try_cache) { if (result_li) {
const processed_obj_li = await process_ae_obj__person_props({ if (try_cache) {
obj_li: result_li, const processed_obj_li = await process_ae_obj__person_props(
log_lvl {
}); obj_li: result_li,
await db_save_ae_obj_li__ae_obj({ log_lvl
db_instance: db_core, }
table_name: 'person', );
obj_li: processed_obj_li, await db_save_ae_obj_li__ae_obj({
properties_to_save: properties_to_save, db_instance: db_core,
log_lvl 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; return ae_promises.load__person_obj_li;
} }
@@ -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 updated = processed_obj.updated_on ?? processed_obj.created_on;
const name = processed_obj.full_name ?? processed_obj.name ?? ''; 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_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
(processed_obj as any).tmp_sort_2 =
`${group}_${priority}_${sort}_${name}_${updated}`;
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);

View File

@@ -113,10 +113,18 @@ export async function lookup_site_domain({
try { try {
cached = await db_core.site_domain.where('fqdn').equals(fqdn).first(); cached = await db_core.site_domain.where('fqdn').equals(fqdn).first();
if (cached) { 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 // Trigger background refresh to keep cache fresh, but don't await it
_refresh_site_domain_background({ api_cfg, fqdn, view, log_lvl: 0 }); _refresh_site_domain_background({
api_cfg,
fqdn,
view,
log_lvl: 0
});
return cached as any; return cached as any;
} }
@@ -125,13 +133,23 @@ export async function lookup_site_domain({
} }
// 2. SLOW PATH: Wait for API if cache is empty // 2. SLOW PATH: Wait for API if cache is empty
return await _refresh_site_domain_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 * Internal helper to perform the actual API fetch and cache update
*/ */
async function _refresh_site_domain_background({ api_cfg, fqdn, view, log_lvl }: any) { async function _refresh_site_domain_background({
api_cfg,
fqdn,
view,
log_lvl
}: any) {
try { try {
const guest_api_cfg = { ...api_cfg }; const guest_api_cfg = { ...api_cfg };
guest_api_cfg.headers = { ...api_cfg.headers }; guest_api_cfg.headers = { ...api_cfg.headers };
@@ -144,7 +162,7 @@ async function _refresh_site_domain_background({ api_cfg, fqdn, view, log_lvl }:
'JWT' 'JWT'
]; ];
auth_props.forEach(prop => { auth_props.forEach((prop) => {
delete guest_api_cfg.headers[prop]; delete guest_api_cfg.headers[prop];
delete guest_api_cfg.headers[prop.toLowerCase()]; delete guest_api_cfg.headers[prop.toLowerCase()];
delete guest_api_cfg.headers[prop.replaceAll('-', '_')]; delete guest_api_cfg.headers[prop.replaceAll('-', '_')];
@@ -481,11 +499,12 @@ export async function load_ae_obj_li__site_domain({
.then(async function (domain_li) { .then(async function (domain_li) {
if (domain_li) { if (domain_li) {
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__site_domain_props({ const processed_obj_li =
obj_li: domain_li, await process_ae_obj__site_domain_props({
site_id, obj_li: domain_li,
log_lvl site_id,
}); log_lvl
});
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_core, db_instance: db_core,
table_name: 'site_domain', table_name: 'site_domain',
@@ -729,11 +748,15 @@ async function _process_generic_props<T extends Record<string, any>>({
const updated = processed_obj.updated_on ?? processed_obj.created_on; const updated = processed_obj.updated_on ?? processed_obj.created_on;
const name = processed_obj.name ?? processed_obj.fqdn ?? ''; 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_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
(processed_obj as any).tmp_sort_2 =
`${group}_${priority}_${sort}_${name}_${updated}`;
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);

View File

@@ -93,7 +93,9 @@ export async function load_ae_obj_li__user({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_User[]> { }): Promise<ae_User[]> {
if (log_lvl) { 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 // SCENARIO A: Text Search
@@ -107,9 +109,17 @@ export async function load_ae_obj_li__user({
] ]
}); });
} else if (for_obj_id) { } 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) { } 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({ return await api.search_ae_obj({
@@ -130,13 +140,33 @@ export async function load_ae_obj_li__user({
if (for_obj_id && include_global) { if (for_obj_id && include_global) {
if (log_lvl) console.log('Strategy: Multi-call (Account + Global)'); if (log_lvl) console.log('Strategy: Multi-call (Account + Global)');
const [acct_users, global_users] = await Promise.all([ 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({
load_ae_obj_li__user({ api_cfg, for_obj_id: null, include_global: true, enabled, hidden, view, limit, log_lvl }) 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 // Merge and unique-ify by ID
const merged = [...acct_users, ...global_users]; 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; return unique;
} }
@@ -162,7 +192,8 @@ export async function load_ae_obj_li__user({
} }
// SCENARIO D: Account Only or Everything (confirmed working List API) // 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})`); if (log_lvl)
console.log(`Strategy: Standard List API (for_obj_id=${for_obj_id})`);
return await api.get_ae_obj_li({ return await api.get_ae_obj_li({
api_cfg, api_cfg,
obj_type: 'user', obj_type: 'user',
@@ -383,7 +414,10 @@ export async function auth_ae_obj__username_password({
}); });
if (log_lvl) { 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; return ae_promises.auth__username_password;
} }
@@ -450,7 +484,10 @@ export async function auth_ae_obj__user_id_user_auth_key({
}); });
if (log_lvl) { 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; return ae_promises.auth__user_id_user_key;
} }
@@ -525,7 +562,9 @@ export async function qry_ae_obj_li__user_email({
log_lvl?: number; log_lvl?: number;
}) { }) {
if (log_lvl) { 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'; const endpoint = '/user/lookup_email';
@@ -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 updated = processed_obj.updated_on ?? processed_obj.created_on;
const name = processed_obj.username ?? processed_obj.name ?? ''; 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_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
(processed_obj as any).tmp_sort_2 =
`${group}_${priority}_${sort}_${name}_${updated}`;
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);

View File

@@ -25,7 +25,10 @@ import {
auth_ae_obj__user_id_change_password auth_ae_obj__user_id_change_password
} from '$lib/ae_core/ae_core__user'; } 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'; 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) { 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; return false;
} }
@@ -240,7 +245,10 @@ async function load_ae_obj_code__data_store({
get_ds_result get_ds_result
); );
} }
localStorage.setItem(`${key_prefix}${code}`, JSON.stringify(get_ds_result)); localStorage.setItem(
`${key_prefix}${code}`,
JSON.stringify(get_ds_result)
);
} else { } else {
if (log_lvl) { if (log_lvl) {
console.log( console.log(
@@ -491,7 +499,10 @@ async function download_export__obj_type({
log_lvl: log_lvl 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; return ae_promises.download__export_file;
} }

View File

@@ -13,12 +13,17 @@ export function add_url_params({
log_lvl?: number; log_lvl?: number;
}) { }) {
if (log_lvl) { 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); 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) { if (log_lvl) {
console.log('New URL:', url_obj.toString()); 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. // This is used to clean the header property names. Not underscores allowed in the header names.
// Updated 2025-01-28 // 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) { if (log_lvl) {
console.log(`*** clean_headers() ***`, headers); console.log(`*** clean_headers() ***`, headers);
} }

View File

@@ -33,8 +33,14 @@ async function _refresh_lu_country_background({
if (result?.length) { if (result?.length) {
await db_lookups.lu_country.clear(); await db_lookups.lu_country.clear();
await db_lookups.lu_country.bulkPut(result); await db_lookups.lu_country.bulkPut(result);
await db_lookups.lu_cache_meta.put({ lu_type: 'country', refreshed_at: Date.now() }); await db_lookups.lu_cache_meta.put({
if (log_lvl) console.log(`lu_country: saved ${result.length} records to IDB`); lu_type: 'country',
refreshed_at: Date.now()
});
if (log_lvl)
console.log(
`lu_country: saved ${result.length} records to IDB`
);
} }
} catch (error) { } catch (error) {
console.error('lu_country refresh failed:', error); console.error('lu_country refresh failed:', error);
@@ -59,6 +65,8 @@ export async function load_ae_obj_li__country({
// Fire-and-forget — liveQuery subscribers receive updates when IDB is written // Fire-and-forget — liveQuery subscribers receive updates when IDB is written
_refresh_lu_country_background({ api_cfg, log_lvl }); _refresh_lu_country_background({ api_cfg, log_lvl });
} else if (log_lvl) { } else if (log_lvl) {
console.log(`lu_country: IDB fresh (${count} records), skipping refresh`); console.log(
`lu_country: IDB fresh (${count} records), skipping refresh`
);
} }
} }

View File

@@ -19,7 +19,8 @@ async function _refresh_lu_country_subdivision_background({
api_cfg: any; api_cfg: any;
log_lvl?: number; log_lvl?: number;
}) { }) {
if (log_lvl) console.log('*** _refresh_lu_country_subdivision_background() ***'); if (log_lvl)
console.log('*** _refresh_lu_country_subdivision_background() ***');
try { try {
const result = await api.get_ae_obj_li_for_lu({ const result = await api.get_ae_obj_li_for_lu({
api_cfg, api_cfg,
@@ -37,7 +38,9 @@ async function _refresh_lu_country_subdivision_background({
refreshed_at: Date.now() refreshed_at: Date.now()
}); });
if (log_lvl) if (log_lvl)
console.log(`lu_country_subdivision: saved ${result.length} records to IDB`); console.log(
`lu_country_subdivision: saved ${result.length} records to IDB`
);
} }
} catch (error) { } catch (error) {
console.error('lu_country_subdivision refresh failed:', error); console.error('lu_country_subdivision refresh failed:', error);
@@ -61,6 +64,8 @@ export async function load_ae_obj_li__country_subdivision({
if (count === 0 || is_stale) { if (count === 0 || is_stale) {
_refresh_lu_country_subdivision_background({ api_cfg, log_lvl }); _refresh_lu_country_subdivision_background({ api_cfg, log_lvl });
} else if (log_lvl) { } else if (log_lvl) {
console.log(`lu_country_subdivision: IDB fresh (${count} records), skipping refresh`); console.log(
`lu_country_subdivision: IDB fresh (${count} records), skipping refresh`
);
} }
} }

View File

@@ -50,16 +50,24 @@ export async function load_ae_obj_by_code__data_store({
return null; 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 (!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; return null;
} }
// Map content fields for convenience // Map content fields for convenience
const text_val = get_ds_result.text || ''; 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 = { const mapped_ds: ae_DataStore = {
...get_ds_result, ...get_ds_result,
@@ -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 === 'html') return mapped_ds.html;
if (data_type === 'json') return mapped_ds.json; if (data_type === 'json') return mapped_ds.json;
return mapped_ds.text; return mapped_ds.text;
} catch (error) { } catch (error) {
if (log_lvl) console.error('*ae_func* Fetch failed.', error); if (log_lvl) console.error('*ae_func* Fetch failed.', error);
return null; return null;

View File

@@ -22,7 +22,9 @@ export async function load_ae_obj_id__hosted_file({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_HostedFile | null> { }): Promise<ae_HostedFile | null> {
if (log_lvl) { 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 { try {
@@ -36,10 +38,11 @@ export async function load_ae_obj_id__hosted_file({
if (ae_promises.load__hosted_file_obj) { if (ae_promises.load__hosted_file_obj) {
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__hosted_file_props({ const processed_obj_li =
obj_li: [ae_promises.load__hosted_file_obj], await process_ae_obj__hosted_file_props({
log_lvl obj_li: [ae_promises.load__hosted_file_obj],
}); log_lvl
});
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_core, db_instance: db_core,
table_name: 'file', table_name: 'file',
@@ -49,12 +52,14 @@ export async function load_ae_obj_id__hosted_file({
}); });
} }
} else if (try_cache) { } 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) { } catch (error: any) {
console.log('V3 Request failed.', error); console.log('V3 Request failed.', error);
if (try_cache) { 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,7 +95,9 @@ export async function load_ae_obj_li__hosted_file({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_HostedFile[]> { }): Promise<ae_HostedFile[]> {
if (log_lvl) { 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 { try {
@@ -109,10 +116,11 @@ export async function load_ae_obj_li__hosted_file({
if (ae_promises.load__hosted_file_obj_li) { if (ae_promises.load__hosted_file_obj_li) {
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__hosted_file_props({ const processed_obj_li =
obj_li: ae_promises.load__hosted_file_obj_li, await process_ae_obj__hosted_file_props({
log_lvl obj_li: ae_promises.load__hosted_file_obj_li,
}); log_lvl
});
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_core, db_instance: db_core,
table_name: 'file', table_name: 'file',
@@ -123,14 +131,16 @@ export async function load_ae_obj_li__hosted_file({
} }
} else if (try_cache) { } else if (try_cache) {
ae_promises.load__hosted_file_obj_li = await db_core.file 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(); .toArray();
} }
} catch (error: any) { } catch (error: any) {
console.log('V3 List Request failed.', error); console.log('V3 List Request failed.', error);
if (try_cache) { if (try_cache) {
ae_promises.load__hosted_file_obj_li = await db_core.file 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(); .toArray();
} }
} }
@@ -159,7 +169,9 @@ export async function delete_ae_obj_id__hosted_file({
log_lvl?: number; log_lvl?: number;
}) { }) {
if (log_lvl) { 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 // Use the specialized hosted file delete endpoint
@@ -203,7 +215,9 @@ export async function download_ae_obj_id__hosted_file({
log_lvl?: number; log_lvl?: number;
}) { }) {
if (log_lvl) { 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; 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 updated = processed_obj.updated_on ?? processed_obj.created_on;
const name = processed_obj.name ?? ''; const name = processed_obj.name ?? '';
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`; (processed_obj as any).tmp_sort_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
(processed_obj as any).tmp_sort_2 =
`${group}_${priority}_${sort}_${name}_${updated}`;
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);

View File

@@ -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 (data_to_save.length === 0) {
if (log_lvl > 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 []; 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. // bulkPut efficiently handles both inserts and updates.
const keys = await db_table.bulkPut(data_to_save); const keys = await db_table.bulkPut(data_to_save);
if (log_lvl > 0) { 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; return keys;
} catch (error) { } catch (error) {

View File

@@ -49,7 +49,8 @@ export async function generate_qr_code({
if (qr_type == 'vcard') { if (qr_type == 'vcard') {
if (qr_data.informal_name) { 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 { } else {
params['n'] = `${qr_data.family_name};${qr_data.given_name}`; 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 is true, ensure we return an object URL for use in <img src=...>
if (return_blob) { 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 already a Blob, use it directly
if (data instanceof Blob) { if (data instanceof Blob) {
@@ -133,7 +135,8 @@ export async function generate_qr_code({
const blob = new Blob([data as BlobPart], { type: 'image/png' }); const blob = new Blob([data as BlobPart], { type: 'image/png' });
return URL.createObjectURL(blob); return URL.createObjectURL(blob);
} catch (e) { } 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; 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. * @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. * @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 { const {
n = '', n = '',
fn = '', fn = '',
@@ -179,7 +185,8 @@ export async function js_generate_qr_code(qr_type: string, params: key_val = {})
log_lvl = 0 log_lvl = 0
} = params; } = params;
if (log_lvl >= 2) 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; let qr_data: string | null = null;
@@ -211,13 +218,15 @@ export async function js_generate_qr_code(qr_type: string, params: key_val = {})
case 'obj': case 'obj':
// Custom format: OBJ:ot:obj_type,oi:obj_id // 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}`; qr_data = `OBJ:ot:${obj_type},oi:${obj_id}`;
break; break;
case 'kv': case 'kv':
// Custom format: KV:k:"key",v:"val" // 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}"`; qr_data = `KV:k:"${key}",v:"${val}"`;
break; break;
@@ -229,7 +238,8 @@ export async function js_generate_qr_code(qr_type: string, params: key_val = {})
case 'str': case 'str':
// Raw string data // 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; qr_data = str;
break; break;

View File

@@ -27,7 +27,7 @@ async function _refresh_lu_time_zone_background({
for_lu_type: 'time_zone', for_lu_type: 'time_zone',
enabled: 'enabled', enabled: 'enabled',
hidden: 'not_hidden', hidden: 'not_hidden',
only_priority: true, // ~72 priority timezone records only_priority: true, // ~72 priority timezone records
limit: 1800, limit: 1800,
log_lvl log_lvl
}); });
@@ -38,7 +38,10 @@ async function _refresh_lu_time_zone_background({
lu_type: 'time_zone', lu_type: 'time_zone',
refreshed_at: Date.now() refreshed_at: Date.now()
}); });
if (log_lvl) console.log(`lu_time_zone: saved ${result.length} records to IDB`); if (log_lvl)
console.log(
`lu_time_zone: saved ${result.length} records to IDB`
);
} }
} catch (error) { } catch (error) {
console.error('lu_time_zone refresh failed:', error); console.error('lu_time_zone refresh failed:', error);
@@ -62,6 +65,8 @@ export async function load_ae_obj_li__time_zone({
if (count === 0 || is_stale) { if (count === 0 || is_stale) {
_refresh_lu_time_zone_background({ api_cfg, log_lvl }); _refresh_lu_time_zone_background({ api_cfg, log_lvl });
} else if (log_lvl) { } else if (log_lvl) {
console.log(`lu_time_zone: IDB fresh (${count} records), skipping refresh`); console.log(
`lu_time_zone: IDB fresh (${count} records), skipping refresh`
);
} }
} }

View File

@@ -12,7 +12,7 @@ import Dexie, { type Table } from 'dexie';
export interface LuCountry { export interface LuCountry {
id: number; id: number;
group: string; // dedup key = alpha_2_code (e.g. "US") group: string; // dedup key = alpha_2_code (e.g. "US")
alpha_2_code: string; alpha_2_code: string;
name: string; name: string;
english_short_name?: string; english_short_name?: string;
@@ -22,12 +22,12 @@ export interface LuCountry {
priority?: number; priority?: number;
sort?: number; sort?: number;
account_id?: number | null; account_id?: number | null;
[key: string]: unknown; // allow extra fields from API without TS errors [key: string]: unknown; // allow extra fields from API without TS errors
} }
export interface LuCountrySubdivision { export interface LuCountrySubdivision {
id: number; id: number;
group: string; // dedup key = code (e.g. "US-NY") group: string; // dedup key = code (e.g. "US-NY")
code: string; code: string;
name: string; name: string;
country_alpha_2_code?: string; country_alpha_2_code?: string;
@@ -42,9 +42,9 @@ export interface LuCountrySubdivision {
export interface LuTimeZone { export interface LuTimeZone {
id: number; id: number;
group: string; // dedup key = name (IANA identifier, e.g. "US/Eastern") group: string; // dedup key = name (IANA identifier, e.g. "US/Eastern")
name: string; name: string;
name_override?: string; // display label override; prefer this over name when set name_override?: string; // display label override; prefer this over name when set
enable?: number; enable?: number;
hide?: number; hide?: number;
priority?: number; priority?: number;
@@ -55,7 +55,7 @@ export interface LuTimeZone {
export interface LuCacheMeta { export interface LuCacheMeta {
lu_type: 'country' | 'country_subdivision' | 'time_zone'; lu_type: 'country' | 'country_subdivision' | 'time_zone';
refreshed_at: number; // Unix timestamp ms — used for 24h TTL check refreshed_at: number; // Unix timestamp ms — used for 24h TTL check
} }
class LookupsDexie extends Dexie { class LookupsDexie extends Dexie {
@@ -67,10 +67,10 @@ class LookupsDexie extends Dexie {
constructor() { constructor() {
super('ae_lookups_db'); super('ae_lookups_db');
this.version(1).stores({ this.version(1).stores({
lu_country: 'id, alpha_2_code, group', lu_country: 'id, alpha_2_code, group',
lu_country_subdivision: 'id, code, country_alpha_2_code, group', lu_country_subdivision: 'id, code, country_alpha_2_code, group',
lu_time_zone: 'id, name, group', lu_time_zone: 'id, name, group',
lu_cache_meta: 'lu_type' lu_cache_meta: 'lu_type'
}); });
} }
} }

View File

@@ -1,125 +1,140 @@
<script lang="ts"> <script lang="ts">
/** /**
* AE_AITools.svelte * AE_AITools.svelte
* GENERIC Aether AI Toolset (Runes/Svelte 5) * GENERIC Aether AI Toolset (Runes/Svelte 5)
* Extracted logic from Journals module to be system-wide. * Extracted logic from Journals module to be system-wide.
*/ */
import OpenAI from 'openai'; import OpenAI from 'openai';
import { Modal } from 'flowbite-svelte'; import { Modal } from 'flowbite-svelte';
import { import {
Bot, BotMessageSquare, Loader, FileText, Bot,
Save, FilePenLine, RotateCcw, Settings, BotMessageSquare,
RefreshCcw, Globe, Copy Loader,
} from '@lucide/svelte'; FileText,
import { ae_loc, ae_api } from '$lib/stores/ae_stores'; Save,
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte'; 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 { interface Props {
// Core Props // Core Props
content: string; // The text to summarize/analyze content: string; // The text to summarize/analyze
summary: string; // The result (bindable) summary: string; // The result (bindable)
// Configuration (Bindable for global settings persistence) // Configuration (Bindable for global settings persistence)
model?: string; model?: string;
baseUrl?: string; baseUrl?: string;
token?: string; token?: string;
systemPrompt?: string; systemPrompt?: string;
maxTokens?: number; maxTokens?: number;
temperature?: number; temperature?: number;
// Callbacks // Callbacks
onSave?: (newSummary: string) => void; onSave?: (newSummary: string) => void;
onSyncConfig?: () => void; // Optional: callback to sync from global site config onSyncConfig?: () => void; // Optional: callback to sync from global site config
// UI Customization // UI Customization
buttonClass?: string; buttonClass?: string;
log_lvl?: number; 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 { active_tab = 'result';
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 no token is provided, trigger a "Demo Mode" placeholder after a fake delay
if (model === undefined) model = 'dgrzone-deepseek-8b-quick'; if (!token || token === '') {
if (baseUrl === undefined) baseUrl = 'https://ai.dgrzone.com/api'; console.log('AE_AITools: No token provided. Entering Demo Mode.');
if (token === undefined) token = ''; ae_promises = new Promise((resolve) => {
if (systemPrompt === undefined) systemPrompt = 'You are a helpful assistant.'; setTimeout(() => {
if (maxTokens === undefined) maxTokens = 512; 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}`;
if (temperature === undefined) temperature = 0.7; show_modal = true;
resolve(true);
// Internal State }, 1500);
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
}); });
return;
}
try { const ai_client = new OpenAI({
ae_promises = ai_client.chat.completions.create({ apiKey: token,
baseURL: baseUrl,
dangerouslyAllowBrowser: true
});
try {
ae_promises = ai_client.chat.completions
.create({
model: model || 'dgrzone-deepseek-8b-quick', model: model || 'dgrzone-deepseek-8b-quick',
max_tokens: maxTokens, max_tokens: maxTokens,
temperature: temperature, temperature: temperature,
messages: [ messages: [
{ role: 'system', content: systemPrompt || 'You are a helpful assistant.' }, {
role: 'system',
content: systemPrompt || 'You are a helpful assistant.'
},
{ role: 'user', content: content } { 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; tmp_summary = result;
show_modal = true; show_modal = true;
}); });
} catch (err: any) { } catch (err: any) {
console.error('AE_AITools: AI Error:', err); console.error('AE_AITools: AI Error:', err);
// Even on error, show the modal with the error message so the UI can be inspected // 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.`; 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; show_modal = true;
ae_promises = Promise.resolve(); ae_promises = Promise.resolve();
}
} }
}
function handle_save() { function handle_save() {
summary = tmp_summary; summary = tmp_summary;
if (onSave) onSave(tmp_summary); if (onSave) onSave(tmp_summary);
show_modal = false; show_modal = false;
} }
</script> </script>
<div class="ae-ai-tools-wrapper inline-flex items-center gap-1"> <div class="ae-ai-tools-wrapper inline-flex items-center gap-1">
@@ -128,13 +143,12 @@
type="button" type="button"
onclick={generate_ai_result} onclick={generate_ai_result}
class={buttonClass} class={buttonClass}
title="Generate AI summary/analysis" title="Generate AI summary/analysis">
>
{#await ae_promises} {#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> <span class="text-sm">Processing...</span>
{:then} {: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> <span class="text-sm">Summarize</span>
{:catch} {:catch}
<span class="text-sm text-red-500">Error</span> <span class="text-sm text-red-500">Error</span>
@@ -149,8 +163,7 @@
show_modal = true; show_modal = true;
}} }}
class="btn btn-sm variant-soft-surface shadow-md" class="btn btn-sm variant-soft-surface shadow-md"
title="AI Settings" title="AI Settings">
>
<Settings size="1.2em" /> <Settings size="1.2em" />
</button> </button>
@@ -160,32 +173,37 @@
title="Aether AI Assistant" title="Aether AI Assistant"
bind:open={show_modal} bind:open={show_modal}
size="lg" size="lg"
class="bg-white dark:bg-gray-800" class="bg-white dark:bg-gray-800">
>
<div class="space-y-4 p-2"> <div class="space-y-4 p-2">
<!-- Tab Navigation --> <!-- Tab Navigation -->
<div class="flex gap-1 border-b border-surface-500/20 pb-2"> <div class="border-surface-500/20 flex gap-1 border-b pb-2">
<button <button
class="btn btn-sm {active_tab === 'result' ? 'variant-filled-primary' : 'variant-soft-surface'}" class="btn btn-sm {active_tab === 'result'
onclick={() => active_tab = 'result'} ? 'variant-filled-primary'
> : 'variant-soft-surface'}"
onclick={() => (active_tab = 'result')}>
<Bot size="1.1em" class="mr-1" /> Result <Bot size="1.1em" class="mr-1" /> Result
</button> </button>
<button <button
class="btn btn-sm {active_tab === 'settings' ? 'variant-filled-secondary' : 'variant-soft-surface'}" class="btn btn-sm {active_tab === 'settings'
onclick={() => active_tab = 'settings'} ? 'variant-filled-secondary'
> : 'variant-soft-surface'}"
onclick={() => (active_tab = 'settings')}>
<Settings size="1.1em" class="mr-1" /> Settings <Settings size="1.1em" class="mr-1" /> Settings
</button> </button>
</div> </div>
{#if active_tab === 'result'} {#if active_tab === 'result'}
<div class="space-y-4 animate-in fade-in duration-200"> <div class="animate-in fade-in space-y-4 duration-200">
<div class="flex gap-2 justify-start"> <div class="flex justify-start gap-2">
<button class="btn btn-sm variant-filled-success" onclick={handle_save}> <button
class="btn btn-sm variant-filled-success"
onclick={handle_save}>
<Save size="1.1em" class="mr-1" /> Save Result <Save size="1.1em" class="mr-1" /> Save Result
</button> </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 <RotateCcw size="1.1em" class="mr-1" /> Re-run
</button> </button>
</div> </div>
@@ -195,57 +213,84 @@
bind:new_content={tmp_summary} bind:new_content={tmp_summary}
theme_mode={$ae_loc.theme_mode} theme_mode={$ae_loc.theme_mode}
placeholder="AI Result will appear here..." 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> </div>
{:else} {: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 --> <!-- Connection Settings -->
<div class="space-y-4"> <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 <Globe size="1.1em" /> API Connection
</h3> </h3>
{#if onSyncConfig} {#if onSyncConfig}
<button class="btn btn-sm variant-soft-primary" onclick={onSyncConfig}> <button
<Copy size="1.1em" class="mr-1" /> Sync Global Defaults class="btn btn-sm variant-soft-primary"
onclick={onSyncConfig}>
<Copy size="1.1em" class="mr-1" /> Sync Global
Defaults
</button> </button>
{/if} {/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"> <label class="label">
<span>Base URL</span> <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>
<label class="label"> <label class="label">
<span>Model</span> <span>Model</span>
<input type="text" bind:value={model} class="input input-sm" /> <input
type="text"
bind:value={model}
class="input input-sm" />
</label> </label>
</div> </div>
<label class="label"> <label class="label">
<span>API Token</span> <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> </label>
</div> </div>
<!-- Model Parameters --> <!-- Model Parameters -->
<div class="space-y-4 pt-4 border-t border-surface-500/10"> <div
<h3 class="text-sm font-bold uppercase tracking-widest text-surface-500 flex items-center gap-2"> 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 <FilePenLine size="1.1em" /> Inference Parameters
</h3> </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"> <label class="label">
<span>Temperature ({temperature})</span> <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>
<label class="label"> <label class="label">
<span>Max Tokens</span> <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> </label>
</div> </div>
<label class="label"> <label class="label">
<span>System Prompt</span> <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> </label>
</div> </div>
</div> </div>

View File

@@ -1,58 +1,64 @@
<script lang="ts"> <script lang="ts">
/** /**
* AE_ObjectFlags.svelte * AE_ObjectFlags.svelte
* GENERIC Aether Object Flags & Visibility Toggles * GENERIC Aether Object Flags & Visibility Toggles
* Manages: alert, private, public, personal, professional, template * Manages: alert, private, public, personal, professional, template
*/ */
import { import {
Siren, MessageSquareWarning, Fingerprint, Siren,
Globe, BookHeart, BriefcaseBusiness, NotepadTextDashed, MessageSquareWarning,
Settings Fingerprint,
} from '@lucide/svelte'; Globe,
import { ae_loc } from '$lib/stores/ae_stores'; BookHeart,
BriefcaseBusiness,
NotepadTextDashed,
Settings
} from '@lucide/svelte';
import { ae_loc } from '$lib/stores/ae_stores';
interface Props { interface Props {
// The object containing the flags (bindable) // The object containing the flags (bindable)
obj: any; obj: any;
// Visibility configuration (optional overrides) // Visibility configuration (optional overrides)
show_labels?: boolean; show_labels?: boolean;
hide_alert?: boolean; hide_alert?: boolean;
hide_private?: boolean; hide_private?: boolean;
hide_public?: boolean; hide_public?: boolean;
hide_personal?: boolean; hide_personal?: boolean;
hide_professional?: boolean; hide_professional?: boolean;
hide_template?: boolean; hide_template?: boolean;
// Callbacks // Callbacks
on_toggle?: (prop: string, newValue: boolean) => void; on_toggle?: (prop: string, newValue: boolean) => void;
// Styling // Styling
container_class?: string; container_class?: string;
} }
let { let {
obj = $bindable(), obj = $bindable(),
show_labels = true, show_labels = true,
hide_alert: hide_alert = false, hide_alert: hide_alert = false,
hide_private: hide_private = false, hide_private: hide_private = false,
hide_public: hide_public = false, hide_public: hide_public = false,
hide_personal: hide_personal = false, hide_personal: hide_personal = false,
hide_professional: hide_professional = false, hide_professional: hide_professional = false,
hide_template: hide_template = false, hide_template: hide_template = false,
on_toggle: onToggle, on_toggle: onToggle,
container_class = "flex flex-row flex-wrap gap-1 items-center justify-evenly py-2 border-y border-surface-500/10" container_class = 'flex flex-row flex-wrap gap-1 items-center justify-evenly py-2 border-y border-surface-500/10'
}: Props = $props(); }: Props = $props();
function handle_toggle(prop: string) { function handle_toggle(prop: string) {
obj[prop] = !obj[prop]; obj[prop] = !obj[prop];
if (onToggle) onToggle(prop, obj[prop]); if (onToggle) onToggle(prop, obj[prop]);
} }
</script> </script>
<div class={container_class}> <div class={container_class}>
{#if show_labels} {#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: <Settings size="1.1em" /> Flags:
</span> </span>
{/if} {/if}
@@ -63,9 +69,10 @@
type="button" type="button"
onclick={() => handle_toggle('alert')} onclick={() => handle_toggle('alert')}
class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition" class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition"
title="Toggle Alert Status" title="Toggle Alert Status">
> <Siren
<Siren size="1.2em" class={obj?.alert ? 'text-error-500' : 'opacity-40'} /> size="1.2em"
class={obj?.alert ? 'text-error-500' : 'opacity-40'} />
</button> </button>
{/if} {/if}
@@ -75,9 +82,10 @@
type="button" type="button"
onclick={() => handle_toggle('private')} onclick={() => handle_toggle('private')}
class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition" class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition"
title="Toggle Private/Encrypted" title="Toggle Private/Encrypted">
> <Fingerprint
<Fingerprint size="1.2em" class={obj?.private ? 'text-success-500' : 'opacity-40'} /> size="1.2em"
class={obj?.private ? 'text-success-500' : 'opacity-40'} />
</button> </button>
{/if} {/if}
@@ -87,9 +95,10 @@
type="button" type="button"
onclick={() => handle_toggle('public')} onclick={() => handle_toggle('public')}
class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition" class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition"
title="Toggle Public Visibility" title="Toggle Public Visibility">
> <Globe
<Globe size="1.2em" class={obj?.public ? 'text-success-500' : 'opacity-40'} /> size="1.2em"
class={obj?.public ? 'text-success-500' : 'opacity-40'} />
</button> </button>
{/if} {/if}
@@ -99,9 +108,10 @@
type="button" type="button"
onclick={() => handle_toggle('personal')} onclick={() => handle_toggle('personal')}
class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition" class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition"
title="Toggle Personal Scope" title="Toggle Personal Scope">
> <BookHeart
<BookHeart size="1.2em" class={obj?.personal ? 'text-success-500' : 'opacity-40'} /> size="1.2em"
class={obj?.personal ? 'text-success-500' : 'opacity-40'} />
</button> </button>
{/if} {/if}
@@ -111,9 +121,10 @@
type="button" type="button"
onclick={() => handle_toggle('professional')} onclick={() => handle_toggle('professional')}
class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition" class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition"
title="Toggle Professional Scope" title="Toggle Professional Scope">
> <BriefcaseBusiness
<BriefcaseBusiness size="1.2em" class={obj?.professional ? 'text-success-500' : 'opacity-40'} /> size="1.2em"
class={obj?.professional ? 'text-success-500' : 'opacity-40'} />
</button> </button>
{/if} {/if}
@@ -123,9 +134,10 @@
type="button" type="button"
onclick={() => handle_toggle('template')} onclick={() => handle_toggle('template')}
class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition" class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition"
title="Toggle Template Mode" title="Toggle Template Mode">
> <NotepadTextDashed
<NotepadTextDashed size="1.2em" class={obj?.template ? 'text-success-500' : 'opacity-40'} /> size="1.2em"
class={obj?.template ? 'text-success-500' : 'opacity-40'} />
</button> </button>
{/if} {/if}
</div> </div>

View File

@@ -1,95 +1,96 @@
<script lang="ts"> <script lang="ts">
/** /**
* AE_Record_Controls.svelte * AE_Record_Controls.svelte
* GENERIC Aether Record Management Controls * GENERIC Aether Record Management Controls
* Manages: priority, hide, enable, alert, delete/disable * Manages: priority, hide, enable, alert, delete/disable
* *
* Emits events — NO API calls. Parent is responsible for: * Emits events — NO API calls. Parent is responsible for:
* 1. Calling the API (update_ae_obj, delete_ae_obj_id__*) * 1. Calling the API (update_ae_obj, delete_ae_obj_id__*)
* 2. Refreshing the object from cache/API * 2. Refreshing the object from cache/API
* 3. Navigating away on delete * 3. Navigating away on delete
* *
* Usage: * Usage:
* <AE_Record_Controls * <AE_Record_Controls
* obj={$lq__event_session_obj} * obj={$lq__event_session_obj}
* obj_label="session" * obj_label="session"
* allow_delete={$ae_loc.manager_access} * allow_delete={$ae_loc.manager_access}
* allow_disable={$ae_loc.administrator_access} * allow_disable={$ae_loc.administrator_access}
* on_toggle={(field, val) => { ... }} * on_toggle={(field, val) => { ... }}
* on_delete={(method) => { ... }} * on_delete={(method) => { ... }}
* /> * />
*/ */
import { import {
Star, Star,
Eye, Eye,
EyeOff, EyeOff,
ToggleLeft, ToggleLeft,
ToggleRight, ToggleRight,
Bell, Bell,
BellOff, BellOff,
Trash2, Trash2,
CircleMinus, CircleMinus,
Settings Settings
} from '@lucide/svelte'; } from '@lucide/svelte';
interface Props { interface Props {
// The object whose flags are being displayed (read-only — parent owns state) // The object whose flags are being displayed (read-only — parent owns state)
obj: any; obj: any;
// Human-readable label for confirm dialogs ("session", "presenter", "location", etc.) // Human-readable label for confirm dialogs ("session", "presenter", "location", etc.)
obj_label?: string; obj_label?: string;
// Visibility — hide any control that doesn't apply for this object type // Visibility — hide any control that doesn't apply for this object type
show_alert?: boolean; show_alert?: boolean;
show_priority?: boolean; show_priority?: boolean;
show_enable?: boolean; show_enable?: boolean;
show_hide?: boolean; show_hide?: boolean;
show_labels?: boolean; show_labels?: boolean;
// Permission gates — parent passes booleans derived from $ae_loc // Permission gates — parent passes booleans derived from $ae_loc
allow_delete?: boolean; // Hard permanent delete (manager+) allow_delete?: boolean; // Hard permanent delete (manager+)
allow_disable?: boolean; // Soft disable/remove (administrator+) allow_disable?: boolean; // Soft disable/remove (administrator+)
// Callbacks — parent handles API + refresh + navigation // Callbacks — parent handles API + refresh + navigation
on_toggle?: (field: string, new_val: boolean) => void; on_toggle?: (field: string, new_val: boolean) => void;
on_delete?: (method: 'delete' | 'disable') => void; on_delete?: (method: 'delete' | 'disable') => void;
// Styling // Styling
container_class?: string; container_class?: string;
} }
let { let {
obj, obj,
obj_label = 'record', obj_label = 'record',
show_alert = true, show_alert = true,
show_priority = true, show_priority = true,
show_enable = true, show_enable = true,
show_hide = true, show_hide = true,
show_labels = true, show_labels = true,
allow_delete = false, allow_delete = false,
allow_disable = false, allow_disable = false,
on_toggle, on_toggle,
on_delete, on_delete,
container_class = 'flex flex-row flex-wrap gap-1 items-center justify-evenly py-2 border-y border-surface-500/10' container_class = 'flex flex-row flex-wrap gap-1 items-center justify-evenly py-2 border-y border-surface-500/10'
}: Props = $props(); }: Props = $props();
function toggle(field: string) { function toggle(field: string) {
if (on_toggle) on_toggle(field, !obj?.[field]); if (on_toggle) on_toggle(field, !obj?.[field]);
} }
function handle_delete(method: 'delete' | 'disable') { function handle_delete(method: 'delete' | 'disable') {
const msg = const msg =
method === 'delete' method === 'delete'
? `Permanently delete this ${obj_label}? This cannot be undone.` ? `Permanently delete this ${obj_label}? This cannot be undone.`
: `Remove (disable) this ${obj_label}?`; : `Remove (disable) this ${obj_label}?`;
if (!confirm(msg)) return; if (!confirm(msg)) return;
if (on_delete) on_delete(method); if (on_delete) on_delete(method);
} }
</script> </script>
<div class={container_class}> <div class={container_class}>
{#if show_labels} {#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: <Settings size="1.1em" /> Controls:
</span> </span>
{/if} {/if}
@@ -103,12 +104,10 @@
class:preset-filled-warning-500={obj?.priority} class:preset-filled-warning-500={obj?.priority}
class:preset-tonal-secondary={!obj?.priority} class:preset-tonal-secondary={!obj?.priority}
class:hover:preset-filled-warning-500={!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 <Star
size="1.2em" size="1.2em"
class={obj?.priority ? 'fill-current' : 'opacity-50'} class={obj?.priority ? 'fill-current' : 'opacity-50'} />
/>
</button> </button>
{/if} {/if}
@@ -121,8 +120,7 @@
class:preset-filled-warning-500={obj?.hide} class:preset-filled-warning-500={obj?.hide}
class:preset-tonal-secondary={!obj?.hide} class:preset-tonal-secondary={!obj?.hide}
class:hover:preset-filled-warning-500={!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} {#if obj?.hide}
<EyeOff size="1.2em" class="text-warning-500" /> <EyeOff size="1.2em" class="text-warning-500" />
{:else} {:else}
@@ -140,8 +138,7 @@
class:preset-filled-success-500={obj?.enable} class:preset-filled-success-500={obj?.enable}
class:preset-filled-error-500={!obj?.enable} class:preset-filled-error-500={!obj?.enable}
class:hover:preset-filled-success-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} {#if obj?.enable}
<ToggleRight size="1.2em" class="text-success-300" /> <ToggleRight size="1.2em" class="text-success-300" />
{:else} {:else}
@@ -159,8 +156,7 @@
class:preset-filled-error-500={obj?.alert} class:preset-filled-error-500={obj?.alert}
class:preset-tonal-secondary={!obj?.alert} class:preset-tonal-secondary={!obj?.alert}
class:hover:preset-filled-error-500={!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} {#if obj?.alert}
<Bell size="1.2em" class="text-error-300" /> <Bell size="1.2em" class="text-error-300" />
{:else} {:else}
@@ -175,8 +171,7 @@
type="button" type="button"
onclick={() => handle_delete('delete')} onclick={() => handle_delete('delete')}
class="btn-icon btn-icon-sm preset-filled-error-500 hover:preset-filled-error-600 transition" 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" /> <Trash2 size="1.2em" />
</button> </button>
{:else if allow_disable} {:else if allow_disable}
@@ -184,8 +179,7 @@
type="button" type="button"
onclick={() => handle_delete('disable')} onclick={() => handle_delete('disable')}
class="btn-icon btn-icon-sm preset-filled-warning-500 hover:preset-filled-warning-600 transition" 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" /> <CircleMinus size="1.2em" />
</button> </button>
{/if} {/if}

View File

@@ -46,7 +46,9 @@ export async function load_ae_obj_id__event({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_Event | null> { }): Promise<ae_Event | null> {
if (log_lvl) { 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 // Hierarchy Enforcement: Pulling presentations/presenters requires pulling sessions first
@@ -57,20 +59,43 @@ export async function load_ae_obj_id__event({
try { try {
const cached_event = await db_events.event.get(event_id); const cached_event = await db_events.event.get(event_id);
if (cached_event) { 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 // Trigger background refresh
_refresh_event_background({ _refresh_event_background({
api_cfg, event_id, view, try_cache, api_cfg,
inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_template_li, event_id,
enabled, hidden, 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 log_lvl: 0
}); });
// Still handle nested loads for the cached version to ensure UI richness // Still handle nested loads for the cached version to ensure UI richness
return await _handle_nested_loads(cached_event, { 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, api_cfg,
enabled, hidden, try_cache, log_lvl 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) { } catch (e) {
@@ -80,9 +105,19 @@ export async function load_ae_obj_id__event({
// 2. SLOW PATH: Wait for API if cache is empty or try_cache is false // 2. SLOW PATH: Wait for API if cache is empty or try_cache is false
return await _refresh_event_background({ return await _refresh_event_background({
api_cfg, event_id, view, try_cache, api_cfg,
inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_template_li, event_id,
enabled, hidden, 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 log_lvl
}); });
} }
@@ -91,9 +126,19 @@ export async function load_ae_obj_id__event({
* Internal helper to perform the actual API fetch and cache update for events * Internal helper to perform the actual API fetch and cache update for events
*/ */
async function _refresh_event_background({ async function _refresh_event_background({
api_cfg, event_id, view, try_cache, api_cfg,
inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_template_li, event_id,
enabled, hidden, 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 log_lvl
}: any) { }: any) {
// Check if offline // Check if offline
@@ -129,8 +174,18 @@ async function _refresh_event_background({
} }
return await _handle_nested_loads(processed_obj, { 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, api_cfg,
enabled, hidden, try_cache: false, log_lvl 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) { } catch (error: any) {
@@ -142,8 +197,27 @@ async function _refresh_event_background({
/** /**
* Shared logic for loading nested child collections * 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) { async function _handle_nested_loads(
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})`); 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 // String-Only ID Vision: the '_id' field IS the string ID
const current_event_id = event_obj.id || event_obj.event_id; const current_event_id = event_obj.id || event_obj.event_id;
@@ -151,56 +225,66 @@ async function _handle_nested_loads(event_obj: any, { api_cfg, inc_device_li, in
const tasks = []; const tasks = [];
if (inc_device_li) { if (inc_device_li) {
tasks.push(load_ae_obj_li__event_device({ tasks.push(
api_cfg, load_ae_obj_li__event_device({
for_obj_type: 'event', api_cfg,
for_obj_id: current_event_id, for_obj_type: 'event',
try_cache, for_obj_id: current_event_id,
log_lvl try_cache,
}).then(res => event_obj.event_device_obj_li = res)); log_lvl
}).then((res) => (event_obj.event_device_obj_li = res))
);
} }
if (inc_file_li) { if (inc_file_li) {
tasks.push(load_ae_obj_li__event_file({ tasks.push(
api_cfg, load_ae_obj_li__event_file({
for_obj_type: 'event', api_cfg,
for_obj_id: current_event_id, for_obj_type: 'event',
enabled: 'all', for_obj_id: current_event_id,
limit: 100, enabled: 'all',
try_cache, limit: 100,
log_lvl try_cache,
}).then(res => event_obj.event_file_li = res)); log_lvl
}).then((res) => (event_obj.event_file_li = res))
);
} }
if (inc_location_li) { if (inc_location_li) {
tasks.push(load_ae_obj_li__event_location({ tasks.push(
api_cfg, load_ae_obj_li__event_location({
for_obj_type: 'event', api_cfg,
for_obj_id: current_event_id, for_obj_type: 'event',
enabled, for_obj_id: current_event_id,
hidden, enabled,
try_cache, hidden,
log_lvl try_cache,
}).then(res => event_obj.event_location_obj_li = res)); log_lvl
}).then((res) => (event_obj.event_location_obj_li = res))
);
} }
if (inc_session_li) { if (inc_session_li) {
tasks.push(load_ae_obj_li__event_session({ tasks.push(
api_cfg, load_ae_obj_li__event_session({
for_obj_type: 'event', api_cfg,
for_obj_id: current_event_id, for_obj_type: 'event',
inc_presentation_li, for_obj_id: current_event_id,
inc_presenter_li, inc_presentation_li,
enabled, inc_presenter_li,
hidden, enabled,
try_cache, hidden,
log_lvl try_cache,
}).then(res => event_obj.event_session_obj_li = res)); log_lvl
}).then((res) => (event_obj.event_session_obj_li = res))
);
} }
if (inc_template_li) { if (inc_template_li) {
tasks.push(load_ae_obj_li__event_badge_template({ tasks.push(
api_cfg, load_ae_obj_li__event_badge_template({
event_id: current_event_id, api_cfg,
try_cache, event_id: current_event_id,
log_lvl try_cache,
}).then(res => event_obj.event_badge_template_obj_li = res)); log_lvl
}).then((res) => (event_obj.event_badge_template_obj_li = res))
);
} }
if (tasks.length > 0) await Promise.all(tasks); if (tasks.length > 0) await Promise.all(tasks);
@@ -238,20 +322,25 @@ export async function load_ae_obj_li__event({
inc_presenter_li?: boolean; inc_presenter_li?: boolean;
limit?: number; limit?: number;
offset?: 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; params?: key_val;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_Event[]> { }): Promise<ae_Event[]> {
// Hierarchy Enforcement: Pulling presentations/presenters requires pulling sessions first // Hierarchy Enforcement: Pulling presentations/presenters requires pulling sessions first
if (inc_presenter_li || inc_presentation_li) inc_session_li = true; if (inc_presenter_li || inc_presentation_li) inc_session_li = true;
// Check if offline // Check if offline
if (typeof navigator !== 'undefined' && !navigator.onLine) { 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 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(); .toArray();
return ae_promises.load__event_obj_li || []; return ae_promises.load__event_obj_li || [];
} }
@@ -264,7 +353,11 @@ export async function load_ae_obj_li__event({
}; };
if (for_obj_id) { 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({ promise = api.search_ae_obj({
@@ -319,9 +412,11 @@ export async function load_ae_obj_li__event({
} else { } else {
console.log('No results returned from API.'); console.log('No results returned from API.');
if (try_cache) { 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 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(); .toArray();
} else { } else {
ae_promises.load__event_obj_li = []; ae_promises.load__event_obj_li = [];
@@ -330,9 +425,13 @@ export async function load_ae_obj_li__event({
} catch (error: any) { } catch (error: any) {
console.log('API request failed.', error); console.log('API request failed.', error);
if (try_cache) { 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 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(); .toArray();
} else { } else {
ae_promises.load__event_obj_li = []; ae_promises.load__event_obj_li = [];
@@ -340,18 +439,20 @@ export async function load_ae_obj_li__event({
} }
if (inc_session_li && ae_promises.load__event_obj_li) { if (inc_session_li && ae_promises.load__event_obj_li) {
const session_tasks = ae_promises.load__event_obj_li.map((event_obj: any) => { const session_tasks = ae_promises.load__event_obj_li.map(
const current_event_id = event_obj.id || event_obj.event_id; (event_obj: any) => {
return load_ae_obj_li__event_session({ const current_event_id = event_obj.id || event_obj.event_id;
api_cfg, return load_ae_obj_li__event_session({
for_obj_type: 'event', api_cfg,
for_obj_id: current_event_id, for_obj_type: 'event',
inc_presentation_li, for_obj_id: current_event_id,
inc_presenter_li, inc_presentation_li,
try_cache, inc_presenter_li,
log_lvl try_cache,
}).then((res) => (event_obj.event_session_obj_li = res)); log_lvl
}); }).then((res) => (event_obj.event_session_obj_li = res));
}
);
await Promise.all(session_tasks); await Promise.all(session_tasks);
} }
@@ -543,13 +644,21 @@ export async function search__event({
}; };
const params: key_val = {}; const params: key_val = {};
search_query.and.push({ field: 'default_qry_str', op: 'like', value: `%${qry_str.trim()}%` }); search_query.and.push({
params['lk_qry'] = { 'default_qry_str': qry_str.trim() }; field: 'default_qry_str',
op: 'like',
value: `%${qry_str.trim()}%`
});
params['lk_qry'] = { default_qry_str: qry_str.trim() };
if (for_obj_id) { 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. // 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. // NOTE: We do NOT push 'physical' and 'virtual' to the server-side query here.
@@ -593,7 +702,11 @@ export async function search__event({
let valid_result_li: ae_Event[] = []; let valid_result_li: ae_Event[] = [];
if (Array.isArray(result_li)) { if (Array.isArray(result_li)) {
valid_result_li = 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; valid_result_li = (result_li as any).data;
} else { } else {
return []; return [];
@@ -641,13 +754,12 @@ export async function search__event({
// Handle person ID filter // Handle person ID filter
if (qry_person_id) { if (qry_person_id) {
const match = ( const match =
ev.external_person_id === qry_person_id || ev.external_person_id === qry_person_id ||
ev.poc_person_id === qry_person_id || ev.poc_person_id === qry_person_id ||
ev.poc_person_id_random === qry_person_id || ev.poc_person_id_random === qry_person_id ||
ev.poc_event_person_id === 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; if (!match) return false;
} }
@@ -655,7 +767,9 @@ export async function search__event({
}); });
if (log_lvl) { 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); return filtered_obj_li.slice(0, limit);
@@ -663,7 +777,6 @@ export async function search__event({
export const qry_ae_obj_li__event = search__event; export const qry_ae_obj_li__event = search__event;
// Updated 2026-03-10 // Updated 2026-03-10
export const properties_to_save = [ export const properties_to_save = [
'id', 'id',
@@ -780,14 +893,21 @@ async function _process_generic_props<T extends Record<string, any>>({
const group = processed_obj.group ?? '0'; const group = processed_obj.group ?? '0';
const priority = processed_obj.priority ? 1 : 0; const priority = processed_obj.priority ? 1 : 0;
const sort = processed_obj.sort ?? '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 ?? ''; const name = processed_obj.name ?? '';
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`; (processed_obj as any).tmp_sort_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
(processed_obj as any).tmp_sort_2 =
`${group}_${priority}_${sort}_${name}_${updated}`;
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);
@@ -849,7 +969,8 @@ export function sync_config__event_pres_mgmt({
pres_mgmt_cfg_remote?.label__session_poc_name_short ?? 'POC'; pres_mgmt_cfg_remote?.label__session_poc_name_short ?? 'POC';
pres_mgmt_cfg_local.label__session_poc_name = pres_mgmt_cfg_local.label__session_poc_name =
pres_mgmt_cfg_remote?.label__session_poc_name ?? 'Point of Contact'; 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_local.require__presenter_agree =
pres_mgmt_cfg_remote?.require__presenter_agree ?? false; pres_mgmt_cfg_remote?.require__presenter_agree ?? false;
pres_mgmt_cfg_local.require__session_agree = pres_mgmt_cfg_local.require__session_agree =
@@ -874,24 +995,28 @@ export function sync_config__event_pres_mgmt({
pres_mgmt_cfg_local.hide__presentation_datetime = pres_mgmt_cfg_local.hide__presentation_datetime =
pres_mgmt_cfg_remote?.hide__presentation_datetime ?? false; pres_mgmt_cfg_remote?.hide__presentation_datetime ?? false;
pres_mgmt_cfg_local.show_content__presentation_description = 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_local.hide__presenter_code =
pres_mgmt_cfg_remote?.hide__presenter_code ?? false; pres_mgmt_cfg_remote?.hide__presenter_code ?? false;
pres_mgmt_cfg_local.hide__presenter_biography = pres_mgmt_cfg_local.hide__presenter_biography =
pres_mgmt_cfg_remote?.hide__presenter_biography ?? false; 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_local.hide__session_description =
pres_mgmt_cfg_remote?.hide__session_description ?? false; pres_mgmt_cfg_remote?.hide__session_description ?? false;
pres_mgmt_cfg_local.hide__session_location = pres_mgmt_cfg_local.hide__session_location =
pres_mgmt_cfg_remote?.hide__session_location ?? false; 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_local.hide__session_poc_profile =
pres_mgmt_cfg_remote?.hide__session_poc_profile ?? false; pres_mgmt_cfg_remote?.hide__session_poc_profile ?? false;
pres_mgmt_cfg_local.hide__session_poc_biography = pres_mgmt_cfg_local.hide__session_poc_biography =
pres_mgmt_cfg_remote?.hide__session_poc_biography ?? false; pres_mgmt_cfg_remote?.hide__session_poc_biography ?? false;
pres_mgmt_cfg_local.hide__session_poc_profile_pic = pres_mgmt_cfg_local.hide__session_poc_profile_pic =
pres_mgmt_cfg_remote?.hide__session_poc_profile_pic ?? false; 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_local.hide_launcher_link_legacy =
pres_mgmt_cfg_remote?.hide_launcher_link_legacy ?? false; pres_mgmt_cfg_remote?.hide_launcher_link_legacy ?? false;
} }

View File

@@ -9,9 +9,15 @@ vi.mock('$lib/api/api', () => ({
search_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_core/core__idb_dexie', () => ({
vi.mock('$lib/ae_events/db_events', () => ({ db_events: { badge: { get: vi.fn(), delete: vi.fn() } } })); db_save_ae_obj_li__ae_obj: 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_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'; import { create_ae_obj__event_badge } from './ae_events__event_badge';
@@ -25,9 +31,17 @@ describe('create_ae_obj__event_badge', () => {
const api_cfg = { base_url: 'http://localhost', headers: {} }; const api_cfg = { base_url: 'http://localhost', headers: {} };
const event_id = 'evt1'; 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).toHaveBeenCalled();
expect(mockCreateNested).toHaveBeenCalledWith({ expect(mockCreateNested).toHaveBeenCalledWith({
@@ -51,9 +65,16 @@ describe('update_ae_obj__event_badge', () => {
const fakeResult = { event_badge_id: 'eb999', full_name: 'Updated' }; const fakeResult = { event_badge_id: 'eb999', full_name: 'Updated' };
mockUpdate.mockResolvedValue(fakeResult); 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 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).toHaveBeenCalled();
expect(mockUpdate).toHaveBeenCalledWith({ expect(mockUpdate).toHaveBeenCalledWith({
@@ -79,10 +100,16 @@ describe('delete_ae_obj_id__event_badge', () => {
const db = await import('$lib/ae_events/db_events'); const db = await import('$lib/ae_events/db_events');
const dbDelete = db.db_events.badge.delete as any; 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 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(mockDelete).toHaveBeenCalled();
expect(dbDelete).toHaveBeenCalledWith('ebDel'); expect(dbDelete).toHaveBeenCalledWith('ebDel');
@@ -97,9 +124,15 @@ describe('search__event_badge', () => {
const fakeList = [{ event_badge_id: 'eb1' }, { event_badge_id: 'eb2' }]; const fakeList = [{ event_badge_id: 'eb1' }, { event_badge_id: 'eb2' }];
mockSearch.mockResolvedValue({ data: fakeList }); 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 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(mockSearch).toHaveBeenCalled();
expect(Array.isArray(res)).toBe(true); expect(Array.isArray(res)).toBe(true);

View File

@@ -35,29 +35,36 @@ export async function load_ae_obj_id__event_badge({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventBadge | null> { }): Promise<ae_EventBadge | null> {
if (log_lvl) { 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 { try {
ae_promises.load__event_badge_obj = await api ae_promises.load__event_badge_obj = await api.get_ae_obj({
.get_ae_obj({ api_cfg,
api_cfg, obj_type: 'event_badge',
obj_type: 'event_badge', obj_id: event_badge_id,
obj_id: event_badge_id, view,
view, log_lvl
log_lvl });
});
if (ae_promises.load__event_badge_obj) { if (ae_promises.load__event_badge_obj) {
if (try_cache) { if (try_cache) {
// In theory we should be able to use the event_id found in the Badge load object. 2026-02-04 // 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 // 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}`); if (log_lvl)
const processed_obj_li = await process_ae_obj__event_badge_props({ console.log(
obj_li: [ae_promises.load__event_badge_obj], `Saving to local cache... Event ID: ${event_id} or ${ae_promises.load__event_badge_obj.event_id}`
event_id: event_id || ae_promises.load__event_badge_obj.event_id, );
log_lvl 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({ await db_save_ae_obj_li__ae_obj({
db_instance: db_events, db_instance: db_events,
table_name: 'badge', table_name: 'badge',
@@ -71,15 +78,21 @@ export async function load_ae_obj_id__event_badge({
} else { } else {
console.log('No results returned from API.'); console.log('No results returned from API.');
if (try_cache) { if (try_cache) {
if (log_lvl) console.log('Attempting to load from local cache...'); if (log_lvl)
ae_promises.load__event_badge_obj = await db_events.badge.get(event_badge_id); 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) { } catch (error: any) {
console.log('API request failed.', error); console.log('API request failed.', error);
if (try_cache) { if (try_cache) {
if (log_lvl) console.log('Attempting to load from local cache after error...'); if (log_lvl)
ae_promises.load__event_badge_obj = await db_events.badge.get(event_badge_id); 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 { } else {
ae_promises.load__event_badge_obj = null; 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) { if (inc_template && ae_promises.load__event_badge_obj) {
// Load the templates for the event badge // 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) { if (current_template_id) {
ae_promises.load__event_badge_obj.event_badge_template = await load_ae_obj_id__event_badge_template({ ae_promises.load__event_badge_obj.event_badge_template =
api_cfg: api_cfg, await load_ae_obj_id__event_badge_template({
event_badge_template_id: current_template_id, api_cfg: api_cfg,
try_cache: try_cache, event_badge_template_id: current_template_id,
log_lvl: log_lvl try_cache: try_cache,
}); log_lvl: log_lvl
});
} }
} }
@@ -111,7 +126,12 @@ export async function load_ae_obj_li__event_badge({
view = 'default', view = 'default',
limit = 99, limit = 99,
offset = 0, 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 = {}, params = {},
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
@@ -130,32 +150,34 @@ export async function load_ae_obj_li__event_badge({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventBadge[]> { }): Promise<ae_EventBadge[]> {
if (log_lvl) { 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 { try {
ae_promises.load__event_badge_obj_li = await api ae_promises.load__event_badge_obj_li = await api.get_ae_obj_li({
.get_ae_obj_li({ api_cfg,
api_cfg, obj_type: 'event_badge',
obj_type: 'event_badge', for_obj_type: 'event',
for_obj_type: 'event', for_obj_id: event_id,
for_obj_id: event_id, enabled: enabled,
enabled: enabled, hidden: hidden,
hidden: hidden, view: view,
view: view, order_by_li: order_by_li,
order_by_li: order_by_li, limit: limit,
limit: limit, offset: offset,
offset: offset, log_lvl: log_lvl
log_lvl: log_lvl });
});
if (ae_promises.load__event_badge_obj_li) { if (ae_promises.load__event_badge_obj_li) {
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__event_badge_props({ const processed_obj_li =
obj_li: ae_promises.load__event_badge_obj_li, await process_ae_obj__event_badge_props({
event_id, obj_li: ae_promises.load__event_badge_obj_li,
log_lvl event_id,
}); log_lvl
});
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_events, db_instance: db_events,
table_name: 'badge', table_name: 'badge',
@@ -169,9 +191,11 @@ export async function load_ae_obj_li__event_badge({
} else { } else {
console.log('No results returned from API.'); console.log('No results returned from API.');
if (try_cache) { 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 ae_promises.load__event_badge_obj_li = await db_events.badge
.where('event_id').equals(event_id) .where('event_id')
.equals(event_id)
.toArray(); .toArray();
} else { } else {
ae_promises.load__event_badge_obj_li = []; ae_promises.load__event_badge_obj_li = [];
@@ -180,9 +204,13 @@ export async function load_ae_obj_li__event_badge({
} catch (error: any) { } catch (error: any) {
console.log('API request failed.', error); console.log('API request failed.', error);
if (try_cache) { 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 ae_promises.load__event_badge_obj_li = await db_events.badge
.where('event_id').equals(event_id) .where('event_id')
.equals(event_id)
.toArray(); .toArray();
} else { } else {
ae_promises.load__event_badge_obj_li = []; 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) { for (const badge_obj of ae_promises.load__event_badge_obj_li) {
const current_template_id = badge_obj.event_badge_template_id; const current_template_id = badge_obj.event_badge_template_id;
if (current_template_id) { if (current_template_id) {
badge_obj.event_badge_template = await load_ae_obj_id__event_badge_template({ badge_obj.event_badge_template =
api_cfg: api_cfg, await load_ae_obj_id__event_badge_template({
event_badge_template_id: current_template_id, api_cfg: api_cfg,
try_cache: try_cache, event_badge_template_id: current_template_id,
log_lvl: log_lvl try_cache: try_cache,
}); log_lvl: log_lvl
});
} }
} }
} }
@@ -223,7 +252,9 @@ export async function create_ae_obj__event_badge({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventBadge | null> { }): Promise<ae_EventBadge | null> {
if (log_lvl) { 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({ const result = await api.create_nested_obj({
@@ -275,7 +306,9 @@ export async function delete_ae_obj_id__event_badge({
log_lvl?: number; log_lvl?: number;
}) { }) {
if (log_lvl) { 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({ const result = await api.delete_nested_ae_obj({
@@ -315,7 +348,9 @@ export async function update_ae_obj__event_badge({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventBadge | null> { }): Promise<ae_EventBadge | null> {
if (log_lvl) { 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({ const result = await api.update_nested_obj({
@@ -391,7 +426,9 @@ export async function search__event_badge({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventBadge[] | false> { }): Promise<ae_EventBadge[] | false> {
if (log_lvl) { 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 = { 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) // 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) { if (fulltext_search_qry_str && fulltext_search_qry_str.trim().length > 0) {
const qry = fulltext_search_qry_str.trim(); 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) { if (words.length === 1) {
// Single word: use simple LIKE // 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) { } else if (words.length > 1) {
// Multiple words: each word must match (AND logic) // Multiple words: each word must match (AND logic)
for (const word of words) { 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) { if (affiliations_qry_str && affiliations_qry_str.trim().length > 0) {
const qry = affiliations_qry_str.trim(); 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'] = params['lk_qry'] || {};
params['lk_qry']['affiliations'] = qry; params['lk_qry']['affiliations'] = qry;
} }
@@ -435,11 +484,19 @@ export async function search__event_badge({
} }
if (external_event_id) { 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) { 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') { if (printed_status === 'printed') {
@@ -448,11 +505,15 @@ export async function search__event_badge({
search_query.and.push({ field: 'print_count', op: 'eq', value: 0 }); search_query.and.push({ field: 'print_count', op: 'eq', value: 0 });
} }
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: true }); if (enabled === 'enabled')
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: false }); 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 }); if (hidden === 'hidden')
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: false }); 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 ae_promises.search__event_badge_obj_li = await api
.search_ae_obj({ .search_ae_obj({
@@ -473,17 +534,21 @@ export async function search__event_badge({
let result_li: ae_EventBadge[] = []; let result_li: ae_EventBadge[] = [];
if (Array.isArray(badge_obj_li_get_result)) { if (Array.isArray(badge_obj_li_get_result)) {
result_li = 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; result_li = badge_obj_li_get_result.data;
} }
if (result_li.length > 0) { if (result_li.length > 0) {
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__event_badge_props({ const processed_obj_li =
obj_li: result_li, await process_ae_obj__event_badge_props({
event_id, obj_li: result_li,
log_lvl event_id,
}); log_lvl
});
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_events, db_instance: db_events,
table_name: 'badge', table_name: 'badge',
@@ -631,14 +696,21 @@ async function _process_generic_props<T extends Record<string, any>>({
const group = processed_obj.group ?? '0'; const group = processed_obj.group ?? '0';
const priority = processed_obj.priority ? 1 : 0; const priority = processed_obj.priority ? 1 : 0;
const sort = processed_obj.sort ?? '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 ?? ''; const name = processed_obj.name ?? '';
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`; (processed_obj as any).tmp_sort_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
(processed_obj as any).tmp_sort_2 =
`${group}_${priority}_${sort}_${name}_${updated}`;
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);
@@ -663,7 +735,9 @@ export async function process_ae_obj__event_badge_props({
log_lvl, log_lvl,
specific_processor: (obj) => { specific_processor: (obj) => {
if (log_lvl) { 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 (event_id) {
if (!obj.event_id) obj.event_id = event_id; if (!obj.event_id) obj.event_id = event_id;

View File

@@ -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 updated = processed_obj.updated_on ?? processed_obj.created_on;
const name = processed_obj.name ?? ''; const name = processed_obj.name ?? '';
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`; (processed_obj as any).tmp_sort_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
(processed_obj as any).tmp_sort_2 =
`${group}_${priority}_${sort}_${name}_${updated}`;
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);
@@ -129,7 +133,9 @@ export async function load_ae_obj_id__event_badge_template({
log_lvl?: number; log_lvl?: number;
}) { }) {
if (log_lvl) { 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 { try {
@@ -156,11 +162,13 @@ export async function load_ae_obj_id__event_badge_template({
}); });
} }
} else if (try_cache) { } 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) { } catch (error: any) {
if (try_cache) { 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; log_lvl?: number;
}) { }) {
try { try {
ae_promises.load__event_badge_template_obj_li = await api.get_ae_obj_li({ ae_promises.load__event_badge_template_obj_li = await api.get_ae_obj_li(
api_cfg, {
obj_type: 'event_badge_template', api_cfg,
for_obj_type: 'event', obj_type: 'event_badge_template',
for_obj_id: event_id, for_obj_type: 'event',
enabled, for_obj_id: event_id,
hidden, enabled,
view, hidden,
limit, view,
offset, limit,
order_by_li, offset,
log_lvl order_by_li,
}); log_lvl
}
);
if (ae_promises.load__event_badge_template_obj_li) { if (ae_promises.load__event_badge_template_obj_li) {
if (try_cache) { if (try_cache) {
@@ -226,15 +236,19 @@ export async function load_ae_obj_li__event_badge_template({
}); });
} }
} else if (try_cache) { } else if (try_cache) {
ae_promises.load__event_badge_template_obj_li = await db_events.badge_template ae_promises.load__event_badge_template_obj_li =
.where('event_id').equals(event_id) await db_events.badge_template
.toArray(); .where('event_id')
.equals(event_id)
.toArray();
} }
} catch (error: any) { } catch (error: any) {
if (try_cache) { if (try_cache) {
ae_promises.load__event_badge_template_obj_li = await db_events.badge_template ae_promises.load__event_badge_template_obj_li =
.where('event_id').equals(event_id) await db_events.badge_template
.toArray(); .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 // Updated 2026-01-20 to V3
export async function create_ae_obj__event_badge_template({ export async function create_ae_obj__event_badge_template({
api_cfg, api_cfg,
event_id, event_id,
data_kv, data_kv,
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
event_id: string; event_id: string;
data_kv: key_val; data_kv: key_val;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}) { }) {
const result = await api.create_nested_obj({ const result = await api.create_nested_obj({
api_cfg, api_cfg,
for_obj_type: 'event', for_obj_type: 'event',
for_obj_id: event_id, for_obj_id: event_id,
obj_type: 'event_badge_template', obj_type: 'event_badge_template',
fields: { ...data_kv }, fields: { ...data_kv },
log_lvl log_lvl
}); });
if (result && try_cache) { if (result && try_cache) {
const processed_obj_li = await process_ae_badge_template_props({ const processed_obj_li = await process_ae_badge_template_props({
obj_li: [result], obj_li: [result],
log_lvl log_lvl
}); });
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_events, db_instance: db_events,
table_name: 'badge_template', table_name: 'badge_template',
obj_li: processed_obj_li, obj_li: processed_obj_li,
properties_to_save, properties_to_save,
log_lvl log_lvl
}); });
} }
return result; return result;
} }
// Updated 2026-01-20 to V3 // Updated 2026-01-20 to V3
export async function delete_ae_obj_id__event_badge_template({ export async function delete_ae_obj_id__event_badge_template({
api_cfg, api_cfg,
event_id, event_id,
event_badge_template_id, event_badge_template_id,
method = 'delete', method = 'delete',
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
event_id: string; event_id: string;
event_badge_template_id: string; event_badge_template_id: string;
method?: 'delete' | 'soft_delete' | 'disable' | 'hide'; method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}) { }) {
const result = await api.delete_nested_ae_obj({ const result = await api.delete_nested_ae_obj({
api_cfg, api_cfg,
for_obj_type: 'event', for_obj_type: 'event',
for_obj_id: event_id, for_obj_id: event_id,
obj_type: 'event_badge_template', obj_type: 'event_badge_template',
obj_id: event_badge_template_id, obj_id: event_badge_template_id,
method, method,
log_lvl log_lvl
}); });
if (try_cache) { if (try_cache) {
await db_events.badge_template.delete(event_badge_template_id); await db_events.badge_template.delete(event_badge_template_id);
} }
return result; return result;
} }
// Updated 2026-01-20 to V3 // Updated 2026-01-20 to V3
export async function update_ae_obj__event_badge_template({ export async function update_ae_obj__event_badge_template({
api_cfg, api_cfg,
event_id, event_id,
event_badge_template_id, event_badge_template_id,
data_kv, data_kv,
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
event_id: string; event_id: string;
event_badge_template_id: string; event_badge_template_id: string;
data_kv: key_val; data_kv: key_val;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}) { }) {
const result = await api.update_nested_obj({ const result = await api.update_nested_obj({
api_cfg, api_cfg,
for_obj_type: 'event', for_obj_type: 'event',
for_obj_id: event_id, for_obj_id: event_id,
obj_type: 'event_badge_template', obj_type: 'event_badge_template',
obj_id: event_badge_template_id, obj_id: event_badge_template_id,
fields: data_kv, fields: data_kv,
log_lvl log_lvl
}); });
if (result && try_cache) { if (result && try_cache) {
const processed_obj_li = await process_ae_badge_template_props({ const processed_obj_li = await process_ae_badge_template_props({
obj_li: [result], obj_li: [result],
log_lvl log_lvl
}); });
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_events, db_instance: db_events,
table_name: 'badge_template', table_name: 'badge_template',
obj_li: processed_obj_li, obj_li: processed_obj_li,
properties_to_save, properties_to_save,
log_lvl log_lvl
}); });
} }
return result; return result;
} }
// Updated 2026-01-20 to V3 // Updated 2026-01-20 to V3
@@ -388,11 +402,15 @@ export async function search__event_badge_template({
and: [{ field: 'event_id', op: 'eq', value: event_id }] and: [{ field: 'event_id', op: 'eq', value: event_id }]
}; };
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: true }); if (enabled === 'enabled')
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: false }); 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 }); if (hidden === 'hidden')
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: false }); 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({ const result_li = await api.search_ae_obj({
api_cfg, api_cfg,

View File

@@ -28,7 +28,9 @@ export async function load_ae_obj_id__event_device({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventDevice | null> { }): Promise<ae_EventDevice | null> {
if (log_lvl) { 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 { try {
@@ -57,26 +59,30 @@ export async function load_ae_obj_id__event_device({
}); });
} }
} else if (try_cache) { } 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) { } catch (error: any) {
console.log('V3 Request failed.', error); console.log('V3 Request failed.', error);
if (try_cache) { 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 (!ae_promises.load__event_device_obj) return null;
if (inc_location_id) { 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) { if (current_location_id) {
ae_promises.load__event_device_obj.event_location_obj = await load_ae_obj_id__event_location({ ae_promises.load__event_device_obj.event_location_obj =
api_cfg, await load_ae_obj_id__event_location({
event_location_id: current_location_id, api_cfg,
try_cache, event_location_id: current_location_id,
log_lvl try_cache,
}); log_lvl
});
} }
} }
@@ -115,7 +121,9 @@ export async function load_ae_obj_li__event_device({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventDevice[]> { }): Promise<ae_EventDevice[]> {
if (log_lvl) { 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 { try {
@@ -150,14 +158,16 @@ export async function load_ae_obj_li__event_device({
} }
} else if (try_cache) { } else if (try_cache) {
ae_promises.load__event_device_obj_li = await db_events.device 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(); .toArray();
} }
} catch (error: any) { } catch (error: any) {
console.log('V3 List Request failed.', error); console.log('V3 List Request failed.', error);
if (try_cache) { if (try_cache) {
ae_promises.load__event_device_obj_li = await db_events.device 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(); .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) { if (inc_location_id && ae_promises.load__event_device_obj_li) {
for (const device of ae_promises.load__event_device_obj_li) { for (const device of ae_promises.load__event_device_obj_li) {
if (device.event_location_id) { if (device.event_location_id) {
device.event_location_obj = await load_ae_obj_id__event_location({ device.event_location_obj =
api_cfg, await load_ae_obj_id__event_location({
event_location_id: device.event_location_id, api_cfg,
try_cache, event_location_id: device.event_location_id,
log_lvl try_cache,
}); log_lvl
});
} }
} }
} }
@@ -180,155 +191,161 @@ export async function load_ae_obj_li__event_device({
// Updated 2026-01-20 to V3 // Updated 2026-01-20 to V3
export async function create_ae_obj__event_device({ export async function create_ae_obj__event_device({
api_cfg, api_cfg,
event_id, event_id,
data_kv, data_kv,
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
event_id?: string; event_id?: string;
data_kv: key_val; data_kv: key_val;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventDevice | null> { }): Promise<ae_EventDevice | null> {
if (!event_id) event_id = get(slct).event_id; if (!event_id) event_id = get(slct).event_id;
if (!event_id) { if (!event_id) {
console.error('create_ae_obj__event_device: event_id is required'); console.error('create_ae_obj__event_device: event_id is required');
return null; return null;
} }
if (log_lvl) { if (log_lvl) {
console.log(`*** create_ae_obj__event_device() *** [V3] event_id=${event_id}`); console.log(
} `*** create_ae_obj__event_device() *** [V3] event_id=${event_id}`
);
}
const result = await api.create_nested_obj({ const result = await api.create_nested_obj({
api_cfg, api_cfg,
for_obj_type: 'event', for_obj_type: 'event',
for_obj_id: event_id, for_obj_id: event_id,
obj_type: 'event_device', obj_type: 'event_device',
fields: { ...data_kv }, fields: { ...data_kv },
log_lvl log_lvl
}); });
if (result) { if (result) {
const processed = await process_ae_obj__event_device_props({ const processed = await process_ae_obj__event_device_props({
obj_li: [result], obj_li: [result],
log_lvl log_lvl
}); });
const processed_obj = processed[0]; const processed_obj = processed[0];
if (try_cache) { if (try_cache) {
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_events, db_instance: db_events,
table_name: 'device', table_name: 'device',
obj_li: [processed_obj], obj_li: [processed_obj],
properties_to_save, properties_to_save,
log_lvl log_lvl
}); });
} }
return processed_obj; return processed_obj;
} }
return null; return null;
} }
// Updated 2026-01-20 to V3 // Updated 2026-01-20 to V3
export async function delete_ae_obj_id__event_device({ export async function delete_ae_obj_id__event_device({
api_cfg, api_cfg,
event_id, event_id,
event_device_id, event_device_id,
method = 'delete', method = 'delete',
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
event_id?: string; event_id?: string;
event_device_id: string; event_device_id: string;
method?: 'delete' | 'soft_delete' | 'disable' | 'hide'; method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}) { }) {
if (!event_id) event_id = get(slct).event_id; if (!event_id) event_id = get(slct).event_id;
if (!event_id) { if (!event_id) {
console.error('delete_ae_obj_id__event_device: event_id is required'); console.error('delete_ae_obj_id__event_device: event_id is required');
return null; return null;
} }
if (log_lvl) { if (log_lvl) {
console.log(`*** delete_ae_obj_id__event_device() *** [V3] id=${event_device_id}`); console.log(
} `*** delete_ae_obj_id__event_device() *** [V3] id=${event_device_id}`
);
}
const result = await api.delete_nested_ae_obj({ const result = await api.delete_nested_ae_obj({
api_cfg, api_cfg,
for_obj_type: 'event', for_obj_type: 'event',
for_obj_id: event_id, for_obj_id: event_id,
obj_type: 'event_device', obj_type: 'event_device',
obj_id: event_device_id, obj_id: event_device_id,
method, method,
log_lvl log_lvl
}); });
if (try_cache) { if (try_cache) {
await db_events.device.delete(event_device_id); await db_events.device.delete(event_device_id);
} }
return result; return result;
} }
// Updated 2026-01-20 to V3 // Updated 2026-01-20 to V3
export async function update_ae_obj__event_device({ export async function update_ae_obj__event_device({
api_cfg, api_cfg,
event_id, event_id,
event_device_id, event_device_id,
data_kv, data_kv,
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
event_id?: string; event_id?: string;
event_device_id: string; event_device_id: string;
data_kv: key_val; data_kv: key_val;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventDevice | null> { }): Promise<ae_EventDevice | null> {
if (!event_id) event_id = get(slct).event_id; if (!event_id) event_id = get(slct).event_id;
if (!event_id) { if (!event_id) {
console.error('update_ae_obj__event_device: event_id is required'); console.error('update_ae_obj__event_device: event_id is required');
return null; return null;
} }
if (log_lvl) { if (log_lvl) {
console.log(`*** update_ae_obj__event_device() *** [V3] id=${event_device_id}`); console.log(
} `*** update_ae_obj__event_device() *** [V3] id=${event_device_id}`
);
}
const result = await api.update_nested_obj({ const result = await api.update_nested_obj({
api_cfg, api_cfg,
for_obj_type: 'event', for_obj_type: 'event',
for_obj_id: event_id, for_obj_id: event_id,
obj_type: 'event_device', obj_type: 'event_device',
obj_id: event_device_id, obj_id: event_device_id,
fields: data_kv, fields: data_kv,
log_lvl log_lvl
}); });
if (result) { if (result) {
const processed = await process_ae_obj__event_device_props({ const processed = await process_ae_obj__event_device_props({
obj_li: [result], obj_li: [result],
log_lvl log_lvl
}); });
const processed_obj = processed[0]; const processed_obj = processed[0];
if (try_cache) { if (try_cache) {
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_events, db_instance: db_events,
table_name: 'device', table_name: 'device',
obj_li: [processed_obj], obj_li: [processed_obj],
properties_to_save, properties_to_save,
log_lvl log_lvl
}); });
} }
return processed_obj; return processed_obj;
} }
return null; return null;
} }
// Updated 2026-01-20 to V3 // Updated 2026-01-20 to V3
@@ -361,7 +378,9 @@ export async function search__event_device({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventDevice[]> { }): Promise<ae_EventDevice[]> {
if (log_lvl) { 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 = { const search_query: any = {
@@ -370,11 +389,15 @@ export async function search__event_device({
}; };
// Logical filters // Logical filters
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: true }); if (enabled === 'enabled')
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: false }); 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 }); if (hidden === 'hidden')
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: false }); 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({ const result_li = await api.search_ae_obj({
api_cfg, api_cfg,
@@ -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 updated = processed_obj.updated_on ?? processed_obj.created_on;
const name = processed_obj.name ?? ''; const name = processed_obj.name ?? '';
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`; (processed_obj as any).tmp_sort_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
(processed_obj as any).tmp_sort_2 =
`${group}_${priority}_${sort}_${name}_${updated}`;
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);

View File

@@ -191,7 +191,9 @@ async function _refresh_file_li_background({
if (typeof navigator !== 'undefined' && !navigator.onLine) return []; if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
try { try {
if (log_lvl) { 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({ const result_li = await api.get_ae_obj_li({
@@ -211,7 +213,10 @@ async function _refresh_file_li_background({
if (result_li) { if (result_li) {
if (log_lvl) { 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) { if (result_li.length > 0) {
console.log(`🔍 [DEBUG] Sample Raw IDs:`, { console.log(`🔍 [DEBUG] Sample Raw IDs:`, {
id: result_li[0].id, id: result_li[0].id,
@@ -253,7 +258,10 @@ async function _refresh_file_li_background({
} }
if (try_cache) { 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({ await db_save_ae_obj_li__ae_obj({
db_instance: db_events, db_instance: db_events,
table_name: 'file', table_name: 'file',
@@ -265,10 +273,15 @@ async function _refresh_file_li_background({
return processed; return processed;
} }
} catch (e) { } 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 []; 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, api_cfg,
hosted_file_id, hosted_file_id,
params = {}, params = {},
@@ -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 []; return [];
@@ -545,7 +558,11 @@ async function _process_generic_props<T extends Record<string, any>>({
if (key.endsWith('_random')) { if (key.endsWith('_random')) {
const newKey = key.slice(0, -7); const newKey = key.slice(0, -7);
// ONLY overwrite if the random variant has a valid value // 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]; (processed_obj as any)[newKey] = processed_obj[key];
} }
} }

View File

@@ -37,7 +37,9 @@ export async function load_ae_obj_id__event_location({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventLocation | null> { }): Promise<ae_EventLocation | null> {
if (log_lvl) { 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 // 1. FAST PATH: Cache hit
@@ -46,12 +48,26 @@ export async function load_ae_obj_id__event_location({
const cached = await db_events.location.get(event_location_id); const cached = await db_events.location.get(event_location_id);
if (cached) { if (cached) {
_refresh_location_id_background({ _refresh_location_id_background({
api_cfg, event_location_id, view, try_cache, api_cfg,
inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li, 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 log_lvl: 0
}); });
return await _handle_nested_loads(cached, { 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, api_cfg,
inc_file_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_device_li,
inc_all_file_li,
log_lvl log_lvl
}); });
} }
@@ -60,24 +76,65 @@ export async function load_ae_obj_id__event_location({
// 2. SLOW PATH: Wait for API // 2. SLOW PATH: Wait for API
return await _refresh_location_id_background({ return await _refresh_location_id_background({
api_cfg, event_location_id, view, try_cache, api_cfg,
inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li, 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 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; if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
try { try {
const result = await api.get_ae_obj({ 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) { 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]; const processed_obj = processed[0];
if (try_cache) { 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, { 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, api_cfg,
inc_file_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_device_li,
inc_all_file_li,
log_lvl log_lvl
}); });
} }
@@ -129,23 +186,47 @@ export async function load_ae_obj_li__event_location({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventLocation[]> { }): Promise<ae_EventLocation[]> {
if (log_lvl) { 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 // 1. FAST PATH: Check cache
if (try_cache) { if (try_cache) {
try { 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) { if (cached_li && cached_li.length > 0) {
_refresh_location_li_background({ _refresh_location_li_background({
api_cfg, for_obj_type, for_obj_id, api_cfg,
inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li, for_obj_type,
enabled, hidden, view, limit, offset, order_by_li, try_cache, 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 log_lvl: 0
}); });
for (const loc of cached_li) { for (const loc of cached_li) {
_handle_nested_loads(loc, { _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, api_cfg,
inc_file_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_device_li,
inc_all_file_li,
log_lvl: 0 log_lvl: 0
}); });
} }
@@ -156,31 +237,89 @@ export async function load_ae_obj_li__event_location({
// 2. SLOW PATH: API // 2. SLOW PATH: API
return await _refresh_location_li_background({ return await _refresh_location_li_background({
api_cfg, for_obj_type, for_obj_id, api_cfg,
inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li, for_obj_type,
enabled, hidden, view, limit, offset, order_by_li, try_cache, 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 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 []; if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
try { try {
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 }); 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) { 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 // String-Only ID Vision: Ensure linking ID is set for indexing
if (for_obj_type === 'event') { 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) { 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) { for (const loc of processed) {
_handle_nested_loads(loc, { _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, api_cfg,
inc_file_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_device_li,
inc_all_file_li,
log_lvl: 0 log_lvl: 0
}); });
} }
@@ -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. * 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) { async function _handle_nested_loads(
const current_location_id = location_obj.id || location_obj.event_location_id; 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; if (!current_location_id) return location_obj;
const tasks = []; const tasks = [];
if (inc_file_li) { if (inc_file_li) {
tasks.push(load_ae_obj_li__event_file({ tasks.push(
api_cfg, for_obj_type: 'event_location', for_obj_id: current_location_id, load_ae_obj_li__event_file({
enabled: 'all', limit: 25, log_lvl api_cfg,
}).then(res => location_obj.event_file_li = res)); 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) { if (inc_session_li) {
tasks.push(load_ae_obj_li__event_session({ tasks.push(
api_cfg, for_obj_type: 'event_location', for_obj_id: current_location_id, load_ae_obj_li__event_session({
inc_file_li: inc_all_file_li, api_cfg,
inc_all_file_li: inc_all_file_li, for_obj_type: 'event_location',
inc_presentation_li: inc_presentation_li, for_obj_id: current_location_id,
inc_presenter_li: inc_presenter_li, inc_file_li: inc_all_file_li,
enabled: 'enabled', hidden: 'not_hidden', limit: 150, log_lvl inc_all_file_li: inc_all_file_li,
}).then(res => location_obj.event_session_obj_li = res)); 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) { if (inc_device_li) {
tasks.push(load_ae_obj_li__event_device({ tasks.push(
api_cfg, for_obj_type: 'event_location', for_obj_id: current_location_id, load_ae_obj_li__event_device({
enabled: 'all', limit: 50, log_lvl api_cfg,
}).then(res => location_obj.event_device_li = res)); 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); 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 // Updated 2026-01-20 to V3
export async function create_ae_obj__event_location({ export async function create_ae_obj__event_location({
api_cfg, api_cfg,
event_id, event_id,
data_kv, data_kv,
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
event_id: string; event_id: string;
data_kv: key_val; data_kv: key_val;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventLocation | null> { }): Promise<ae_EventLocation | null> {
const result = await api.create_nested_obj({ const result = await api.create_nested_obj({
api_cfg, api_cfg,
for_obj_type: 'event', for_obj_type: 'event',
for_obj_id: event_id, for_obj_id: event_id,
obj_type: 'event_location', obj_type: 'event_location',
fields: { ...data_kv }, fields: { ...data_kv },
log_lvl log_lvl
}); });
if (result) { 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({
const processed_obj = processed[0]; obj_li: [result],
if (try_cache) { log_lvl
await db_save_ae_obj_li__ae_obj({ });
db_instance: db_events, const processed_obj = processed[0];
table_name: 'location', if (try_cache) {
obj_li: [processed_obj], await db_save_ae_obj_li__ae_obj({
properties_to_save, db_instance: db_events,
log_lvl table_name: 'location',
}); obj_li: [processed_obj],
} properties_to_save,
return processed_obj; log_lvl
} });
return null; }
return processed_obj;
}
return null;
} }
// Updated 2026-01-20 to V3 // Updated 2026-01-20 to V3
export async function delete_ae_obj_id__event_location({ export async function delete_ae_obj_id__event_location({
api_cfg, api_cfg,
event_id, event_id,
event_location_id, event_location_id,
method = 'delete', method = 'delete',
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
event_id: string; event_id: string;
event_location_id: string; event_location_id: string;
method?: 'delete' | 'soft_delete' | 'disable' | 'hide'; method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}) { }) {
const result = await api.delete_nested_ae_obj({ const result = await api.delete_nested_ae_obj({
api_cfg, api_cfg,
for_obj_type: 'event', for_obj_type: 'event',
for_obj_id: event_id, for_obj_id: event_id,
obj_type: 'event_location', obj_type: 'event_location',
obj_id: event_location_id, obj_id: event_location_id,
method, method,
log_lvl log_lvl
}); });
if (try_cache) await db_events.location.delete(event_location_id); if (try_cache) await db_events.location.delete(event_location_id);
return result; return result;
} }
// Updated 2026-01-20 to V3 // Updated 2026-01-20 to V3
export async function update_ae_obj__event_location({ export async function update_ae_obj__event_location({
api_cfg, api_cfg,
event_id, event_id,
event_location_id, event_location_id,
data_kv, data_kv,
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
event_id: string; event_id: string;
event_location_id: string; event_location_id: string;
data_kv: key_val; data_kv: key_val;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventLocation | null> { }): Promise<ae_EventLocation | null> {
const result = await api.update_nested_obj({ const result = await api.update_nested_obj({
api_cfg, api_cfg,
for_obj_type: 'event', for_obj_type: 'event',
for_obj_id: event_id, for_obj_id: event_id,
obj_type: 'event_location', obj_type: 'event_location',
obj_id: event_location_id, obj_id: event_location_id,
fields: data_kv, fields: data_kv,
log_lvl log_lvl
}); });
if (result) { 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({
const processed_obj = processed[0]; obj_li: [result],
if (try_cache) { log_lvl
await db_save_ae_obj_li__ae_obj({ });
db_instance: db_events, const processed_obj = processed[0];
table_name: 'location', if (try_cache) {
obj_li: [processed_obj], await db_save_ae_obj_li__ae_obj({
properties_to_save, db_instance: db_events,
log_lvl table_name: 'location',
}); obj_li: [processed_obj],
} properties_to_save,
return processed_obj; log_lvl
} });
return null; }
return processed_obj;
}
return null;
} }
// Updated 2026-01-20 to V3 // Updated 2026-01-20 to V3
export async function search__event_location({ 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[]> { }): Promise<ae_EventLocation[]> {
const search_query: any = { q: qry_str, and: [{ field: 'event_id', op: 'eq', value: event_id }] }; const search_query: any = {
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: true }); q: qry_str,
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: false }); and: [{ field: 'event_id', op: 'eq', value: event_id }]
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 (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({ 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) { 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) { 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; return processed;
} }
@@ -362,10 +584,40 @@ export async function search__event_location({
} }
export const properties_to_save = [ 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 []; if (!obj_li || obj_li.length === 0) return [];
const processed_obj_li: T[] = []; const processed_obj_li: T[] = [];
for (const original_obj of obj_li) { 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 randomIdKey = `${obj_type}_id_random`;
const baseIdKey = `${obj_type}_id`; const baseIdKey = `${obj_type}_id`;
if (processed_obj[randomIdKey]) (processed_obj as any).id = processed_obj[randomIdKey]; if (processed_obj[randomIdKey])
else if (processed_obj[baseIdKey]) (processed_obj as any).id = processed_obj[baseIdKey]; (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 group = processed_obj.group ?? '0';
const priority = processed_obj.priority ? 1 : 0; const priority = processed_obj.priority ? 1 : 0;
const sort = processed_obj.sort ?? '0'; const sort = processed_obj.sort ?? '0';
const updated = processed_obj.updated_on ?? processed_obj.created_on; const updated = processed_obj.updated_on ?? processed_obj.created_on;
const name = processed_obj.name ?? ''; const name = processed_obj.name ?? '';
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`; (processed_obj as any).tmp_sort_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
if (specific_processor) processed_obj = await Promise.resolve(specific_processor(processed_obj)); (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); processed_obj_li.push(processed_obj as T);
} }
return processed_obj_li; return processed_obj_li;
} }
export async function process_ae_obj__event_location_props({ obj_li, log_lvl = 0 }: { obj_li: any[]; log_lvl?: number; }) { export async function process_ae_obj__event_location_props({
return _process_generic_props({ obj_li, obj_type: 'event_location', log_lvl, specific_processor: (obj) => { obj_li,
if (obj.event_id_random) obj.event_id = obj.event_id_random; log_lvl = 0
return obj; }: {
}}); 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;
}
});
} }

View File

@@ -37,41 +37,116 @@ export async function load_ae_obj_id__event_presentation({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventPresentation | null> { }): Promise<ae_EventPresentation | null> {
if (log_lvl) { 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 // 1. FAST PATH: Cache hit
if (try_cache) { if (try_cache) {
try { try {
const cached = await db_events.presentation.get(event_presentation_id); const cached = await db_events.presentation.get(
event_presentation_id
);
if (cached) { if (cached) {
// Background refresh (non-blocking) // 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 // 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) {} } catch (e) {}
} }
// 2. SLOW PATH: Wait for API // 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 * 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; if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
try { try {
const result = await api.get_ae_obj({ 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) { 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]; const processed_obj = processed[0];
if (try_cache) { 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 // 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) {} } catch (e) {}
return null; 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 * 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) { async function _handle_nested_loads(
const current_presentation_id = presentation_obj.id || presentation_obj.event_presentation_id; 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; if (!current_presentation_id) return presentation_obj;
const tasks = []; const tasks = [];
if (inc_file_li) { if (inc_file_li) {
tasks.push(load_ae_obj_li__event_file({ tasks.push(
api_cfg, for_obj_type: 'event_presentation', for_obj_id: current_presentation_id, load_ae_obj_li__event_file({
enabled, limit: 25, try_cache, log_lvl api_cfg,
}).then(res => presentation_obj.event_file_li = res)); 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) { if (inc_presenter_li) {
tasks.push(load_ae_obj_li__event_presenter({ tasks.push(
api_cfg, for_obj_type: 'event_presentation', for_obj_id: current_presentation_id, load_ae_obj_li__event_presenter({
inc_file_li, enabled, hidden, limit, offset, try_cache, log_lvl api_cfg,
}).then(res => presentation_obj.event_presenter_li = res)); 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); if (tasks.length > 0) await Promise.all(tasks);
@@ -140,17 +246,36 @@ export async function load_ae_obj_li__event_presentation({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventPresentation[]> { }): Promise<ae_EventPresentation[]> {
if (log_lvl) { 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 // 1. FAST PATH: Cache hit
if (try_cache) { if (try_cache) {
try { try {
// String-Only ID Vision: query event_session_id using the string ID // 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) { if (cached_li && cached_li.length > 0) {
// Background refresh (non-blocking) // 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) // 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. // 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 // 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 []; if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
try { try {
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 }); 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) { 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 // Ensure the linking ID is set correctly for indexing
if (for_obj_type === 'event_session') { 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) { 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 // CRITICAL FIX (2026-02-26): Yield to microtask queue so Dexie liveQuery observers
// fire before we return. Without this, component-mounted liveQueries may subscribe // fire before we return. Without this, component-mounted liveQueries may subscribe
// to IDB *before* the write completes, causing empty results on cold-start. // 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. // 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. // Now we await all nested loads AND preserve try_cache so presenters are written to IDB.
if (inc_file_li || inc_presenter_li) { if (inc_file_li || inc_presenter_li) {
await Promise.all(processed.map(p => await Promise.all(
_handle_nested_loads(p, { api_cfg, inc_file_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl: 0 }) 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; return processed;
} }
@@ -206,42 +392,45 @@ async function _refresh_presentation_li_background({ api_cfg, for_obj_type, for_
// Updated 2026-01-20 to V3 // Updated 2026-01-20 to V3
export async function create_ae_obj__event_presentation({ export async function create_ae_obj__event_presentation({
api_cfg, api_cfg,
event_session_id, event_session_id,
data_kv, data_kv,
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
event_session_id: string; event_session_id: string;
data_kv: key_val; data_kv: key_val;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventPresentation | null> { }): Promise<ae_EventPresentation | null> {
const result = await api.create_nested_obj({ const result = await api.create_nested_obj({
api_cfg, api_cfg,
for_obj_type: 'event_session', for_obj_type: 'event_session',
for_obj_id: event_session_id, for_obj_id: event_session_id,
obj_type: 'event_presentation', obj_type: 'event_presentation',
fields: { ...data_kv }, fields: { ...data_kv },
log_lvl log_lvl
}); });
if (result) { 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({
const processed_obj = processed[0]; obj_li: [result],
if (try_cache) { log_lvl
await db_save_ae_obj_li__ae_obj({ });
db_instance: db_events, const processed_obj = processed[0];
table_name: 'presentation', if (try_cache) {
obj_li: [processed_obj], await db_save_ae_obj_li__ae_obj({
properties_to_save, db_instance: db_events,
log_lvl table_name: 'presentation',
}); obj_li: [processed_obj],
} properties_to_save,
return processed_obj; log_lvl
} });
return null; }
return processed_obj;
}
return null;
} }
// Updated 2026-01-20 to V3 // Updated 2026-01-20 to V3
@@ -259,7 +448,11 @@ export async function delete_ae_obj_id__event_presentation({
log_lvl?: number; log_lvl?: number;
}) { }) {
const result = await api.delete_ae_obj({ const result = await api.delete_ae_obj({
api_cfg, obj_type: 'event_presentation', obj_id: event_presentation_id, method, log_lvl 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); if (try_cache) await db_events.presentation.delete(event_presentation_id);
return result; return result;
@@ -280,13 +473,26 @@ export async function update_ae_obj__event_presentation({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventPresentation | null> { }): Promise<ae_EventPresentation | null> {
const result = await api.update_ae_obj({ const result = await api.update_ae_obj({
api_cfg, obj_type: 'event_presentation', obj_id: event_presentation_id, fields: data_kv, log_lvl api_cfg,
obj_type: 'event_presentation',
obj_id: event_presentation_id,
fields: data_kv,
log_lvl
}); });
if (result) { 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]; const processed_obj = processed[0];
if (try_cache) { 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; 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 // Updated 2026-01-21 to Restore Full Aether Search Logic
export async function search__event_presentation({ 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[]> { }): 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 = {}; 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 (fulltext_search_qry_str && fulltext_search_qry_str.length > 2)
if (like_search_qry_str) params['lk_qry'] = { 'default_qry_str': like_search_qry_str }; params['ft_qry'] = { default_qry_str: fulltext_search_qry_str };
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 1 }); if (like_search_qry_str)
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 0 }); params['lk_qry'] = { default_qry_str: like_search_qry_str };
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 1 }); if (enabled === 'enabled')
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 0 }); 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({ 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) { 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) { 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; return processed;
} }
@@ -322,10 +578,53 @@ export async function search__event_presentation({
export const qry__event_presentation = search__event_presentation; export const qry__event_presentation = search__event_presentation;
export const properties_to_save = [ 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 []; if (!obj_li || obj_li.length === 0) return [];
const processed_obj_li: T[] = []; const processed_obj_li: T[] = [];
for (const original_obj of obj_li) { 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 randomIdKey = `${obj_type}_id_random`;
const baseIdKey = `${obj_type}_id`; const baseIdKey = `${obj_type}_id`;
if (processed_obj[randomIdKey]) (processed_obj as any).id = processed_obj[randomIdKey]; if (processed_obj[randomIdKey])
else if (processed_obj[baseIdKey]) (processed_obj as any).id = processed_obj[baseIdKey]; (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 group = processed_obj.group ?? '0';
const priority = processed_obj.priority ? 1 : 0; const priority = processed_obj.priority ? 1 : 0;
const sort = processed_obj.sort ?? '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 ?? ''; const name = processed_obj.name ?? '';
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`; (processed_obj as any).tmp_sort_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
if (specific_processor) processed_obj = await Promise.resolve(specific_processor(processed_obj)); (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); processed_obj_li.push(processed_obj as T);
} }
return processed_obj_li; return processed_obj_li;
} }
export async function process_ae_obj__event_presentation_props({ obj_li, log_lvl = 0 }: { obj_li: any[]; log_lvl?: number; }) { export async function process_ae_obj__event_presentation_props({
return _process_generic_props({ obj_li, obj_type: 'event_presentation', log_lvl, specific_processor: (obj) => { obj_li,
// Ensure linking IDs are the string versions for indexing log_lvl = 0
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; obj_li: any[];
return obj; 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;
}
});
} }

View File

@@ -29,7 +29,9 @@ export async function load_ae_obj_id__event_presenter({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventPresenter | null> { }): Promise<ae_EventPresenter | null> {
if (log_lvl) { 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 // 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); const cached = await db_events.presenter.get(event_presenter_id);
if (cached) { if (cached) {
// Background refresh (non-blocking) // 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; return cached;
} }
} catch (e) {} } catch (e) {}
} }
// 2. SLOW PATH: Wait for API // 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; if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
try { try {
const result = await api.get_ae_obj({ 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) { 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]; const processed_obj = processed[0];
if (try_cache) { 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) { if (inc_file_li) {
processed_obj.event_file_li = await load_ae_obj_li__event_file({ 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, api_cfg,
enabled: 'all', limit: 25, try_cache: false, log_lvl for_obj_type: 'event_presenter',
for_obj_id: event_presenter_id,
enabled: 'all',
limit: 25,
try_cache: false,
log_lvl
}); });
} }
return processed_obj; return processed_obj;
@@ -104,7 +147,9 @@ export async function load_ae_obj_li__event_presenter({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventPresenter[]> { }): Promise<ae_EventPresenter[]> {
if (log_lvl) { 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 // 1. FAST PATH: Check cache using specific indices
@@ -112,31 +157,94 @@ export async function load_ae_obj_li__event_presenter({
try { try {
let cached_li: any[] = []; let cached_li: any[] = [];
if (for_obj_type === 'event_presentation') { 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') { } 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') { } 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) { if (cached_li && cached_li.length > 0) {
// Background refresh (non-blocking) // 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; return cached_li;
} }
} catch (e) {} } catch (e) {}
} }
// 2. SLOW PATH: Wait for API // 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 []; if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
try { try {
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 }); 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) { 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 // String-Only ID Vision: Ensure linking ID is set for indexing
processed.forEach((p) => { processed.forEach((p) => {
@@ -155,7 +263,13 @@ async function _refresh_presenter_li_background({ api_cfg, for_obj_type, for_obj
}); });
if (try_cache) { 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 // CRITICAL FIX (2026-02-26): Yield to microtask queue so Dexie liveQuery observers
// fire before we return. Without this, component-mounted liveQueries may subscribe // fire before we return. Without this, component-mounted liveQueries may subscribe
// to IDB *before* the write completes, causing empty results on cold-start. // 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) // Background nested loads for refreshed items (FIRE AND FORGET)
if (inc_file_li) { if (inc_file_li) {
processed.forEach(p => { processed.forEach((p) => {
load_ae_obj_li__event_file({ load_ae_obj_li__event_file({
api_cfg, for_obj_type: 'event_presenter', for_obj_id: p.id, api_cfg,
enabled: 'all', limit: 25, try_cache: false, log_lvl: 0 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 // Updated 2026-01-20 to V3
export async function create_ae_obj__event_presenter({ export async function create_ae_obj__event_presenter({
api_cfg, api_cfg,
event_presentation_id, event_presentation_id,
data_kv, data_kv,
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
event_presentation_id?: string; event_presentation_id?: string;
data_kv: key_val; data_kv: key_val;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventPresenter | null> { }): 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) { 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; return null;
} }
const result = await api.create_nested_obj({ const result = await api.create_nested_obj({
api_cfg, api_cfg,
for_obj_type: 'event_presentation', for_obj_type: 'event_presentation',
for_obj_id: event_presentation_id, for_obj_id: event_presentation_id,
obj_type: 'event_presenter', obj_type: 'event_presenter',
fields: { ...data_kv }, fields: { ...data_kv },
log_lvl log_lvl
}); });
if (result) { 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({
const processed_obj = processed[0]; obj_li: [result],
if (try_cache) { log_lvl
await db_save_ae_obj_li__ae_obj({ });
db_instance: db_events, const processed_obj = processed[0];
table_name: 'presenter', if (try_cache) {
obj_li: [processed_obj], await db_save_ae_obj_li__ae_obj({
properties_to_save, db_instance: db_events,
log_lvl table_name: 'presenter',
}); obj_li: [processed_obj],
} properties_to_save,
return processed_obj; log_lvl
} });
return null; }
return processed_obj;
}
return null;
} }
// Updated 2026-01-20 to V3 // Updated 2026-01-20 to V3
export async function delete_ae_obj_id__event_presenter({ export async function delete_ae_obj_id__event_presenter({
api_cfg, api_cfg,
event_presentation_id, event_presentation_id,
event_presenter_id, event_presenter_id,
method = 'delete', method = 'delete',
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
event_presentation_id?: string; event_presentation_id?: string;
event_presenter_id: string; event_presenter_id: string;
method?: 'delete' | 'soft_delete' | 'disable' | 'hide'; method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; 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) { 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; return null;
} }
const result = await api.delete_nested_ae_obj({ const result = await api.delete_nested_ae_obj({
api_cfg, api_cfg,
for_obj_type: 'event_presentation', for_obj_type: 'event_presentation',
for_obj_id: event_presentation_id, for_obj_id: event_presentation_id,
obj_type: 'event_presenter', obj_type: 'event_presenter',
obj_id: event_presenter_id, obj_id: event_presenter_id,
method, method,
log_lvl log_lvl
}); });
if (try_cache) await db_events.presenter.delete(event_presenter_id); if (try_cache) await db_events.presenter.delete(event_presenter_id);
return result; return result;
} }
// Updated 2026-01-20 to V3 // Updated 2026-01-20 to V3
export async function update_ae_obj__event_presenter({ export async function update_ae_obj__event_presenter({
api_cfg, api_cfg,
event_presentation_id, event_presentation_id,
event_presenter_id, event_presenter_id,
data_kv, data_kv,
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
event_presentation_id?: string; event_presentation_id?: string;
event_presenter_id: string; event_presenter_id: string;
data_kv: key_val; data_kv: key_val;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventPresenter | null> { }): 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) { 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; return null;
} }
const result = await api.update_nested_obj({ const result = await api.update_nested_obj({
api_cfg, api_cfg,
for_obj_type: 'event_presentation', for_obj_type: 'event_presentation',
for_obj_id: event_presentation_id, for_obj_id: event_presentation_id,
obj_type: 'event_presenter', obj_type: 'event_presenter',
obj_id: event_presenter_id, obj_id: event_presenter_id,
fields: data_kv, fields: data_kv,
log_lvl log_lvl
}); });
if (result) { 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({
const processed_obj = processed[0]; obj_li: [result],
if (try_cache) { log_lvl
await db_save_ae_obj_li__ae_obj({ });
db_instance: db_events, const processed_obj = processed[0];
table_name: 'presenter', if (try_cache) {
obj_li: [processed_obj], await db_save_ae_obj_li__ae_obj({
properties_to_save, db_instance: db_events,
log_lvl table_name: 'presenter',
}); obj_li: [processed_obj],
} properties_to_save,
return processed_obj; log_lvl
} });
return null; }
return processed_obj;
}
return null;
} }
// Updated 2026-01-21 to Restore Full Aether Search Logic // Updated 2026-01-21 to Restore Full Aether Search Logic
@@ -320,7 +454,11 @@ export async function search__event_presenter({
view = 'default', view = 'default',
limit = 25, limit = 25,
offset = 0, 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, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
@@ -342,29 +480,71 @@ export async function search__event_presenter({
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventPresenter[]> { }): 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 = {}; const params: key_val = {};
if (fulltext_search_qry_str && fulltext_search_qry_str.length > 2) if (fulltext_search_qry_str && fulltext_search_qry_str.length > 2)
params['ft_qry'] = { default_qry_str: fulltext_search_qry_str }; params['ft_qry'] = { default_qry_str: fulltext_search_qry_str };
if (ft_presenter_search_qry_str && ft_presenter_search_qry_str.length > 2) 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 }; params['ft_qry'] = {
if (like_search_qry_str) params['lk_qry'] = { default_qry_str: like_search_qry_str }; ...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) 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) if (like_presenter_search_qry_str)
params['lk_qry'] = { ...params['lk_qry'], event_presenter_li_qry_str: like_presenter_search_qry_str }; params['lk_qry'] = {
if (agree !== null) search_query.and.push({ field: 'agree', op: 'eq', value: agree ? 1 : 0 }); ...params['lk_qry'],
if (biography === true) search_query.and.push({ field: 'biography', op: 'ne', value: '' }); event_presenter_li_qry_str: like_presenter_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 (agree !== null)
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 1 }); search_query.and.push({
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 0 }); 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({ 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) { 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) { 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; return processed;
} }
@@ -400,8 +580,16 @@ export async function email_sign_in__event_presenter({
session_name?: string | null; session_name?: string | null;
presentation_name?: string | null; presentation_name?: string | null;
}) { }) {
if (!to_email || !person_id || !person_passcode || !event_id || !event_presenter_id) { if (
console.error('Missing required parameters for email_sign_in__event_presenter'); !to_email ||
!person_id ||
!person_passcode ||
!event_id ||
!event_presenter_id
) {
console.error(
'Missing required parameters for email_sign_in__event_presenter'
);
return null; return null;
} }
const subject = `Pres Mgmt Hub Sign In Link for Presenter: ${to_name ?? 'Presenter'}`; 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 = [ 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 []; if (!obj_li || obj_li.length === 0) return [];
const processed_obj_li: T[] = []; const processed_obj_li: T[] = [];
for (const original_obj of obj_li) { 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]) { if (processed_obj[randomIdKey]) {
(processed_obj as any).id = processed_obj[randomIdKey]; (processed_obj as any).id = processed_obj[randomIdKey];
(processed_obj as any)[baseIdKey] = processed_obj[randomIdKey]; (processed_obj as any)[baseIdKey] = processed_obj[randomIdKey];
} } else if (processed_obj[baseIdKey])
else if (processed_obj[baseIdKey]) (processed_obj as any).id = processed_obj[baseIdKey]; (processed_obj as any).id = processed_obj[baseIdKey];
const group = processed_obj.group ?? '0'; const group = processed_obj.group ?? '0';
const priority = processed_obj.priority ? 1 : 0; const priority = processed_obj.priority ? 1 : 0;
const sort = processed_obj.sort ?? '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 ?? ''; const name = processed_obj.name ?? '';
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`; (processed_obj as any).tmp_sort_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
if (specific_processor) processed_obj = await Promise.resolve(specific_processor(processed_obj)); (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); processed_obj_li.push(processed_obj as T);
} }
return processed_obj_li; return processed_obj_li;
} }
export async function process_ae_obj__event_presenter_props({ obj_li, log_lvl = 0 }: { obj_li: any[]; log_lvl?: number; }) { export async function process_ae_obj__event_presenter_props({
return _process_generic_props({ obj_li, obj_type: 'event_presenter', log_lvl, specific_processor: (obj) => { obj_li,
// String-Only ID Vision: Ensure linking IDs are the string versions for indexing log_lvl = 0
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; obj_li: any[];
if (obj.event_session_id_random) obj.event_session_id = obj.event_session_id_random; log_lvl?: number;
if (obj.event_id_random) obj.event_id = obj.event_id_random; }) {
return obj; 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;
}
});
} }

View File

@@ -44,7 +44,9 @@ export async function load_ae_obj_id__event_session({
}): Promise<ae_EventSession | null> { }): Promise<ae_EventSession | null> {
const start_time = performance.now(); const start_time = performance.now();
if (log_lvl) { 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 // 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); const cached = await db_events.session.get(event_session_id);
if (cached) { if (cached) {
const elapsed = (performance.now() - start_time).toFixed(2); 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) // Background tasks: refresh parent and warm child caches (non-blocking)
_refresh_session_id_background({ _refresh_session_id_background({
api_cfg, event_session_id, view, try_cache, api_cfg,
inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, event_session_id,
enabled, hidden, limit, offset, log_lvl: log_lvl > 1 ? log_lvl : 0 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 // 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 return cached; // Return immediately without awaiting nested loads
} else if (log_lvl) { } 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) { } 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 // 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}`); if (log_lvl)
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 }); 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 * 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(); const start_time = performance.now();
if (typeof navigator !== 'undefined' && !navigator.onLine) return null; if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
try { try {
if (log_lvl) console.log(`📡 [Trace] _refresh_session_id: API Fetching id=${event_session_id}`); if (log_lvl)
const result = await api.get_ae_obj({ api_cfg, obj_type: 'event_session', obj_id: event_session_id, view, 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) { 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 processed_obj = processed[0];
const elapsed = (performance.now() - start_time).toFixed(2); 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) { 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 // CRITICAL FIX (2026-02-26): Yield to microtask queue so Dexie liveQuery observers
// fire before we return. Without this, component-mounted liveQueries may subscribe // fire before we return. Without this, component-mounted liveQueries may subscribe
// to IDB *before* the write completes, causing empty results on cold-start. // to IDB *before* the write completes, causing empty results on cold-start.
await Promise.resolve(); 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. // 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 // Previously set to `false`, which meant presentations/presenters were fetched from API
// but NEVER written to IndexedDB, causing "refresh twice" bug on cold-start. // but NEVER written to IndexedDB, causing "refresh twice" bug on cold-start.
// Now nested loads inherit parent's caching behavior for deterministic first-render. // 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) { } 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; 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 * 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 start_time = performance.now();
const current_session_id = session_obj.id || session_obj.event_session_id; const current_session_id = session_obj.id || session_obj.event_session_id;
if (!current_session_id) return session_obj; if (!current_session_id) return session_obj;
const tasks = []; const tasks = [];
if (inc_file_li) { if (inc_file_li) {
tasks.push(load_ae_obj_li__event_file({ tasks.push(
api_cfg, for_obj_type: 'event_session', for_obj_id: current_session_id, load_ae_obj_li__event_file({
enabled, limit: 15, try_cache, log_lvl api_cfg,
}).then(res => session_obj.event_file_li = res)); 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) { if (inc_presentation_li) {
tasks.push(load_ae_obj_li__event_presentation({ tasks.push(
api_cfg, for_obj_type: 'event_session', for_obj_id: current_session_id, load_ae_obj_li__event_presentation({
inc_file_li: inc_all_file_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl api_cfg,
}).then(res => session_obj.event_presentation_li = res)); 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) { if (tasks.length > 0) {
await Promise.all(tasks); 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; return session_obj;
} }
@@ -191,7 +331,9 @@ export async function load_ae_obj_li__event_session({
}): Promise<ae_EventSession[]> { }): Promise<ae_EventSession[]> {
const start_time = performance.now(); const start_time = performance.now();
if (log_lvl) { 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 // Hierarchy Enforcement: Pulling presenters requires pulling presentations first
@@ -201,226 +343,409 @@ export async function load_ae_obj_li__event_session({
try { try {
// Robust lookup logic // Robust lookup logic
let query; let query;
if (for_obj_type === 'event_location') query = db_events.session.where('event_location_id').equals(for_obj_id); if (for_obj_type === 'event_location')
else if (for_obj_type === 'event') query = db_events.session.where('event_id').equals(for_obj_id); 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); else query = db_events.session.where('for_id').equals(for_obj_id);
const cached_li = await query.toArray(); const cached_li = await query.toArray();
if (cached_li && cached_li.length > 0) { if (cached_li && cached_li.length > 0) {
const elapsed = (performance.now() - start_time).toFixed(2); 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) // Background refresh (non-blocking)
_refresh_session_li_background({ _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 // 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, inc_file_li: false,
enabled, hidden, limit, offset, order_by_li, try_cache, 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 log_lvl: log_lvl > 1 ? log_lvl : 0
}); });
return cached_li; return cached_li;
} else if (log_lvl) { } 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) { } 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(); const start_time = performance.now();
if (typeof navigator !== 'undefined' && !navigator.onLine) return []; if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
try { try {
if (log_lvl) console.log(`📡 [Trace] _refresh_session_li: API Fetching for=${for_obj_type}:${for_obj_id} (view=${view})`); if (log_lvl)
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 }); 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) { 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); 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) { 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({
if (log_lvl) console.log(`💾 [Trace] _refresh_session_li: Saved to IDB cache.`); 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) // Fire nested loads for each session only if explicitly requested (usually only for single objects)
if (inc_file_li || inc_presentation_li) { if (inc_file_li || inc_presentation_li) {
processed.forEach(s => { 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 }); _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; return processed;
} }
} catch (e) { } 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 []; return [];
} }
export async function create_ae_obj__event_session({ export async function create_ae_obj__event_session({
api_cfg, api_cfg,
event_id, event_id,
data_kv, data_kv,
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
event_id?: string; event_id?: string;
data_kv: key_val; data_kv: key_val;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventSession | null> { }): Promise<ae_EventSession | null> {
if (!event_id) event_id = get(slct).event_id; if (!event_id) event_id = get(slct).event_id;
if (!event_id) { if (!event_id) {
console.error('create_ae_obj__event_session: event_id is required'); console.error('create_ae_obj__event_session: event_id is required');
return null; return null;
} }
const result = await api.create_nested_obj({ const result = await api.create_nested_obj({
api_cfg, api_cfg,
for_obj_type: 'event', for_obj_type: 'event',
for_obj_id: event_id, for_obj_id: event_id,
obj_type: 'event_session', obj_type: 'event_session',
fields: { ...data_kv }, fields: { ...data_kv },
log_lvl log_lvl
}); });
if (result) { 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({
const processed_obj = processed[0]; obj_li: [result],
if (try_cache) { log_lvl
await db_save_ae_obj_li__ae_obj({ });
db_instance: db_events, const processed_obj = processed[0];
table_name: 'session', if (try_cache) {
obj_li: [processed_obj], await db_save_ae_obj_li__ae_obj({
properties_to_save, db_instance: db_events,
log_lvl table_name: 'session',
}); obj_li: [processed_obj],
} properties_to_save,
return processed_obj; log_lvl
} });
return null; }
return processed_obj;
}
return null;
} }
export async function delete_ae_obj_id__event_session({ export async function delete_ae_obj_id__event_session({
api_cfg, api_cfg,
event_id, event_id,
event_session_id, event_session_id,
method = 'delete', method = 'delete',
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
event_id?: string; event_id?: string;
event_session_id: string; event_session_id: string;
method?: 'delete' | 'soft_delete' | 'disable' | 'hide'; method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}) { }) {
if (!event_id) event_id = get(slct).event_id; if (!event_id) event_id = get(slct).event_id;
if (!event_id) { if (!event_id) {
console.error('delete_ae_obj_id__event_session: event_id is required'); console.error('delete_ae_obj_id__event_session: event_id is required');
return null; return null;
} }
const result = await api.delete_nested_ae_obj({ const result = await api.delete_nested_ae_obj({
api_cfg, api_cfg,
for_obj_type: 'event', for_obj_type: 'event',
for_obj_id: event_id, for_obj_id: event_id,
obj_type: 'event_session', obj_type: 'event_session',
obj_id: event_session_id, obj_id: event_session_id,
method, method,
log_lvl log_lvl
}); });
if (try_cache) await db_events.session.delete(event_session_id); if (try_cache) await db_events.session.delete(event_session_id);
return result; return result;
} }
export async function update_ae_obj__event_session({ export async function update_ae_obj__event_session({
api_cfg, api_cfg,
event_id, event_id,
event_session_id, event_session_id,
data_kv, data_kv,
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
event_id?: string; event_id?: string;
event_session_id: string; event_session_id: string;
data_kv: key_val; data_kv: key_val;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventSession | null> { }): Promise<ae_EventSession | null> {
if (!event_id) event_id = get(slct).event_id; if (!event_id) event_id = get(slct).event_id;
if (!event_id) { if (!event_id) {
console.error('update_ae_obj__event_session: event_id is required'); console.error('update_ae_obj__event_session: event_id is required');
return null; return null;
} }
const result = await api.update_nested_obj({ const result = await api.update_nested_obj({
api_cfg, api_cfg,
for_obj_type: 'event', for_obj_type: 'event',
for_obj_id: event_id, for_obj_id: event_id,
obj_type: 'event_session', obj_type: 'event_session',
obj_id: event_session_id, obj_id: event_session_id,
fields: data_kv, fields: data_kv,
log_lvl log_lvl
}); });
if (result) { 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({
const processed_obj = processed[0]; obj_li: [result],
if (try_cache) { log_lvl
await db_save_ae_obj_li__ae_obj({ });
db_instance: db_events, const processed_obj = processed[0];
table_name: 'session', if (try_cache) {
obj_li: [processed_obj], await db_save_ae_obj_li__ae_obj({
properties_to_save, db_instance: db_events,
log_lvl table_name: 'session',
}); obj_li: [processed_obj],
} properties_to_save,
return processed_obj; log_lvl
} });
return null; }
return processed_obj;
}
return null;
} }
export async function search__event_session({ 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[]> { }): 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) { if (fulltext_search_qry_str || ft_presenter_search_qry_str) {
const ft: any = {}; const ft: any = {};
if (fulltext_search_qry_str && fulltext_search_qry_str.length > 2) ft['default_qry_str'] = fulltext_search_qry_str; if (fulltext_search_qry_str && fulltext_search_qry_str.length > 2)
if (ft_presenter_search_qry_str && ft_presenter_search_qry_str.length > 2) ft['event_presenter_li_qry_str'] = ft_presenter_search_qry_str; 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 (Object.keys(ft).length) search_query.params = { ft_qry: ft };
} }
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 1 }); if (enabled === 'enabled')
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 0 }); search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 1 }); else if (enabled === 'not_enabled')
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 0 }); 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) { 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({ 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 // Handle V3 API envelope
let valid_result_li: ae_EventSession[] = []; let valid_result_li: ae_EventSession[] = [];
if (Array.isArray(result_li)) { if (Array.isArray(result_li)) {
valid_result_li = 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; valid_result_li = (result_li as any).data;
} }
if (valid_result_li && valid_result_li.length > 0) { 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) { 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; return processed;
} }
@@ -429,18 +754,103 @@ export async function search__event_session({
export const qry__event_session = 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 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>`; 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 = [ 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 []; if (!obj_li || obj_li.length === 0) return [];
const processed_obj_li: T[] = []; const processed_obj_li: T[] = [];
for (const original_obj of obj_li) { 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]) { if (processed_obj[randomIdKey]) {
(processed_obj as any).id = processed_obj[randomIdKey]; (processed_obj as any).id = processed_obj[randomIdKey];
(processed_obj as any)[baseIdKey] = processed_obj[randomIdKey]; (processed_obj as any)[baseIdKey] = processed_obj[randomIdKey];
} } else if (processed_obj[baseIdKey])
else if (processed_obj[baseIdKey]) (processed_obj as any).id = processed_obj[baseIdKey]; (processed_obj as any).id = processed_obj[baseIdKey];
const group = processed_obj.group ?? '0'; const group = processed_obj.group ?? '0';
const priority = processed_obj.priority ? 1 : 0; const priority = processed_obj.priority ? 1 : 0;
const sort = processed_obj.sort ?? '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 ?? ''; const name = processed_obj.name ?? '';
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`; (processed_obj as any).tmp_sort_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
if (specific_processor) processed_obj = await Promise.resolve(specific_processor(processed_obj)); (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); processed_obj_li.push(processed_obj as T);
} }
return processed_obj_li; return processed_obj_li;
} }
export async function process_ae_obj__event_session_props({ obj_li, log_lvl = 0 }: { obj_li: any[]; log_lvl?: number; }) { export async function process_ae_obj__event_session_props({
return _process_generic_props({ obj_li, obj_type: 'event_session', log_lvl }); obj_li,
log_lvl = 0
}: {
obj_li: any[];
log_lvl?: number;
}) {
return _process_generic_props({
obj_li,
obj_type: 'event_session',
log_lvl
});
} }

View File

@@ -92,11 +92,15 @@ async function _process_generic_props<T extends Record<string, any>>({
(processed_obj as any).description = ''; (processed_obj as any).description = '';
} }
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`; (processed_obj as any).tmp_sort_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
(processed_obj as any).tmp_sort_2 =
`${group}_${priority}_${sort}_${name}_${updated}`;
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);
@@ -137,7 +141,9 @@ export async function load_ae_obj_id__event_exhibit({
}): Promise<ae_EventExhibit | null> { }): Promise<ae_EventExhibit | null> {
const start_time = performance.now(); const start_time = performance.now();
if (log_lvl) { if (log_lvl) {
console.log(`🔎 [Trace] load_ae_obj_id__event_exhibit: START (id=${exhibit_id}, try_cache=${try_cache})`); console.log(
`🔎 [Trace] load_ae_obj_id__event_exhibit: START (id=${exhibit_id}, try_cache=${try_cache})`
);
} }
// 1. FAST PATH: Return cached data immediately // 1. FAST PATH: Return cached data immediately
@@ -146,34 +152,74 @@ export async function load_ae_obj_id__event_exhibit({
const cached = await db_events.exhibit.get(exhibit_id); const cached = await db_events.exhibit.get(exhibit_id);
if (cached) { if (cached) {
const elapsed = (performance.now() - start_time).toFixed(2); 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 exhibit shell.`); if (log_lvl)
console.log(
`✅ [Trace] load_ae_obj_id: CACHE HIT at ${elapsed}ms. Returning stale exhibit shell.`
);
// Background refresh (non-blocking) // Background refresh (non-blocking)
_refresh_exhibit_id_background({ api_cfg, exhibit_id, view, try_cache, log_lvl: log_lvl > 1 ? log_lvl : 0 }); _refresh_exhibit_id_background({
api_cfg,
exhibit_id,
view,
try_cache,
log_lvl: log_lvl > 1 ? log_lvl : 0
});
return cached; return cached;
} }
} catch (e) { } 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 // 2. SLOW PATH: Wait for API
return await _refresh_exhibit_id_background({ api_cfg, exhibit_id, view, try_cache, log_lvl }); return await _refresh_exhibit_id_background({
api_cfg,
exhibit_id,
view,
try_cache,
log_lvl
});
} }
async function _refresh_exhibit_id_background({ api_cfg, exhibit_id, view, try_cache, log_lvl }: any) { async function _refresh_exhibit_id_background({
api_cfg,
exhibit_id,
view,
try_cache,
log_lvl
}: any) {
const start_time = performance.now(); const start_time = performance.now();
if (typeof navigator !== 'undefined' && !navigator.onLine) return null; if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
try { try {
if (log_lvl) console.log(`📡 [Trace] _refresh_exhibit_id: API Fetching id=${exhibit_id}`); if (log_lvl)
const result = await api.get_ae_obj({ api_cfg, obj_type: 'event_exhibit', obj_id: exhibit_id, view, log_lvl }); console.log(
`📡 [Trace] _refresh_exhibit_id: API Fetching id=${exhibit_id}`
);
const result = await api.get_ae_obj({
api_cfg,
obj_type: 'event_exhibit',
obj_id: exhibit_id,
view,
log_lvl
});
if (result) { if (result) {
const processed = await process_ae_obj__exhibit_props({ obj_li: [result], log_lvl }); const processed = await process_ae_obj__exhibit_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0]; const processed_obj = processed[0];
const elapsed = (performance.now() - start_time).toFixed(2); const elapsed = (performance.now() - start_time).toFixed(2);
if (log_lvl) console.log(`📦 [Trace] _refresh_exhibit_id: Received from API at ${elapsed}ms`); if (log_lvl)
console.log(
`📦 [Trace] _refresh_exhibit_id: Received from API at ${elapsed}ms`
);
if (try_cache) { if (try_cache) {
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
@@ -187,7 +233,11 @@ async function _refresh_exhibit_id_background({ api_cfg, exhibit_id, view, try_c
return processed_obj; return processed_obj;
} }
} catch (e) { } catch (e) {
if (log_lvl) console.error(`❌ [Trace] _refresh_exhibit_id: API error for id=${exhibit_id}:`, e); if (log_lvl)
console.error(
`❌ [Trace] _refresh_exhibit_id: API error for id=${exhibit_id}:`,
e
);
} }
return null; return null;
} }
@@ -225,36 +275,82 @@ export async function load_ae_obj_li__event_exhibit({
}): Promise<ae_EventExhibit[]> { }): Promise<ae_EventExhibit[]> {
const start_time = performance.now(); const start_time = performance.now();
if (log_lvl) { if (log_lvl) {
console.log(`🔎 [Trace] load_ae_obj_li__event_exhibit: START (event=${event_id}, try_cache=${try_cache})`); console.log(
`🔎 [Trace] load_ae_obj_li__event_exhibit: START (event=${event_id}, try_cache=${try_cache})`
);
} }
if (try_cache) { if (try_cache) {
try { try {
const cached_li = await db_events.exhibit const cached_li = await db_events.exhibit
.where('event_id').equals(event_id) .where('event_id')
.equals(event_id)
.toArray(); .toArray();
if (cached_li && cached_li.length > 0) { if (cached_li && cached_li.length > 0) {
const elapsed = (performance.now() - start_time).toFixed(2); 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) // Background refresh (non-blocking)
_refresh_exhibit_li_background({ api_cfg, event_id, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl: log_lvl > 1 ? log_lvl : 0 }); _refresh_exhibit_li_background({
api_cfg,
event_id,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl: log_lvl > 1 ? log_lvl : 0
});
return cached_li; return cached_li;
} }
} catch (e) { } 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_exhibit_li_background({ api_cfg, event_id, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl }); return await _refresh_exhibit_li_background({
api_cfg,
event_id,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl
});
} }
async function _refresh_exhibit_li_background({ api_cfg, event_id, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl }: any) { async function _refresh_exhibit_li_background({
api_cfg,
event_id,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl
}: any) {
const start_time = performance.now(); const start_time = performance.now();
if (typeof navigator !== 'undefined' && !navigator.onLine) return []; if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
try { try {
if (log_lvl) console.log(`📡 [Trace] _refresh_exhibit_li: API Fetching exhibits for event=${event_id}`); if (log_lvl)
console.log(
`📡 [Trace] _refresh_exhibit_li: API Fetching exhibits for event=${event_id}`
);
const result_li = await api.get_ae_obj_li({ const result_li = await api.get_ae_obj_li({
api_cfg, api_cfg,
obj_type: 'event_exhibit', obj_type: 'event_exhibit',
@@ -270,9 +366,15 @@ async function _refresh_exhibit_li_background({ api_cfg, event_id, enabled, hidd
}); });
if (result_li) { if (result_li) {
const processed = await process_ae_obj__exhibit_props({ obj_li: result_li, log_lvl }); const processed = await process_ae_obj__exhibit_props({
obj_li: result_li,
log_lvl
});
const elapsed = (performance.now() - start_time).toFixed(2); const elapsed = (performance.now() - start_time).toFixed(2);
if (log_lvl) console.log(`📦 [Trace] _refresh_exhibit_li: Received ${processed.length} items from API at ${elapsed}ms.`); if (log_lvl)
console.log(
`📦 [Trace] _refresh_exhibit_li: Received ${processed.length} items from API at ${elapsed}ms.`
);
if (try_cache) { if (try_cache) {
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
@@ -286,7 +388,8 @@ async function _refresh_exhibit_li_background({ api_cfg, event_id, enabled, hidd
return processed; return processed;
} }
} catch (e) { } catch (e) {
if (log_lvl) console.error(`❌ [Trace] _refresh_exhibit_li: API error:`, e); if (log_lvl)
console.error(`❌ [Trace] _refresh_exhibit_li: API error:`, e);
} }
return []; return [];
} }
@@ -295,87 +398,93 @@ async function _refresh_exhibit_li_background({ api_cfg, event_id, enabled, hidd
* Exhibit Create (V3) * Exhibit Create (V3)
*/ */
export async function create_ae_obj__exhibit({ export async function create_ae_obj__exhibit({
api_cfg, api_cfg,
event_id, event_id,
data_kv, data_kv,
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
event_id: string; event_id: string;
data_kv: key_val; data_kv: key_val;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventExhibit | null> { }): Promise<ae_EventExhibit | null> {
const result = await api.create_nested_obj({ const result = await api.create_nested_obj({
api_cfg, api_cfg,
parent_type: 'event', parent_type: 'event',
parent_id: event_id, parent_id: event_id,
child_type: 'event_exhibit', child_type: 'event_exhibit',
fields: { ...data_kv }, fields: { ...data_kv },
log_lvl log_lvl
}); });
if (result) { if (result) {
const processed = await process_ae_obj__exhibit_props({ obj_li: [result], log_lvl }); const processed = await process_ae_obj__exhibit_props({
const processed_obj = processed[0]; obj_li: [result],
if (try_cache) { log_lvl
await db_save_ae_obj_li__ae_obj({ });
db_instance: db_events, const processed_obj = processed[0];
table_name: 'exhibit', if (try_cache) {
obj_li: [processed_obj], await db_save_ae_obj_li__ae_obj({
properties_to_save: properties_to_save_exhibit, db_instance: db_events,
log_lvl table_name: 'exhibit',
}); obj_li: [processed_obj],
} properties_to_save: properties_to_save_exhibit,
return processed_obj; log_lvl
} });
return null; }
return processed_obj;
}
return null;
} }
/** /**
* Exhibit Update (V3) * Exhibit Update (V3)
*/ */
export async function update_ae_obj__exhibit({ export async function update_ae_obj__exhibit({
api_cfg, api_cfg,
event_id, event_id,
exhibit_id, exhibit_id,
data_kv, data_kv,
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
event_id: string; event_id: string;
exhibit_id: string; exhibit_id: string;
data_kv: key_val; data_kv: key_val;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventExhibit | null> { }): Promise<ae_EventExhibit | null> {
const result = await api.update_nested_obj({ const result = await api.update_nested_obj({
api_cfg, api_cfg,
parent_type: 'event', parent_type: 'event',
parent_id: event_id, parent_id: event_id,
child_type: 'event_exhibit', child_type: 'event_exhibit',
child_id: exhibit_id, child_id: exhibit_id,
fields: data_kv, fields: data_kv,
log_lvl log_lvl
}); });
if (result) { if (result) {
const processed = await process_ae_obj__exhibit_props({ obj_li: [result], log_lvl }); const processed = await process_ae_obj__exhibit_props({
const processed_obj = processed[0]; obj_li: [result],
if (try_cache) { log_lvl
await db_save_ae_obj_li__ae_obj({ });
db_instance: db_events, const processed_obj = processed[0];
table_name: 'exhibit', if (try_cache) {
obj_li: [processed_obj], await db_save_ae_obj_li__ae_obj({
properties_to_save: properties_to_save_exhibit, db_instance: db_events,
log_lvl table_name: 'exhibit',
}); obj_li: [processed_obj],
} properties_to_save: properties_to_save_exhibit,
return processed_obj; log_lvl
} });
return null; }
return processed_obj;
}
return null;
} }
/** /**
@@ -409,24 +518,30 @@ export async function search__exhibit({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventExhibit[]> { }): Promise<ae_EventExhibit[]> {
if (log_lvl) { if (log_lvl) {
console.log(`*** search__exhibit() *** event_id=${event_id} ft=${fulltext_search_qry_str}`); console.log(
`*** search__exhibit() *** event_id=${event_id} ft=${fulltext_search_qry_str}`
);
} }
const search_query: any = { const search_query: any = {
q: fulltext_search_qry_str || '', q: fulltext_search_qry_str || '',
and: [ and: [{ field: 'event_id', op: 'eq', value: event_id }]
{ field: 'event_id', op: 'eq', value: event_id }
]
}; };
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 1 }); if (enabled === 'enabled')
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 0 }); 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 }); if (hidden === 'hidden')
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 0 }); 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 (priority === 'priority') search_query.and.push({ field: 'priority', op: 'eq', value: 1 }); if (priority === 'priority')
else if (priority === 'not_priority') search_query.and.push({ field: 'priority', op: 'eq', value: 0 }); search_query.and.push({ field: 'priority', op: 'eq', value: 1 });
else if (priority === 'not_priority')
search_query.and.push({ field: 'priority', op: 'eq', value: 0 });
try { try {
const result_li = await api.search_ae_obj({ const result_li = await api.search_ae_obj({
@@ -443,7 +558,10 @@ export async function search__exhibit({
}); });
if (result_li && Array.isArray(result_li)) { if (result_li && Array.isArray(result_li)) {
const processed = await process_ae_obj__exhibit_props({ obj_li: result_li, log_lvl }); const processed = await process_ae_obj__exhibit_props({
obj_li: result_li,
log_lvl
});
if (try_cache) { if (try_cache) {
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_events, db_instance: db_events,

View File

@@ -106,11 +106,15 @@ async function _process_generic_props<T extends Record<string, any>>({
(processed_obj as any).exhibitor_notes = ''; (processed_obj as any).exhibitor_notes = '';
} }
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`; (processed_obj as any).tmp_sort_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
(processed_obj as any).tmp_sort_2 =
`${group}_${priority}_${sort}_${name}_${updated}`;
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);
@@ -151,43 +155,86 @@ export async function load_ae_obj_id__event_exhibit_tracking({
}): Promise<ae_EventExhibitTracking | null> { }): Promise<ae_EventExhibitTracking | null> {
const start_time = performance.now(); const start_time = performance.now();
if (log_lvl) { if (log_lvl) {
console.log(`🔎 [Trace] load_ae_obj_id__event_exhibit_tracking: START (id=${exhibit_tracking_id}, try_cache=${try_cache})`); console.log(
`🔎 [Trace] load_ae_obj_id__event_exhibit_tracking: START (id=${exhibit_tracking_id}, try_cache=${try_cache})`
);
} }
// 1. FAST PATH: Return cached data immediately // 1. FAST PATH: Return cached data immediately
if (try_cache) { if (try_cache) {
try { try {
const cached = await db_events.exhibit_tracking.get(exhibit_tracking_id); const cached =
await db_events.exhibit_tracking.get(exhibit_tracking_id);
if (cached) { if (cached) {
const elapsed = (performance.now() - start_time).toFixed(2); 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 lead shell.`); if (log_lvl)
console.log(
`✅ [Trace] load_ae_obj_id: CACHE HIT at ${elapsed}ms. Returning stale lead shell.`
);
// Background refresh (non-blocking) // Background refresh (non-blocking)
_refresh_tracking_id_background({ api_cfg, exhibit_tracking_id, view, try_cache, log_lvl: log_lvl > 1 ? log_lvl : 0 }); _refresh_tracking_id_background({
api_cfg,
exhibit_tracking_id,
view,
try_cache,
log_lvl: log_lvl > 1 ? log_lvl : 0
});
return cached; return cached;
} }
} catch (e) { } 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 // 2. SLOW PATH: Wait for API
return await _refresh_tracking_id_background({ api_cfg, exhibit_tracking_id, view, try_cache, log_lvl }); return await _refresh_tracking_id_background({
api_cfg,
exhibit_tracking_id,
view,
try_cache,
log_lvl
});
} }
async function _refresh_tracking_id_background({ api_cfg, exhibit_tracking_id, view, try_cache, log_lvl }: any) { async function _refresh_tracking_id_background({
api_cfg,
exhibit_tracking_id,
view,
try_cache,
log_lvl
}: any) {
const start_time = performance.now(); const start_time = performance.now();
if (typeof navigator !== 'undefined' && !navigator.onLine) return null; if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
try { try {
if (log_lvl) console.log(`📡 [Trace] _refresh_tracking_id: API Fetching id=${exhibit_tracking_id}`); if (log_lvl)
const result = await api.get_ae_obj({ api_cfg, obj_type: 'event_exhibit_tracking', obj_id: exhibit_tracking_id, view, log_lvl }); console.log(
`📡 [Trace] _refresh_tracking_id: API Fetching id=${exhibit_tracking_id}`
);
const result = await api.get_ae_obj({
api_cfg,
obj_type: 'event_exhibit_tracking',
obj_id: exhibit_tracking_id,
view,
log_lvl
});
if (result) { if (result) {
const processed = await process_ae_obj__exhibit_tracking_props({ obj_li: [result], log_lvl }); const processed = await process_ae_obj__exhibit_tracking_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0]; const processed_obj = processed[0];
const elapsed = (performance.now() - start_time).toFixed(2); const elapsed = (performance.now() - start_time).toFixed(2);
if (log_lvl) console.log(`📦 [Trace] _refresh_tracking_id: Received from API at ${elapsed}ms`); if (log_lvl)
console.log(
`📦 [Trace] _refresh_tracking_id: Received from API at ${elapsed}ms`
);
if (try_cache) { if (try_cache) {
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
@@ -201,7 +248,11 @@ async function _refresh_tracking_id_background({ api_cfg, exhibit_tracking_id, v
return processed_obj; return processed_obj;
} }
} catch (e) { } catch (e) {
if (log_lvl) console.error(`❌ [Trace] _refresh_tracking_id: API error for id=${exhibit_tracking_id}:`, e); if (log_lvl)
console.error(
`❌ [Trace] _refresh_tracking_id: API error for id=${exhibit_tracking_id}:`,
e
);
} }
return null; return null;
} }
@@ -238,36 +289,82 @@ export async function load_ae_obj_li__event_exhibit_tracking({
}): Promise<ae_EventExhibitTracking[]> { }): Promise<ae_EventExhibitTracking[]> {
const start_time = performance.now(); const start_time = performance.now();
if (log_lvl) { if (log_lvl) {
console.log(`🔎 [Trace] load_ae_obj_li__event_exhibit_tracking: START (exhibit=${exhibit_id}, try_cache=${try_cache})`); console.log(
`🔎 [Trace] load_ae_obj_li__event_exhibit_tracking: START (exhibit=${exhibit_id}, try_cache=${try_cache})`
);
} }
if (try_cache) { if (try_cache) {
try { try {
const cached_li = await db_events.exhibit_tracking const cached_li = await db_events.exhibit_tracking
.where('event_exhibit_id').equals(exhibit_id) .where('event_exhibit_id')
.equals(exhibit_id)
.toArray(); .toArray();
if (cached_li && cached_li.length > 0) { if (cached_li && cached_li.length > 0) {
const elapsed = (performance.now() - start_time).toFixed(2); 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) // Background refresh (non-blocking)
_refresh_tracking_li_background({ api_cfg, exhibit_id, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl: log_lvl > 1 ? log_lvl : 0 }); _refresh_tracking_li_background({
api_cfg,
exhibit_id,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl: log_lvl > 1 ? log_lvl : 0
});
return cached_li; return cached_li;
} }
} catch (e) { } 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_tracking_li_background({ api_cfg, exhibit_id, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl }); return await _refresh_tracking_li_background({
api_cfg,
exhibit_id,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl
});
} }
async function _refresh_tracking_li_background({ api_cfg, exhibit_id, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl }: any) { async function _refresh_tracking_li_background({
api_cfg,
exhibit_id,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl
}: any) {
const start_time = performance.now(); const start_time = performance.now();
if (typeof navigator !== 'undefined' && !navigator.onLine) return []; if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
try { try {
if (log_lvl) console.log(`📡 [Trace] _refresh_tracking_li: API Fetching leads for exhibit=${exhibit_id}`); if (log_lvl)
console.log(
`📡 [Trace] _refresh_tracking_li: API Fetching leads for exhibit=${exhibit_id}`
);
const result_li = await api.get_ae_obj_li({ const result_li = await api.get_ae_obj_li({
api_cfg, api_cfg,
obj_type: 'event_exhibit_tracking', obj_type: 'event_exhibit_tracking',
@@ -283,9 +380,15 @@ async function _refresh_tracking_li_background({ api_cfg, exhibit_id, enabled, h
}); });
if (result_li) { if (result_li) {
const processed = await process_ae_obj__exhibit_tracking_props({ obj_li: result_li, log_lvl }); const processed = await process_ae_obj__exhibit_tracking_props({
obj_li: result_li,
log_lvl
});
const elapsed = (performance.now() - start_time).toFixed(2); const elapsed = (performance.now() - start_time).toFixed(2);
if (log_lvl) console.log(`📦 [Trace] _refresh_tracking_li: Received ${processed.length} items from API at ${elapsed}ms.`); if (log_lvl)
console.log(
`📦 [Trace] _refresh_tracking_li: Received ${processed.length} items from API at ${elapsed}ms.`
);
if (try_cache) { if (try_cache) {
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
@@ -299,7 +402,8 @@ async function _refresh_tracking_li_background({ api_cfg, exhibit_id, enabled, h
return processed; return processed;
} }
} catch (e) { } catch (e) {
if (log_lvl) console.error(`❌ [Trace] _refresh_tracking_li: API error:`, e); if (log_lvl)
console.error(`❌ [Trace] _refresh_tracking_li: API error:`, e);
} }
return []; return [];
} }
@@ -308,95 +412,101 @@ async function _refresh_tracking_li_background({ api_cfg, exhibit_id, enabled, h
* Lead Capture (V3) * Lead Capture (V3)
*/ */
export async function create_ae_obj__exhibit_tracking({ export async function create_ae_obj__exhibit_tracking({
api_cfg, api_cfg,
exhibit_id, exhibit_id,
event_badge_id, event_badge_id,
external_person_id, external_person_id,
group, group,
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
exhibit_id: string; exhibit_id: string;
event_badge_id: string; event_badge_id: string;
external_person_id: string; external_person_id: string;
group?: string; group?: string;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventExhibitTracking | null> { }): Promise<ae_EventExhibitTracking | null> {
const result = await api.create_nested_obj({ const result = await api.create_nested_obj({
api_cfg, api_cfg,
parent_type: 'event_exhibit', parent_type: 'event_exhibit',
parent_id: exhibit_id, parent_id: exhibit_id,
child_type: 'event_exhibit_tracking', child_type: 'event_exhibit_tracking',
fields: { fields: {
event_badge_id: event_badge_id, event_badge_id: event_badge_id,
external_person_id, external_person_id,
group group
}, },
log_lvl log_lvl
}); });
if (result) { if (result) {
const processed = await process_ae_obj__exhibit_tracking_props({ obj_li: [result], log_lvl }); const processed = await process_ae_obj__exhibit_tracking_props({
const processed_obj = processed[0]; obj_li: [result],
if (try_cache) { log_lvl
await db_save_ae_obj_li__ae_obj({ });
db_instance: db_events, const processed_obj = processed[0];
table_name: 'exhibit_tracking', if (try_cache) {
obj_li: [processed_obj], await db_save_ae_obj_li__ae_obj({
properties_to_save: properties_to_save_exhibit_tracking, db_instance: db_events,
log_lvl table_name: 'exhibit_tracking',
}); obj_li: [processed_obj],
} properties_to_save: properties_to_save_exhibit_tracking,
return processed_obj; log_lvl
} });
return null; }
return processed_obj;
}
return null;
} }
/** /**
* Lead Update (V3) * Lead Update (V3)
*/ */
export async function update_ae_obj__exhibit_tracking({ export async function update_ae_obj__exhibit_tracking({
api_cfg, api_cfg,
exhibit_id, exhibit_id,
exhibit_tracking_id, exhibit_tracking_id,
data, data,
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
exhibit_id: string; exhibit_id: string;
exhibit_tracking_id: string; exhibit_tracking_id: string;
data: any; data: any;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventExhibitTracking | null> { }): Promise<ae_EventExhibitTracking | null> {
const result = await api.update_nested_obj({ const result = await api.update_nested_obj({
api_cfg, api_cfg,
parent_type: 'event_exhibit', parent_type: 'event_exhibit',
parent_id: exhibit_id, parent_id: exhibit_id,
child_type: 'event_exhibit_tracking', child_type: 'event_exhibit_tracking',
child_id: exhibit_tracking_id, child_id: exhibit_tracking_id,
fields: data, fields: data,
log_lvl log_lvl
}); });
if (result) { if (result) {
const processed = await process_ae_obj__exhibit_tracking_props({ obj_li: [result], log_lvl }); const processed = await process_ae_obj__exhibit_tracking_props({
const processed_obj = processed[0]; obj_li: [result],
if (try_cache) { log_lvl
await db_save_ae_obj_li__ae_obj({ });
db_instance: db_events, const processed_obj = processed[0];
table_name: 'exhibit_tracking', if (try_cache) {
obj_li: [processed_obj], await db_save_ae_obj_li__ae_obj({
properties_to_save: properties_to_save_exhibit_tracking, db_instance: db_events,
log_lvl table_name: 'exhibit_tracking',
}); obj_li: [processed_obj],
} properties_to_save: properties_to_save_exhibit_tracking,
return processed_obj; log_lvl
} });
return null; }
return processed_obj;
}
return null;
} }
/** /**
@@ -428,13 +538,15 @@ export async function download_export__event_exhibit_tracking({
log_lvl?: number; log_lvl?: number;
}) { }) {
if (log_lvl) { if (log_lvl) {
console.log(`*** download_export__event_exhibit_tracking() *** exhibit_id=${exhibit_id}`); console.log(
`*** download_export__event_exhibit_tracking() *** exhibit_id=${exhibit_id}`
);
} }
const endpoint = `/v3/action/event_exhibit/${exhibit_id}/tracking_export`; const endpoint = `/v3/action/event_exhibit/${exhibit_id}/tracking_export`;
const params: key_val = { const params: key_val = {
file_type, file_type,
return_file: 'true' // V3 convention: string 'true' for bool query flags return_file: 'true' // V3 convention: string 'true' for bool query flags
}; };
return await api.get_object({ return await api.get_object({
@@ -485,7 +597,9 @@ export async function search__exhibit_tracking({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_EventExhibitTracking[]> { }): Promise<ae_EventExhibitTracking[]> {
if (log_lvl) { if (log_lvl) {
console.log(`*** search__exhibit_tracking() *** exhibit_id=${event_exhibit_id} ft=${fulltext_search_qry_str}`); console.log(
`*** search__exhibit_tracking() *** exhibit_id=${event_exhibit_id} ft=${fulltext_search_qry_str}`
);
} }
if (!event_id || !event_exhibit_id) return []; if (!event_id || !event_exhibit_id) return [];
@@ -498,15 +612,30 @@ export async function search__exhibit_tracking({
] ]
}; };
if (qry_group) search_query.and.push({ field: 'group', op: 'eq', value: qry_group }); if (qry_group)
if (qry_external_person_id) search_query.and.push({ field: 'external_person_id', op: 'eq', value: qry_external_person_id }); search_query.and.push({ field: 'group', op: 'eq', value: qry_group });
if (qry_badge_id) search_query.and.push({ field: 'event_badge_id', op: 'eq', value: qry_badge_id }); if (qry_external_person_id)
search_query.and.push({
field: 'external_person_id',
op: 'eq',
value: qry_external_person_id
});
if (qry_badge_id)
search_query.and.push({
field: 'event_badge_id',
op: 'eq',
value: qry_badge_id
});
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 1 }); if (enabled === 'enabled')
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 0 }); 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 }); if (hidden === 'hidden')
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 0 }); 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 });
try { try {
const result_li = await api.search_ae_obj({ const result_li = await api.search_ae_obj({
@@ -525,7 +654,10 @@ export async function search__exhibit_tracking({
}); });
if (result_li && Array.isArray(result_li)) { if (result_li && Array.isArray(result_li)) {
const processed = await process_ae_obj__exhibit_tracking_props({ obj_li: result_li, log_lvl }); const processed = await process_ae_obj__exhibit_tracking_props({
obj_li: result_li,
log_lvl
});
if (try_cache) { if (try_cache) {
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_events, db_instance: db_events,

View File

@@ -56,13 +56,18 @@ const export_obj = {
search__event_badge: event_badge.search__event_badge, search__event_badge: event_badge.search__event_badge,
// Event Badge Templates // Event Badge Templates
load_ae_obj_id__event_badge_template: event_badge_template.load_ae_obj_id__event_badge_template, load_ae_obj_id__event_badge_template:
load_ae_obj_li__event_badge_template: event_badge_template.load_ae_obj_li__event_badge_template, event_badge_template.load_ae_obj_id__event_badge_template,
create_ae_obj__event_badge_template: event_badge_template.create_ae_obj__event_badge_template, load_ae_obj_li__event_badge_template:
event_badge_template.load_ae_obj_li__event_badge_template,
create_ae_obj__event_badge_template:
event_badge_template.create_ae_obj__event_badge_template,
delete_ae_obj_id__event_badge_template: delete_ae_obj_id__event_badge_template:
event_badge_template.delete_ae_obj_id__event_badge_template, event_badge_template.delete_ae_obj_id__event_badge_template,
update_ae_obj__event_badge_template: event_badge_template.update_ae_obj__event_badge_template, update_ae_obj__event_badge_template:
search__event_badge_template: event_badge_template.search__event_badge_template, event_badge_template.update_ae_obj__event_badge_template,
search__event_badge_template:
event_badge_template.search__event_badge_template,
// Event Devices // Event Devices
load_ae_obj_id__event_device: event_device.load_ae_obj_id__event_device, load_ae_obj_id__event_device: event_device.load_ae_obj_id__event_device,
@@ -77,53 +82,72 @@ const export_obj = {
search__exhibit: search__exhibit, search__exhibit: search__exhibit,
create_ae_obj__exhibit: create_ae_obj__exhibit, create_ae_obj__exhibit: create_ae_obj__exhibit,
update_ae_obj__exhibit: update_ae_obj__exhibit, update_ae_obj__exhibit: update_ae_obj__exhibit,
load_ae_obj_id__event_exhibit_tracking: load_ae_obj_id__event_exhibit_tracking, load_ae_obj_id__event_exhibit_tracking:
load_ae_obj_li__event_exhibit_tracking: load_ae_obj_li__event_exhibit_tracking, load_ae_obj_id__event_exhibit_tracking,
load_ae_obj_li__event_exhibit_tracking:
load_ae_obj_li__event_exhibit_tracking,
search__exhibit_tracking: search__exhibit_tracking, search__exhibit_tracking: search__exhibit_tracking,
create_ae_obj__exhibit_tracking: create_ae_obj__exhibit_tracking, create_ae_obj__exhibit_tracking: create_ae_obj__exhibit_tracking,
update_ae_obj__exhibit_tracking: update_ae_obj__exhibit_tracking, update_ae_obj__exhibit_tracking: update_ae_obj__exhibit_tracking,
download_export__event_exhibit_tracking: download_export__event_exhibit_tracking, download_export__event_exhibit_tracking:
download_export__event_exhibit_tracking,
// Event Files // Event Files
load_ae_obj_id__event_file: event_file.load_ae_obj_id__event_file, load_ae_obj_id__event_file: event_file.load_ae_obj_id__event_file,
load_ae_obj_li__event_file: event_file.load_ae_obj_li__event_file, load_ae_obj_li__event_file: event_file.load_ae_obj_li__event_file,
create_event_file_obj_from_hosted_file_async: event_file.create_event_file_obj_from_hosted_file_async, create_event_file_obj_from_hosted_file_async:
event_file.create_event_file_obj_from_hosted_file_async,
delete_ae_obj_id__event_file: event_file.delete_ae_obj_id__event_file, delete_ae_obj_id__event_file: event_file.delete_ae_obj_id__event_file,
update_ae_obj__event_file: event_file.update_ae_obj__event_file, update_ae_obj__event_file: event_file.update_ae_obj__event_file,
qry__event_file: event_file.qry__event_file, qry__event_file: event_file.qry__event_file,
search__event_file: event_file.search__event_file, search__event_file: event_file.search__event_file,
// Event Locations // Event Locations
load_ae_obj_id__event_location: event_location.load_ae_obj_id__event_location, load_ae_obj_id__event_location:
load_ae_obj_li__event_location: event_location.load_ae_obj_li__event_location, event_location.load_ae_obj_id__event_location,
load_ae_obj_li__event_location:
event_location.load_ae_obj_li__event_location,
create_ae_obj__event_location: event_location.create_ae_obj__event_location, create_ae_obj__event_location: event_location.create_ae_obj__event_location,
delete_ae_obj_id__event_location: event_location.delete_ae_obj_id__event_location, delete_ae_obj_id__event_location:
event_location.delete_ae_obj_id__event_location,
update_ae_obj__event_location: event_location.update_ae_obj__event_location, update_ae_obj__event_location: event_location.update_ae_obj__event_location,
// Event Sessions // Event Sessions
load_ae_obj_id__event_session: event_session.load_ae_obj_id__event_session, load_ae_obj_id__event_session: event_session.load_ae_obj_id__event_session,
load_ae_obj_li__event_session: event_session.load_ae_obj_li__event_session, load_ae_obj_li__event_session: event_session.load_ae_obj_li__event_session,
create_ae_obj__event_session: event_session.create_ae_obj__event_session, create_ae_obj__event_session: event_session.create_ae_obj__event_session,
delete_ae_obj_id__event_session: event_session.delete_ae_obj_id__event_session, delete_ae_obj_id__event_session:
event_session.delete_ae_obj_id__event_session,
update_ae_obj__event_session: event_session.update_ae_obj__event_session, update_ae_obj__event_session: event_session.update_ae_obj__event_session,
qry__event_session: event_session.qry__event_session, qry__event_session: event_session.qry__event_session,
search__event_session: event_session.search__event_session, search__event_session: event_session.search__event_session,
email_sign_in__event_session: event_session.email_sign_in__event_session, email_sign_in__event_session: event_session.email_sign_in__event_session,
// Event Presentations // Event Presentations
load_ae_obj_id__event_presentation: event_presentation.load_ae_obj_id__event_presentation, load_ae_obj_id__event_presentation:
load_ae_obj_li__event_presentation: event_presentation.load_ae_obj_li__event_presentation, event_presentation.load_ae_obj_id__event_presentation,
create_ae_obj__event_presentation: event_presentation.create_ae_obj__event_presentation, load_ae_obj_li__event_presentation:
delete_ae_obj_id__event_presentation: event_presentation.delete_ae_obj_id__event_presentation, event_presentation.load_ae_obj_li__event_presentation,
update_ae_obj__event_presentation: event_presentation.update_ae_obj__event_presentation, create_ae_obj__event_presentation:
event_presentation.create_ae_obj__event_presentation,
delete_ae_obj_id__event_presentation:
event_presentation.delete_ae_obj_id__event_presentation,
update_ae_obj__event_presentation:
event_presentation.update_ae_obj__event_presentation,
// Event Presenters // Event Presenters
load_ae_obj_id__event_presenter: event_presenter.load_ae_obj_id__event_presenter, load_ae_obj_id__event_presenter:
load_ae_obj_li__event_presenter: event_presenter.load_ae_obj_li__event_presenter, event_presenter.load_ae_obj_id__event_presenter,
create_ae_obj__event_presenter: event_presenter.create_ae_obj__event_presenter, load_ae_obj_li__event_presenter:
delete_ae_obj_id__event_presenter: event_presenter.delete_ae_obj_id__event_presenter, event_presenter.load_ae_obj_li__event_presenter,
update_ae_obj__event_presenter: event_presenter.update_ae_obj__event_presenter, create_ae_obj__event_presenter:
event_presenter.create_ae_obj__event_presenter,
delete_ae_obj_id__event_presenter:
event_presenter.delete_ae_obj_id__event_presenter,
update_ae_obj__event_presenter:
event_presenter.update_ae_obj__event_presenter,
search__event_presenter: event_presenter.search__event_presenter, search__event_presenter: event_presenter.search__event_presenter,
email_sign_in__event_presenter: event_presenter.email_sign_in__event_presenter email_sign_in__event_presenter:
event_presenter.email_sign_in__event_presenter
}; };
export const events_func = export_obj; export const events_func = export_obj;

View File

@@ -18,7 +18,7 @@
/* --- Badge front --- */ /* --- Badge front --- */
[data-layout="badge_4x5_fanfold"] .badge_front { [data-layout='badge_4x5_fanfold'] .badge_front {
width: 4in; width: 4in;
min-height: 5in; min-height: 5in;
max-height: 5in; max-height: 5in;
@@ -28,18 +28,18 @@
} }
/* Body area: 5in total ~1in header ~0.5in footer = ~3.5in for content */ /* Body area: 5in total ~1in header ~0.5in footer = ~3.5in for content */
[data-layout="badge_4x5_fanfold"] .badge_body { [data-layout='badge_4x5_fanfold'] .badge_body {
max-height: 3.5in; max-height: 3.5in;
} }
/* --- Badge back --- */ /* --- Badge back --- */
[data-layout="badge_4x5_fanfold"] .badge_back { [data-layout='badge_4x5_fanfold'] .badge_back {
width: 4in; width: 4in;
min-height: 5in; min-height: 5in;
max-height: 5in; max-height: 5in;
} }
[data-layout="badge_4x5_fanfold"] .badge_back_content { [data-layout='badge_4x5_fanfold'] .badge_back_content {
max-height: 4.5in; max-height: 4.5in;
} }

View File

@@ -16,19 +16,19 @@
/* --- Badge front --- */ /* --- Badge front --- */
[data-layout="badge_3.5x5.5_pvc"] .badge_front { [data-layout='badge_3.5x5.5_pvc'] .badge_front {
width: 3.5in; width: 3.5in;
min-height: 5.5in; min-height: 5.5in;
max-height: 5.5in; max-height: 5.5in;
/* debug */ /* debug */
/* outline: thick solid orange; */ /* outline: thick solid orange; */
} }
/* Outer wrapper: remove the generic 4×6 fanfold defaults so the blue dashed /* Outer wrapper: remove the generic 4×6 fanfold defaults so the blue dashed
outline hugs the card tightly. The badge_front CSS above supplies the exact outline hugs the card tightly. The badge_front CSS above supplies the exact
3.5×5.5in size; wrapper just needs to fit it with no extra space. */ 3.5×5.5in size; wrapper just needs to fit it with no extra space. */
[data-layout="badge_3.5x5.5_pvc"].event_badge_wrapper { [data-layout='badge_3.5x5.5_pvc'].event_badge_wrapper {
padding: 0; padding: 0;
gap: 0; gap: 0;
min-height: 0; min-height: 0;
@@ -37,7 +37,7 @@
} }
@media print { @media print {
[data-layout="badge_3.5x5.5_pvc"].event_badge_wrapper { [data-layout='badge_3.5x5.5_pvc'].event_badge_wrapper {
width: 3.5in !important; width: 3.5in !important;
height: 5.5in !important; height: 5.5in !important;
max-width: 3.5in !important; max-width: 3.5in !important;

View File

@@ -829,7 +829,7 @@ export class MySubClassedDexie extends Dexie {
constructor() { constructor() {
super('ae_events_db'); super('ae_events_db');
this.version(6).stores({ this.version(6).stores({
// NO LONGER USE "_random" // NO LONGER USE "_random"
event: ` event: `
id, event_id, event_id_random, id, event_id, event_id_random,
code, code,

View File

@@ -817,7 +817,10 @@ async function _process_generic_props<T extends Record<string, any>>({
const group = processed_obj.group ?? '0'; const group = processed_obj.group ?? '0';
const priority = processed_obj.priority ? 1 : 0; const priority = processed_obj.priority ? 1 : 0;
const sort = processed_obj.sort ?? '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 ?? ''; const name = processed_obj.name ?? '';
(processed_obj as any).tmp_sort_1 = (processed_obj as any).tmp_sort_1 =
@@ -862,7 +865,8 @@ export async function process_ae_obj__journal_props({
obj.cfg_json = obj.cfg_json ?? {}; obj.cfg_json = obj.cfg_json ?? {};
obj.data_json = obj.data_json ?? {}; obj.data_json = obj.data_json ?? {};
const updated = obj.updated_on ?? obj.created_on ?? new Date(0).toISOString(); const updated =
obj.updated_on ?? obj.created_on ?? new Date(0).toISOString();
obj.tmp_sort_3 = `${obj.group ?? '0'}_${obj.priority ? 1 : 0}_${obj.sort ?? '0'}_${ obj.tmp_sort_3 = `${obj.group ?? '0'}_${obj.priority ? 1 : 0}_${obj.sort ?? '0'}_${
obj.name obj.name
}_${updated}`; }_${updated}`;

View File

@@ -942,7 +942,10 @@ async function _process_generic_props<T extends Record<string, any>>({
const group = processed_obj.group ?? '0'; const group = processed_obj.group ?? '0';
const priority = processed_obj.priority ? 1 : 0; const priority = processed_obj.priority ? 1 : 0;
const sort = processed_obj.sort ?? '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 ?? ''; const name = processed_obj.name ?? '';
(processed_obj as any).tmp_sort_1 = (processed_obj as any).tmp_sort_1 =
@@ -1026,7 +1029,8 @@ export async function process_ae_obj__journal_entry_props({
// Journal entry-specific computed sort fields, overriding generic ones if needed // Journal entry-specific computed sort fields, overriding generic ones if needed
const sort_val = (obj.sort ?? 0).toString().padStart(3, '0'); const sort_val = (obj.sort ?? 0).toString().padStart(3, '0');
const updated = obj.updated_on ?? obj.created_on ?? new Date(0).toISOString(); const updated =
obj.updated_on ?? obj.created_on ?? new Date(0).toISOString();
obj.tmp_sort_1 = `${obj.group ?? ''}_${obj.priority ? '1' : '0'}_${ obj.tmp_sort_1 = `${obj.group ?? ''}_${obj.priority ? '1' : '0'}_${
sort_val sort_val
}_${updated}`; }_${updated}`;

View File

@@ -74,18 +74,19 @@ export async function load_ae_obj_id__post({
}); });
if (inc_comment_li && ae_promises.load__post_obj) { if (inc_comment_li && ae_promises.load__post_obj) {
ae_promises.load__post_obj.post_comment_li = await load_ae_obj_li__post_comment({ ae_promises.load__post_obj.post_comment_li =
api_cfg: api_cfg, await load_ae_obj_li__post_comment({
for_obj_type: 'post', api_cfg: api_cfg,
for_obj_id: post_id, for_obj_type: 'post',
enabled, for_obj_id: post_id,
hidden, enabled,
limit, hidden,
offset, limit,
params, offset,
try_cache, params,
log_lvl try_cache,
}); log_lvl
});
} }
return ae_promises.load__post_obj; return ae_promises.load__post_obj;
@@ -151,7 +152,8 @@ export async function load_ae_obj_li__post({
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__post_props({ const processed_obj_li = await process_ae_obj__post_props({
obj_li: post_obj_li_get_result, obj_li: post_obj_li_get_result,
account_id: for_obj_type === 'account' ? for_obj_id : undefined, account_id:
for_obj_type === 'account' ? for_obj_id : undefined,
log_lvl: log_lvl log_lvl: log_lvl
}); });
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
@@ -171,18 +173,19 @@ export async function load_ae_obj_li__post({
if (inc_comment_li && ae_promises.load__post_obj_li) { if (inc_comment_li && ae_promises.load__post_obj_li) {
for (let i = 0; i < ae_promises.load__post_obj_li.length; i++) { for (let i = 0; i < ae_promises.load__post_obj_li.length; i++) {
const post_obj = ae_promises.load__post_obj_li[i]; const post_obj = ae_promises.load__post_obj_li[i];
ae_promises.load__post_obj_li[i].post_comment_li = await load_ae_obj_li__post_comment({ ae_promises.load__post_obj_li[i].post_comment_li =
api_cfg: api_cfg, await load_ae_obj_li__post_comment({
for_obj_type: 'post', api_cfg: api_cfg,
for_obj_id: post_obj.post_id, for_obj_type: 'post',
enabled, for_obj_id: post_obj.post_id,
hidden, enabled,
limit, hidden,
offset, limit,
params, offset,
try_cache, params,
log_lvl try_cache,
}); log_lvl
});
} }
} }
@@ -291,7 +294,10 @@ export async function update_ae_obj__post({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_Post | null> { }): Promise<ae_Post | null> {
if (log_lvl) { if (log_lvl) {
console.log(`*** update_ae_obj__post() *** post_id=${post_id}`, data_kv); console.log(
`*** update_ae_obj__post() *** post_id=${post_id}`,
data_kv
);
} }
const result = await api.update_ae_obj({ const result = await api.update_ae_obj({
@@ -358,7 +364,11 @@ export async function qry__post({
const search_query: any = { and: [] }; const search_query: any = { and: [] };
if (account_id) { if (account_id) {
search_query.and.push({ field: 'account_id', op: 'eq', value: account_id }); search_query.and.push({
field: 'account_id',
op: 'eq',
value: account_id
});
} }
if (qry_str) { if (qry_str) {
@@ -366,7 +376,11 @@ export async function qry__post({
} }
if (qry_person_id) { if (qry_person_id) {
search_query.and.push({ field: 'external_person_id', op: 'eq', value: qry_person_id }); search_query.and.push({
field: 'external_person_id',
op: 'eq',
value: qry_person_id
});
} }
// if (qry_archive_on) { // if (qry_archive_on) {
@@ -428,18 +442,19 @@ export async function qry__post({
if (inc_comment_li && ae_promises.load__post_obj_li) { if (inc_comment_li && ae_promises.load__post_obj_li) {
for (let i = 0; i < ae_promises.load__post_obj_li.length; i++) { for (let i = 0; i < ae_promises.load__post_obj_li.length; i++) {
const post_obj = ae_promises.load__post_obj_li[i]; const post_obj = ae_promises.load__post_obj_li[i];
ae_promises.load__post_obj_li[i].post_comment_li = await load_ae_obj_li__post_comment({ ae_promises.load__post_obj_li[i].post_comment_li =
api_cfg: api_cfg, await load_ae_obj_li__post_comment({
for_obj_type: 'post', api_cfg: api_cfg,
for_obj_id: post_obj.post_id, for_obj_type: 'post',
enabled, for_obj_id: post_obj.post_id,
hidden, enabled,
limit, hidden,
offset, limit,
// params, offset,
try_cache: true, // Always cache comments if we are caching posts // params,
log_lvl try_cache: true, // Always cache comments if we are caching posts
}); log_lvl
});
} }
} }
@@ -512,14 +527,21 @@ async function _process_generic_props<T extends Record<string, any>>({
const group = processed_obj.group ?? '0'; const group = processed_obj.group ?? '0';
const priority = processed_obj.priority ? 1 : 0; const priority = processed_obj.priority ? 1 : 0;
const sort = processed_obj.sort ?? '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.username ?? processed_obj.name ?? ''; 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_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
(processed_obj as any).tmp_sort_2 =
`${group}_${priority}_${sort}_${name}_${updated}`;
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);
@@ -549,7 +571,8 @@ export async function process_ae_obj__post_props({
} }
obj.name = obj.title; obj.name = obj.title;
const sort_val = (obj.sort ?? 0).toString().padStart(3, '0'); const sort_val = (obj.sort ?? 0).toString().padStart(3, '0');
const updated = obj.updated_on ?? obj.created_on ?? new Date(0).toISOString(); const updated =
obj.updated_on ?? obj.created_on ?? new Date(0).toISOString();
obj.tmp_sort_1 = `${obj.group ?? ''}_${obj.priority ? '1' : '0'}_${ obj.tmp_sort_1 = `${obj.group ?? ''}_${obj.priority ? '1' : '0'}_${
sort_val sort_val
}_${updated}`; }_${updated}`;

View File

@@ -24,7 +24,9 @@ export async function load_ae_obj_id__post_comment({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_PostComment | null> { }): Promise<ae_PostComment | null> {
if (log_lvl) { if (log_lvl) {
console.log(`*** load_ae_obj_id__post_comment() *** post_comment_id=${post_comment_id}`); console.log(
`*** load_ae_obj_id__post_comment() *** post_comment_id=${post_comment_id}`
);
} }
ae_promises.load__post_comment_obj = await api ae_promises.load__post_comment_obj = await api
@@ -39,10 +41,11 @@ export async function load_ae_obj_id__post_comment({
.then(async function (post_comment_obj_get_result) { .then(async function (post_comment_obj_get_result) {
if (post_comment_obj_get_result) { if (post_comment_obj_get_result) {
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__post_comment_props({ const processed_obj_li =
obj_li: [post_comment_obj_get_result], await process_ae_obj__post_comment_props({
log_lvl: log_lvl obj_li: [post_comment_obj_get_result],
}); log_lvl: log_lvl
});
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_posts, db_instance: db_posts,
table_name: 'comment', table_name: 'comment',
@@ -121,10 +124,11 @@ export async function load_ae_obj_li__post_comment({
.then(async function (post_comment_obj_li_get_result) { .then(async function (post_comment_obj_li_get_result) {
if (post_comment_obj_li_get_result) { if (post_comment_obj_li_get_result) {
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__post_comment_props({ const processed_obj_li =
obj_li: post_comment_obj_li_get_result, await process_ae_obj__post_comment_props({
log_lvl: log_lvl obj_li: post_comment_obj_li_get_result,
}); log_lvl: log_lvl
});
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_posts, db_instance: db_posts,
table_name: 'comment', table_name: 'comment',
@@ -161,7 +165,9 @@ export async function create_ae_obj__post_comment({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_PostComment | null> { }): Promise<ae_PostComment | null> {
if (log_lvl) { if (log_lvl) {
console.log(`*** create_ae_obj__post_comment() *** account_id=${account_id} post_id=${post_id}`); console.log(
`*** create_ae_obj__post_comment() *** account_id=${account_id} post_id=${post_id}`
);
} }
const result = await api.create_nested_obj({ const result = await api.create_nested_obj({
@@ -212,7 +218,9 @@ export async function delete_ae_obj_id__post_comment({
log_lvl?: number; log_lvl?: number;
}) { }) {
if (log_lvl) { if (log_lvl) {
console.log(`*** delete_ae_obj_id__post_comment() *** post_comment_id=${post_comment_id}`); console.log(
`*** delete_ae_obj_id__post_comment() *** post_comment_id=${post_comment_id}`
);
} }
const result = await api.delete_ae_obj({ const result = await api.delete_ae_obj({
@@ -248,7 +256,10 @@ export async function update_ae_obj__post_comment({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_PostComment | null> { }): Promise<ae_PostComment | null> {
if (log_lvl) { if (log_lvl) {
console.log(`*** update_ae_obj__post_comment() *** post_comment_id=${post_comment_id}`, data_kv); console.log(
`*** update_ae_obj__post_comment() *** post_comment_id=${post_comment_id}`,
data_kv
);
} }
const result = await api.update_ae_obj({ const result = await api.update_ae_obj({
@@ -336,14 +347,21 @@ async function _process_generic_props<T extends Record<string, any>>({
const group = processed_obj.group ?? '0'; const group = processed_obj.group ?? '0';
const priority = processed_obj.priority ? 1 : 0; const priority = processed_obj.priority ? 1 : 0;
const sort = processed_obj.sort ?? '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 ?? ''; const name = processed_obj.name ?? '';
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`; (processed_obj as any).tmp_sort_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
(processed_obj as any).tmp_sort_2 =
`${group}_${priority}_${sort}_${name}_${updated}`;
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);
@@ -366,7 +384,8 @@ export async function process_ae_obj__post_comment_props({
log_lvl, log_lvl,
specific_processor: (obj) => { specific_processor: (obj) => {
const sort_val = (obj.sort ?? 0).toString().padStart(3, '0'); const sort_val = (obj.sort ?? 0).toString().padStart(3, '0');
const updated = obj.updated_on ?? obj.created_on ?? new Date(0).toISOString(); const updated =
obj.updated_on ?? obj.created_on ?? new Date(0).toISOString();
obj.tmp_sort_1 = `${obj.group ?? ''}_${obj.priority ? '1' : '0'}_${ obj.tmp_sort_1 = `${obj.group ?? ''}_${obj.priority ? '1' : '0'}_${
sort_val sort_val
}_${updated}`; }_${updated}`;

View File

@@ -1,6 +1,9 @@
import type { key_val } from '$lib/stores/ae_stores'; import type { key_val } from '$lib/stores/ae_stores';
import { api } from '$lib/api/api'; import { api } from '$lib/api/api';
import { process_ae_obj__activity_log_props, properties_to_save } from '$lib/ae_core/ae_core__activity_log'; import {
process_ae_obj__activity_log_props,
properties_to_save
} from '$lib/ae_core/ae_core__activity_log';
import { db_save_ae_obj_li__ae_obj } from '$lib/ae_core/core__idb_dexie'; import { db_save_ae_obj_li__ae_obj } from '$lib/ae_core/core__idb_dexie';
import { db_core } from '$lib/ae_core/db_core'; import { db_core } from '$lib/ae_core/db_core';
@@ -17,129 +20,144 @@ import { db_core } from '$lib/ae_core/db_core';
* @returns A structured array of meeting report objects. * @returns A structured array of meeting report objects.
*/ */
export async function qry__jitsi_report({ export async function qry__jitsi_report({
api_cfg, api_cfg,
account_id, account_id,
enabled = 'all', enabled = 'all',
hidden = 'all', hidden = 'all',
limit = 500, limit = 500,
try_cache = true, try_cache = true,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
account_id: string; account_id: string;
enabled?: 'enabled' | 'all' | 'not_enabled' | undefined; enabled?: 'enabled' | 'all' | 'not_enabled' | undefined;
hidden?: 'hidden' | 'all' | 'not_hidden' | undefined; hidden?: 'hidden' | 'all' | 'not_hidden' | undefined;
limit?: number; limit?: number;
try_cache?: boolean; try_cache?: boolean;
log_lvl?: number; log_lvl?: number;
}) { }) {
if (log_lvl) console.log('*** qry__jitsi_report() ***'); if (log_lvl) console.log('*** qry__jitsi_report() ***');
// Step 1: Query all relevant activity logs from the API. // Step 1: Query all relevant activity logs from the API.
const search_query = { const search_query = {
or: [ or: [
{ field: 'name', op: 'eq', value: 'jitsi_meeting_event' }, { field: 'name', op: 'eq', value: 'jitsi_meeting_event' },
{ field: 'name', op: 'eq', value: 'jitsi_meeting_stats' } { field: 'name', op: 'eq', value: 'jitsi_meeting_stats' }
], ],
and: [ and: [{ field: 'account_id_random', op: 'eq', value: account_id }]
{ field: 'account_id_random', op: 'eq', value: account_id } };
]
};
const result = await api.search_ae_obj({ const result = await api.search_ae_obj({
api_cfg: api_cfg, api_cfg: api_cfg,
obj_type: 'activity_log', obj_type: 'activity_log',
search_query, search_query,
headers: { 'x-account-id': account_id }, headers: { 'x-account-id': account_id },
enabled, enabled,
hidden, hidden,
limit, limit,
log_lvl: log_lvl log_lvl: log_lvl
}); });
// Handle potential V3 API envelope // Handle potential V3 API envelope
let flat_log_list: any[] = []; let flat_log_list: any[] = [];
if (Array.isArray(result)) { if (Array.isArray(result)) {
flat_log_list = result; flat_log_list = result;
} else if (result && typeof result === 'object' && Array.isArray((result as any).data)) { } else if (
flat_log_list = (result as any).data; result &&
} typeof result === 'object' &&
Array.isArray((result as any).data)
) {
flat_log_list = (result as any).data;
}
if (!flat_log_list || !Array.isArray(flat_log_list) || flat_log_list.length === 0) { if (
if (log_lvl) console.log('No Jitsi activity logs found or error occurred.'); !flat_log_list ||
return []; !Array.isArray(flat_log_list) ||
} flat_log_list.length === 0
) {
if (log_lvl)
console.log('No Jitsi activity logs found or error occurred.');
return [];
}
// Frontier Standard: Process and Save to local cache // Frontier Standard: Process and Save to local cache
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__activity_log_props({ const processed_obj_li = await process_ae_obj__activity_log_props({
obj_li: flat_log_list, obj_li: flat_log_list,
log_lvl: log_lvl log_lvl: log_lvl
}); });
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_core, db_instance: db_core,
table_name: 'activity_log', table_name: 'activity_log',
obj_li: processed_obj_li, obj_li: processed_obj_li,
properties_to_save: properties_to_save, properties_to_save: properties_to_save,
log_lvl: log_lvl log_lvl: log_lvl
}); });
} }
// Step 2: Process the flat list into a structured report. // Step 2: Process the flat list into a structured report.
const meetings = new Map<string, any>(); const meetings = new Map<string, any>();
for (const log of flat_log_list) { for (const log of flat_log_list) {
const meeting_id = log.external_client_id; const meeting_id = log.external_client_id;
if (!meeting_id) continue; if (!meeting_id) continue;
// Make sure the name field is prefixed with "jitsi_" // Make sure the name field is prefixed with "jitsi_"
if (!log.name.startsWith('jitsi_')) continue; if (!log.name.startsWith('jitsi_')) continue;
// Ensure a base entry for the meeting exists // Ensure a base entry for the meeting exists
if (!meetings.has(meeting_id)) { if (!meetings.has(meeting_id)) {
meetings.set(meeting_id, { meetings.set(meeting_id, {
meeting_id: meeting_id, meeting_id: meeting_id,
room_name: 'Unknown', room_name: 'Unknown',
start_time: log.created_on, // Fallback start time start_time: log.created_on, // Fallback start time
final_duration: '00:00:00', final_duration: '00:00:00',
final_participants: [], final_participants: [],
final_participant_count: 0, final_participant_count: 0,
events: [] events: []
}); });
} }
const meeting_report = meetings.get(meeting_id); const meeting_report = meetings.get(meeting_id);
if (log.action === 'jitsi_meeting_init') { if (log.action === 'jitsi_meeting_init') {
// This is the main log entry, containing the final state. // This is the main log entry, containing the final state.
meeting_report.room_name = log.description; meeting_report.room_name = log.description;
meeting_report.start_time = log.created_on; // The init log has the true start time meeting_report.start_time = log.created_on; // The init log has the true start time
if (log.meta_json) { if (log.meta_json) {
meeting_report.final_duration = log.meta_json.duration; meeting_report.final_duration = log.meta_json.duration;
meeting_report.final_participants = log.meta_json.participants; meeting_report.final_participants = log.meta_json.participants;
meeting_report.final_participant_count = log.meta_json.participant_count; meeting_report.final_participant_count =
} log.meta_json.participant_count;
} else { }
// This is a discrete event log. } else {
meeting_report.events.push({ // This is a discrete event log.
timestamp: log.created_on, meeting_report.events.push({
action: log.action, timestamp: log.created_on,
details: log.meta_json action: log.action,
}); details: log.meta_json
} });
} }
}
// Sort events within each meeting chronologically // Sort events within each meeting chronologically
for (const report of meetings.values()) { for (const report of meetings.values()) {
report.events.sort((a: any, b: any) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()); report.events.sort(
} (a: any, b: any) =>
new Date(a.timestamp).getTime() -
new Date(b.timestamp).getTime()
);
}
const final_report = Array.from(meetings.values()); const final_report = Array.from(meetings.values());
final_report.sort((a, b) => new Date(b.start_time).getTime() - new Date(a.start_time).getTime()); final_report.sort(
(a, b) =>
new Date(b.start_time).getTime() - new Date(a.start_time).getTime()
);
if (log_lvl) console.log('Final Jitsi report:', final_report); if (log_lvl) console.log('Final Jitsi report:', final_report);
return final_report; return final_report;
} }
export const load_jitsi_report = qry__jitsi_report; export const load_jitsi_report = qry__jitsi_report;

View File

@@ -175,11 +175,15 @@ async function _process_generic_props<T extends Record<string, any>>({
const updated = processed_obj.updated_on ?? processed_obj.created_on; const updated = processed_obj.updated_on ?? processed_obj.created_on;
const name = processed_obj.name ?? ''; const name = processed_obj.name ?? '';
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`; (processed_obj as any).tmp_sort_1 =
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`; `${group}_${priority}_${sort}_${updated}`;
(processed_obj as any).tmp_sort_2 =
`${group}_${priority}_${sort}_${name}_${updated}`;
if (specific_processor) { 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); processed_obj_li.push(processed_obj as T);
@@ -201,7 +205,9 @@ export async function load_ae_obj_id__sponsorship_cfg({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_SponsorshipCfg | null> { }): Promise<ae_SponsorshipCfg | null> {
if (log_lvl) { if (log_lvl) {
console.log(`*** load_ae_obj_id__sponsorship_cfg() *** [V3] id=${sponsorship_cfg_id} (SWR)`); console.log(
`*** load_ae_obj_id__sponsorship_cfg() *** [V3] id=${sponsorship_cfg_id} (SWR)`
);
} }
// 1. FAST PATH: Cache hit // 1. FAST PATH: Cache hit
@@ -209,17 +215,32 @@ export async function load_ae_obj_id__sponsorship_cfg({
try { try {
const cached = await db_sponsorships.cfg.get(sponsorship_cfg_id); const cached = await db_sponsorships.cfg.get(sponsorship_cfg_id);
if (cached) { if (cached) {
_refresh_sponsorship_cfg_id_background({ api_cfg, sponsorship_cfg_id, try_cache, log_lvl: 0 }); _refresh_sponsorship_cfg_id_background({
api_cfg,
sponsorship_cfg_id,
try_cache,
log_lvl: 0
});
return cached as unknown as ae_SponsorshipCfg; return cached as unknown as ae_SponsorshipCfg;
} }
} catch (e) {} } catch (e) {}
} }
// 2. SLOW PATH: Wait for API // 2. SLOW PATH: Wait for API
return await _refresh_sponsorship_cfg_id_background({ api_cfg, sponsorship_cfg_id, try_cache, log_lvl }); return await _refresh_sponsorship_cfg_id_background({
api_cfg,
sponsorship_cfg_id,
try_cache,
log_lvl
});
} }
async function _refresh_sponsorship_cfg_id_background({ api_cfg, sponsorship_cfg_id, try_cache, log_lvl }: any) { async function _refresh_sponsorship_cfg_id_background({
api_cfg,
sponsorship_cfg_id,
try_cache,
log_lvl
}: any) {
if (typeof navigator !== 'undefined' && !navigator.onLine) return null; if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
try { try {
const result = await api.get_ae_obj({ const result = await api.get_ae_obj({
@@ -230,7 +251,11 @@ async function _refresh_sponsorship_cfg_id_background({ api_cfg, sponsorship_cfg
}); });
if (result) { if (result) {
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__sponsorship_cfg_props({ obj_li: [result], log_lvl }); const processed_obj_li =
await process_ae_obj__sponsorship_cfg_props({
obj_li: [result],
log_lvl
});
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_sponsorships, db_instance: db_sponsorships,
table_name: 'cfg', table_name: 'cfg',
@@ -258,25 +283,43 @@ export async function load_ae_obj_id__sponsorship({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_Sponsorship | null> { }): Promise<ae_Sponsorship | null> {
if (log_lvl) { if (log_lvl) {
console.log(`*** load_ae_obj_id__sponsorship() *** [V3] id=${sponsorship_id} (SWR)`); console.log(
`*** load_ae_obj_id__sponsorship() *** [V3] id=${sponsorship_id} (SWR)`
);
} }
// 1. FAST PATH: Cache hit // 1. FAST PATH: Cache hit
if (try_cache) { if (try_cache) {
try { try {
const cached = await db_sponsorships.sponsorship.get(sponsorship_id); const cached =
await db_sponsorships.sponsorship.get(sponsorship_id);
if (cached) { if (cached) {
_refresh_sponsorship_id_background({ api_cfg, sponsorship_id, try_cache, log_lvl: 0 }); _refresh_sponsorship_id_background({
api_cfg,
sponsorship_id,
try_cache,
log_lvl: 0
});
return cached as unknown as ae_Sponsorship; return cached as unknown as ae_Sponsorship;
} }
} catch (e) {} } catch (e) {}
} }
// 2. SLOW PATH: Wait for API // 2. SLOW PATH: Wait for API
return await _refresh_sponsorship_id_background({ api_cfg, sponsorship_id, try_cache, log_lvl }); return await _refresh_sponsorship_id_background({
api_cfg,
sponsorship_id,
try_cache,
log_lvl
});
} }
async function _refresh_sponsorship_id_background({ api_cfg, sponsorship_id, try_cache, log_lvl }: any) { async function _refresh_sponsorship_id_background({
api_cfg,
sponsorship_id,
try_cache,
log_lvl
}: any) {
if (typeof navigator !== 'undefined' && !navigator.onLine) return null; if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
try { try {
const result = await api.get_ae_obj({ const result = await api.get_ae_obj({
@@ -287,7 +330,11 @@ async function _refresh_sponsorship_id_background({ api_cfg, sponsorship_id, try
}); });
if (result) { if (result) {
if (try_cache) { if (try_cache) {
const processed_obj_li = await process_ae_obj__sponsorship_props({ obj_li: [result], log_lvl }); const processed_obj_li =
await process_ae_obj__sponsorship_props({
obj_li: [result],
log_lvl
});
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
db_instance: db_sponsorships, db_instance: db_sponsorships,
table_name: 'sponsorship', table_name: 'sponsorship',
@@ -332,7 +379,9 @@ export async function load_ae_obj_li__sponsorship({
log_lvl?: number; log_lvl?: number;
}): Promise<ae_Sponsorship[]> { }): Promise<ae_Sponsorship[]> {
if (log_lvl) { if (log_lvl) {
console.log(`*** load_ae_obj_li__sponsorship() *** [V3] for=${for_obj_type}:${for_obj_id}`); console.log(
`*** load_ae_obj_li__sponsorship() *** [V3] for=${for_obj_type}:${for_obj_id}`
);
} }
const result_li = await api.get_ae_obj_li({ const result_li = await api.get_ae_obj_li({

View File

@@ -9,7 +9,10 @@ import {
import { get_obj_li_w_match_prop } from './ae_utils__get_obj_li_w_match_prop'; import { get_obj_li_w_match_prop } from './ae_utils__get_obj_li_w_match_prop';
import { file_extension_icon } from './ae_utils__file_extension_icon'; import { file_extension_icon } from './ae_utils__file_extension_icon';
import { file_extension_icon_lucide } from './ae_utils__file_extension_icon_lucide'; import { file_extension_icon_lucide } from './ae_utils__file_extension_icon_lucide';
import { process_permission_checks, compare_access_levels } from './ae_utils__perm_checks'; import {
process_permission_checks,
compare_access_levels
} from './ae_utils__perm_checks';
import { iso_datetime_formatter } from './ae_utils__datetime_format'; import { iso_datetime_formatter } from './ae_utils__datetime_format';
import { is_datetime_recent } from './ae_utils__is_datetime_recent'; import { is_datetime_recent } from './ae_utils__is_datetime_recent';
import { extract_prefixed_form_data } from './ae_utils__extract_prefixed_form_data'; import { extract_prefixed_form_data } from './ae_utils__extract_prefixed_form_data';
@@ -81,7 +84,6 @@ function handle_url_and_message(name: string, value: null | string) {
// console.log('Message sent to parent (iframe):', message); // console.log('Message sent to parent (iframe):', message);
} }
// ALERT: Not referenced anywhere -2026-02-03 // ALERT: Not referenced anywhere -2026-02-03
function create_a_element({ function create_a_element({
account_id, account_id,

View File

@@ -8,12 +8,22 @@ async function generate_iv() {
} }
// Updated 2025-05-08 // Updated 2025-05-08
export const encrypt_content = async function encrypt_content(content: string, keyData: string) { export const encrypt_content = async function encrypt_content(
content: string,
keyData: string
) {
const iv = await generate_iv(); const iv = await generate_iv();
const keyBytes = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(keyData)); const keyBytes = await crypto.subtle.digest(
const key = await crypto.subtle.importKey('raw', keyBytes, { name: 'AES-CBC' }, false, [ 'SHA-256',
'encrypt' new TextEncoder().encode(keyData)
]); );
const key = await crypto.subtle.importKey(
'raw',
keyBytes,
{ name: 'AES-CBC' },
false,
['encrypt']
);
const encodedContent = await crypto.subtle.encrypt( const encodedContent = await crypto.subtle.encrypt(
{ name: 'AES-CBC', iv: iv.buffer as ArrayBuffer }, { name: 'AES-CBC', iv: iv.buffer as ArrayBuffer },
key, key,
@@ -52,7 +62,10 @@ export const combine_iv_and_base64 = function combine_iv_and_base64(
}; };
// Updated 2025-05-08 // Updated 2025-05-08
export const encrypt_wrapper = async function encrypt_wrapper(content: string, keyData: string) { export const encrypt_wrapper = async function encrypt_wrapper(
content: string,
keyData: string
) {
if (!content) { if (!content) {
console.error('No content provided. Returning empty string.'); console.error('No content provided. Returning empty string.');
return ''; return '';
@@ -73,11 +86,20 @@ export const decrypt_content = async function decrypt_content(
iv: Uint8Array, iv: Uint8Array,
keyData: string keyData: string
) { ) {
const keyBytes = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(keyData)); const keyBytes = await crypto.subtle.digest(
const key = await crypto.subtle.importKey('raw', keyBytes, { name: 'AES-CBC' }, false, [ 'SHA-256',
'decrypt' new TextEncoder().encode(keyData)
]); );
const encryptedContent = Uint8Array.from(atob(base64Content), (c) => c.charCodeAt(0)); const key = await crypto.subtle.importKey(
'raw',
keyBytes,
{ name: 'AES-CBC' },
false,
['decrypt']
);
const encryptedContent = Uint8Array.from(atob(base64Content), (c) =>
c.charCodeAt(0)
);
const decryptedContent = await crypto.subtle.decrypt( const decryptedContent = await crypto.subtle.decrypt(
{ name: 'AES-CBC', iv: iv.buffer as ArrayBuffer }, { name: 'AES-CBC', iv: iv.buffer as ArrayBuffer },
key, key,
@@ -89,7 +111,9 @@ export const decrypt_content = async function decrypt_content(
}; };
// Updated 2025-05-08 // Updated 2025-05-08
export const split_iv_and_base64 = function split_iv_and_base64(combined: string) { export const split_iv_and_base64 = function split_iv_and_base64(
combined: string
) {
if (!combined) { if (!combined) {
console.error('No combined string provided. Returning empty object.'); console.error('No combined string provided. Returning empty object.');
return { iv: new Uint8Array(), base64: '' }; return { iv: new Uint8Array(), base64: '' };
@@ -97,7 +121,9 @@ export const split_iv_and_base64 = function split_iv_and_base64(combined: string
const [iv_hex, encrypted_base64_string] = combined.split(':'); const [iv_hex, encrypted_base64_string] = combined.split(':');
const base64 = encrypted_base64_string; const base64 = encrypted_base64_string;
const match_result = iv_hex.match(/.{1,2}/g); const match_result = iv_hex.match(/.{1,2}/g);
const iv = new Uint8Array((match_result || []).map((byte) => parseInt(byte, 16))); const iv = new Uint8Array(
(match_result || []).map((byte) => parseInt(byte, 16))
);
if (log_lvl) { if (log_lvl) {
console.log(`IV: ${iv}; Encrypted:`, base64); console.log(`IV: ${iv}; Encrypted:`, base64);
} }
@@ -105,7 +131,10 @@ export const split_iv_and_base64 = function split_iv_and_base64(combined: string
}; };
// Updated 2025-05-15 // Updated 2025-05-15
export const decrypt_wrapper = async function decrypt_wrapper(combined: string, keyData: string) { export const decrypt_wrapper = async function decrypt_wrapper(
combined: string,
keyData: string
) {
if (!combined) { if (!combined) {
console.error('No combined string provided. Returning empty string.'); console.error('No combined string provided. Returning empty string.');
return false; return false;

View File

@@ -66,7 +66,9 @@ export const iso_datetime_formatter = function iso_datetime_formatter(
datetime_string = dayjs(raw_datetime).format('MM-DD hh:mm A'); datetime_string = dayjs(raw_datetime).format('MM-DD hh:mm A');
break; break;
case 'datetime_iso_tz': case 'datetime_iso_tz':
datetime_string = dayjs(raw_datetime).format('YYYY-MM-DD HH:mm:ss Z'); datetime_string = dayjs(raw_datetime).format(
'YYYY-MM-DD HH:mm:ss Z'
);
break; break;
case 'datetime_iso_12_no_seconds': case 'datetime_iso_12_no_seconds':
datetime_string = dayjs(raw_datetime).format('YYYY-MM-DD hh:mm A'); datetime_string = dayjs(raw_datetime).format('YYYY-MM-DD hh:mm A');
@@ -75,7 +77,9 @@ export const iso_datetime_formatter = function iso_datetime_formatter(
// datetime_string = dayjs(raw_datetime).format('YYYY-MM-DD hh:mm A'); // datetime_string = dayjs(raw_datetime).format('YYYY-MM-DD hh:mm A');
// break; // break;
case 'datetime_us': case 'datetime_us':
datetime_string = dayjs(raw_datetime).format('MM/DD/YYYY hh:mm:ss A'); datetime_string = dayjs(raw_datetime).format(
'MM/DD/YYYY hh:mm:ss A'
);
break; break;
case 'datetime_short': case 'datetime_short':
datetime_string = dayjs(raw_datetime).format('MMM D, YY HH:mm'); datetime_string = dayjs(raw_datetime).format('MMM D, YY HH:mm');
@@ -93,13 +97,17 @@ export const iso_datetime_formatter = function iso_datetime_formatter(
datetime_string = dayjs(raw_datetime).format('MMMM D, YYYY HH:mm'); datetime_string = dayjs(raw_datetime).format('MMMM D, YYYY HH:mm');
break; break;
case 'datetime_12_long': case 'datetime_12_long':
datetime_string = dayjs(raw_datetime).format('MMMM D, YYYY hh:mm A'); datetime_string = dayjs(raw_datetime).format(
'MMMM D, YYYY hh:mm A'
);
break; break;
case 'datetime_medium_sec': case 'datetime_medium_sec':
datetime_string = dayjs(raw_datetime).format('MMM D, YYYY H:mm:ss'); datetime_string = dayjs(raw_datetime).format('MMM D, YYYY H:mm:ss');
break; break;
case 'datetime_12_medium_sec': case 'datetime_12_medium_sec':
datetime_string = dayjs(raw_datetime).format('MMM D, YYYY h:mm:ss A'); datetime_string = dayjs(raw_datetime).format(
'MMM D, YYYY h:mm:ss A'
);
break; break;
case 'datetime_short_month': case 'datetime_short_month':
datetime_string = dayjs(raw_datetime).format('MMM D hh:mm A'); datetime_string = dayjs(raw_datetime).format('MMM D hh:mm A');

View File

@@ -50,7 +50,9 @@ export const extract_prefixed_form_data = function extract_prefixed_form_data({
for (const field of form_data) { for (const field of form_data) {
let [obj_prop_name, obj_prop_value] = field; let [obj_prop_name, obj_prop_value] = field;
if (log_lvl > 1) { if (log_lvl > 1) {
console.log(`${obj_prop_name}: ${obj_prop_value} type=${typeof obj_prop_value}`); console.log(
`${obj_prop_name}: ${obj_prop_value} type=${typeof obj_prop_value}`
);
} }
// Trim string values if needed // Trim string values if needed
@@ -83,19 +85,31 @@ export const extract_prefixed_form_data = function extract_prefixed_form_data({
// if (obj_prop_name.startsWith(prefix)) { // if (obj_prop_name.startsWith(prefix)) {
obj_prop_name = obj_prop_name.replace(prefix, ''); obj_prop_name = obj_prop_name.replace(prefix, '');
if (log_lvl) { if (log_lvl) {
console.log(`Checking: (${prefix})${obj_prop_name} value=${obj_prop_value}`); console.log(
`Checking: (${prefix})${obj_prop_name} value=${obj_prop_value}`
);
} }
if (rm_empty_id && obj_prop_name.endsWith('id_random') && !obj_prop_value) { if (
rm_empty_id &&
obj_prop_name.endsWith('id_random') &&
!obj_prop_value
) {
if (log_lvl) { if (log_lvl) {
console.log(`Match but empty *_id_random. Ignoring/removing: ${obj_prop_name}`); console.log(
`Match but empty *_id_random. Ignoring/removing: ${obj_prop_name}`
);
} }
} else if (rm_empty && !obj_prop_value) { } else if (rm_empty && !obj_prop_value) {
if (log_lvl) { if (log_lvl) {
console.log(`Match but empty. Ignoring/removing: ${obj_prop_name}`); console.log(
`Match but empty. Ignoring/removing: ${obj_prop_name}`
);
} }
} else { } else {
if (log_lvl) { if (log_lvl) {
console.log(`Match: ${prefix})${obj_prop_name} value=${obj_prop_value}`); console.log(
`Match: ${prefix})${obj_prop_name} value=${obj_prop_value}`
);
} }
data_obj[obj_prop_name] = obj_prop_value; data_obj[obj_prop_name] = obj_prop_value;
} }
@@ -107,19 +121,31 @@ export const extract_prefixed_form_data = function extract_prefixed_form_data({
} else { } else {
// No prefix set // No prefix set
if (log_lvl) { if (log_lvl) {
console.log(`Checking: ${obj_prop_name} value=${obj_prop_value}`); console.log(
`Checking: ${obj_prop_name} value=${obj_prop_value}`
);
} }
if (rm_empty_id && obj_prop_name.endsWith('id_random') && !obj_prop_value) { if (
rm_empty_id &&
obj_prop_name.endsWith('id_random') &&
!obj_prop_value
) {
if (log_lvl > 1) { if (log_lvl > 1) {
console.log(`Match but empty *_id_random. Ignoring/removing: ${obj_prop_name}`); console.log(
`Match but empty *_id_random. Ignoring/removing: ${obj_prop_name}`
);
} }
} else if (rm_empty && !obj_prop_value) { } else if (rm_empty && !obj_prop_value) {
if (log_lvl > 1) { if (log_lvl > 1) {
console.log(`Match but empty. Ignoring/removing: ${obj_prop_name}`); console.log(
`Match but empty. Ignoring/removing: ${obj_prop_name}`
);
} }
} else { } else {
if (log_lvl > 1) { if (log_lvl > 1) {
console.log(`Match: ${obj_prop_name} value=${obj_prop_value}`); console.log(
`Match: ${obj_prop_name} value=${obj_prop_value}`
);
} }
data_obj[obj_prop_name] = obj_prop_value; data_obj[obj_prop_name] = obj_prop_value;
} }

View File

@@ -5,61 +5,63 @@ import * as Lucide from 'lucide-svelte';
* @param extension The file extension (e.g., 'pdf', 'jpg'). * @param extension The file extension (e.g., 'pdf', 'jpg').
* @returns The Lucide icon component. * @returns The Lucide icon component.
*/ */
export function file_extension_icon_lucide(extension: string | undefined | null): any { export function file_extension_icon_lucide(
extension: string | undefined | null
): any {
const ext = extension?.toLowerCase() || ''; const ext = extension?.toLowerCase() || '';
const icon_map: Record<string, any> = { const icon_map: Record<string, any> = {
'pdf': Lucide.FileText, pdf: Lucide.FileText,
'doc': Lucide.FileText, doc: Lucide.FileText,
'docx': Lucide.FileText, docx: Lucide.FileText,
'txt': Lucide.FileText, txt: Lucide.FileText,
'rtf': Lucide.FileText, rtf: Lucide.FileText,
'xls': Lucide.FileSpreadsheet, xls: Lucide.FileSpreadsheet,
'xlsx': Lucide.FileSpreadsheet, xlsx: Lucide.FileSpreadsheet,
'csv': Lucide.FileSpreadsheet, csv: Lucide.FileSpreadsheet,
'png': Lucide.FileImage, png: Lucide.FileImage,
'jpg': Lucide.FileImage, jpg: Lucide.FileImage,
'jpeg': Lucide.FileImage, jpeg: Lucide.FileImage,
'gif': Lucide.FileImage, gif: Lucide.FileImage,
'webp': Lucide.FileImage, webp: Lucide.FileImage,
'bmp': Lucide.FileImage, bmp: Lucide.FileImage,
'svg': Lucide.FileImage, svg: Lucide.FileImage,
'mp3': Lucide.FileAudio, mp3: Lucide.FileAudio,
'wav': Lucide.FileAudio, wav: Lucide.FileAudio,
'm4a': Lucide.FileAudio, m4a: Lucide.FileAudio,
'flac': Lucide.FileAudio, flac: Lucide.FileAudio,
'aac': Lucide.FileAudio, aac: Lucide.FileAudio,
'aif': Lucide.FileAudio, aif: Lucide.FileAudio,
'aiff': Lucide.FileAudio, aiff: Lucide.FileAudio,
'mp4': Lucide.FileVideo, mp4: Lucide.FileVideo,
'mkv': Lucide.FileVideo, mkv: Lucide.FileVideo,
'mov': Lucide.FileVideo, mov: Lucide.FileVideo,
'avi': Lucide.FileVideo, avi: Lucide.FileVideo,
'3gp': Lucide.FileVideo, '3gp': Lucide.FileVideo,
'ppt': Lucide.Presentation, ppt: Lucide.Presentation,
'pptx': Lucide.Presentation, pptx: Lucide.Presentation,
'key': Lucide.Presentation, key: Lucide.Presentation,
'odp': Lucide.Presentation, odp: Lucide.Presentation,
'zip': Lucide.FileArchive, zip: Lucide.FileArchive,
'7z': Lucide.FileArchive, '7z': Lucide.FileArchive,
'rar': Lucide.FileArchive, rar: Lucide.FileArchive,
'tar': Lucide.FileArchive, tar: Lucide.FileArchive,
'gz': Lucide.FileArchive, gz: Lucide.FileArchive,
'json': Lucide.FileJson, json: Lucide.FileJson,
'html': Lucide.FileCode, html: Lucide.FileCode,
'htm': Lucide.FileCode, htm: Lucide.FileCode,
'js': Lucide.FileCode, js: Lucide.FileCode,
'ts': Lucide.FileCode, ts: Lucide.FileCode,
'css': Lucide.FileCode, css: Lucide.FileCode,
'php': Lucide.FileCode php: Lucide.FileCode
}; };
return icon_map[ext] || Lucide.File; return icon_map[ext] || Lucide.File;

View File

@@ -12,12 +12,18 @@ export const clean_filename = function clean_filename(
return ''; return '';
} }
const cleaned_filename = filename.replace(unacceptable_chars, replacement_char); const cleaned_filename = filename.replace(
unacceptable_chars,
replacement_char
);
// console.log(cleaned_filename); // console.log(cleaned_filename);
return cleaned_filename; return cleaned_filename;
}; };
export const format_bytes = function format_bytes(bytes: number, decimals: number = 2) { export const format_bytes = function format_bytes(
bytes: number,
decimals: number = 2
) {
if (bytes === 0) return '0 Bytes'; if (bytes === 0) return '0 Bytes';
const k = 1024; const k = 1024;
@@ -30,14 +36,19 @@ export const format_bytes = function format_bytes(bytes: number, decimals: numbe
}; };
// Updated 2024-08-12 // Updated 2024-08-12
export const guess_file_name = function guess_file_name(filename_string: string) { export const guess_file_name = function guess_file_name(
filename_string: string
) {
// console.log('*** guess_file_name() ***'); // console.log('*** guess_file_name() ***');
if (!filename_string) { if (!filename_string) {
return ''; return '';
} }
if (filename_string.includes('.')) { if (filename_string.includes('.')) {
const file_name = filename_string.substring(0, filename_string.lastIndexOf('.')); const file_name = filename_string.substring(
0,
filename_string.lastIndexOf('.')
);
// console.log(file_name); // console.log(file_name);
return file_name; return file_name;
} else { } else {
@@ -46,7 +57,9 @@ export const guess_file_name = function guess_file_name(filename_string: string)
}; };
// Updated 2024-08-12 // Updated 2024-08-12
export const guess_file_extension = function guess_file_extension(filename_string: string) { export const guess_file_extension = function guess_file_extension(
filename_string: string
) {
// console.log('*** guess_file_extension() ***'); // console.log('*** guess_file_extension() ***');
if (!filename_string) { if (!filename_string) {
return ''; return '';
@@ -57,21 +70,27 @@ export const guess_file_extension = function guess_file_extension(filename_strin
} }
const file_extension = const file_extension =
filename_string.substring(filename_string.lastIndexOf('.') + 1, filename_string.length) || filename_string.substring(
filename_string; filename_string.lastIndexOf('.') + 1,
filename_string.length
) || filename_string;
// console.log(file_extension); // console.log(file_extension);
return file_extension; return file_extension;
}; };
// Updated 2024-08-12 // Updated 2024-08-12
export const get_file_hash = async function get_file_hash(file: File): Promise<string> { export const get_file_hash = async function get_file_hash(
file: File
): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const file_reader = new FileReader(); const file_reader = new FileReader();
file_reader.onload = async function () { file_reader.onload = async function () {
const result = file_reader.result; const result = file_reader.result;
if (!result || typeof result === 'string') { if (!result || typeof result === 'string') {
console.log('File was not read completely or is in wrong format'); console.log(
'File was not read completely or is in wrong format'
);
reject('Error reading the file'); reject('Error reading the file');
return; return;
} }
@@ -84,7 +103,9 @@ export const get_file_hash = async function get_file_hash(file: File): Promise<s
const hash_buffer = await crypto.subtle.digest('SHA-256', result); const hash_buffer = await crypto.subtle.digest('SHA-256', result);
const hash_array = Array.from(new Uint8Array(hash_buffer)); const hash_array = Array.from(new Uint8Array(hash_buffer));
const hash_hex = hash_array.map((b) => b.toString(16).padStart(2, '0')).join(''); const hash_hex = hash_array
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
resolve(hash_hex); resolve(hash_hex);
}; };

View File

@@ -10,11 +10,16 @@ export function format_html(html: string): string {
return html return html
.replace(/>\s*</g, '>\n<') // Add newlines between tags .replace(/>\s*</g, '>\n<') // Add newlines between tags
.split('\n') .split('\n')
.map(line => { .map((line) => {
line = line.trim(); line = line.trim();
if (line.match(/<\//)) indent--; // Decrease indent for closing tags if (line.match(/<\//)) indent--; // Decrease indent for closing tags
const out = tab.repeat(Math.max(0, indent)) + line; const out = tab.repeat(Math.max(0, indent)) + line;
if (line.match(/<[^\/!]/) && !line.match(/\/>/) && !line.match(/<\//)) indent++; // Increase indent for opening tags if (
line.match(/<[^\/!]/) &&
!line.match(/\/>/) &&
!line.match(/<\//)
)
indent++; // Increase indent for opening tags
return out; return out;
}) })
.join('\n'); .join('\n');

View File

@@ -9,7 +9,9 @@ export const is_datetime_recent = function is_datetime_recent({
log_lvl?: number; log_lvl?: number;
}) { }) {
if (log_lvl) { if (log_lvl) {
console.log(`*** is_datetime_recent() *** datetime=${datetime} minutes=${minutes}`); console.log(
`*** is_datetime_recent() *** datetime=${datetime} minutes=${minutes}`
);
} }
const now: any = new Date(); const now: any = new Date();

View File

@@ -17,7 +17,10 @@ export const access_level_order = [
* Compares two access levels based on the hierarchy. * Compares two access levels based on the hierarchy.
* @returns 1 if level_a is higher, -1 if level_b is higher, 0 if equal. * @returns 1 if level_a is higher, -1 if level_b is higher, 0 if equal.
*/ */
export const compare_access_levels = function (level_a: string, level_b: string): number { export const compare_access_levels = function (
level_a: string,
level_b: string
): number {
const index_a = access_level_order.indexOf(level_a || 'anonymous'); const index_a = access_level_order.indexOf(level_a || 'anonymous');
const index_b = access_level_order.indexOf(level_b || 'anonymous'); const index_b = access_level_order.indexOf(level_b || 'anonymous');
@@ -30,7 +33,9 @@ export const compare_access_levels = function (level_a: string, level_b: string)
// NOTE: I know there is a better more efficient way to do this, but I don't have time for that right now. // NOTE: I know there is a better more efficient way to do this, but I don't have time for that right now.
// Reminder: super > manager > administrator > trusted > public > authenticated > anonymous // Reminder: super > manager > administrator > trusted > public > authenticated > anonymous
// Super is the highest level. Anonymous is the lowest level. // Super is the highest level. Anonymous is the lowest level.
export const process_permission_checks = function process_permission_checks(access_type: string) { export const process_permission_checks = function process_permission_checks(
access_type: string
) {
// let access_checks = { 'access_type': null, 'super_check': null }; // let access_checks = { 'access_type': null, 'super_check': null };
const access_checks: key_val = {}; const access_checks: key_val = {};

View File

@@ -17,7 +17,9 @@ import type { key_val } from './ae_utils';
*/ */
// Updated 2022-02-11 // Updated 2022-02-11
export const process_data_string = function process_data_string(data_string: string) { export const process_data_string = function process_data_string(
data_string: string
) {
console.log('*** process_data_string() ***'); console.log('*** process_data_string() ***');
// console.log(data_string); // console.log(data_string);
if (!data_string || data_string.length < 1) { if (!data_string || data_string.length < 1) {
@@ -70,14 +72,18 @@ export const process_data_string = function process_data_string(data_string: str
obj['type'] = 'url'; obj['type'] = 'url';
obj['url'] = data_string; obj['url'] = data_string;
} else { } else {
console.log('The unknown data string type was found. Returning the string part.'); console.log(
'The unknown data string type was found. Returning the string part.'
);
const unknown_str = data_string.slice(colon_index + 1); const unknown_str = data_string.slice(colon_index + 1);
console.log(unknown_str); console.log(unknown_str);
obj['str'] = unknown_str; obj['str'] = unknown_str;
} }
} else { } else {
console.log('The data string type was not found. Returning the entire string.'); console.log(
'The data string type was not found. Returning the entire string.'
);
console.log(data_string); console.log(data_string);
obj['qr_type'] = 'UNKNOWN'; obj['qr_type'] = 'UNKNOWN';

View File

@@ -2,8 +2,8 @@ export function return_obj_type_path({
obj_type = null, obj_type = null,
obj_type_prop_name = null obj_type_prop_name = null
}: { }: {
obj_type?: string | null, obj_type?: string | null;
obj_type_prop_name?: string | null obj_type_prop_name?: string | null;
}) { }) {
console.log('*** return_obj_type_path() ***'); console.log('*** return_obj_type_path() ***');
@@ -14,29 +14,69 @@ export function return_obj_type_path({
{ name: 'archive', display: 'Archive', path: 'archive' }, { name: 'archive', display: 'Archive', path: 'archive' },
{ name: 'address', display: 'Address', path: 'address' }, { name: 'address', display: 'Address', path: 'address' },
{ name: 'archive', display: 'Archive', path: 'archive' }, { name: 'archive', display: 'Archive', path: 'archive' },
{ name: 'archive_content', display: 'Archive Content', path: 'archive/content' }, {
name: 'archive_content',
display: 'Archive Content',
path: 'archive/content'
},
{ name: 'contact', display: 'Contact', path: 'contact' }, { name: 'contact', display: 'Contact', path: 'contact' },
{ name: 'data_store', display: 'Data Store', path: 'data_store' }, { name: 'data_store', display: 'Data Store', path: 'data_store' },
{ name: 'event_abstract', display: 'Event Abstract', path: 'event/abstract' }, {
name: 'event_abstract',
display: 'Event Abstract',
path: 'event/abstract'
},
{ name: 'event_badge', display: 'Event Badge', path: 'event/badge' }, { name: 'event_badge', display: 'Event Badge', path: 'event/badge' },
{ name: 'event_device', display: 'Event Device', path: 'event/device' }, { name: 'event_device', display: 'Event Device', path: 'event/device' },
{ name: 'event_exhibit', display: 'Event Exhibit', path: 'event/exhibit' }, {
name: 'event_exhibit',
display: 'Event Exhibit',
path: 'event/exhibit'
},
{ name: 'event_file', display: 'Event File', path: 'event/file' }, { name: 'event_file', display: 'Event File', path: 'event/file' },
{ name: 'event_location', display: 'Event Location', path: 'event/location' }, {
name: 'event_location',
display: 'Event Location',
path: 'event/location'
},
{ name: 'event_person', display: 'Event Person', path: 'event/person' }, { name: 'event_person', display: 'Event Person', path: 'event/person' },
{ name: 'event_presentation', display: 'Event Presentation', path: 'event/' }, {
{ name: 'event_presenter', display: 'Event Presenter', path: 'event/presenter' }, name: 'event_presentation',
{ name: 'event_registration', display: 'Event Registration', path: 'event/registration' }, display: 'Event Presentation',
{ name: 'event_session', display: 'Event Session', path: 'event/session' }, path: 'event/'
},
{
name: 'event_presenter',
display: 'Event Presenter',
path: 'event/presenter'
},
{
name: 'event_registration',
display: 'Event Registration',
path: 'event/registration'
},
{
name: 'event_session',
display: 'Event Session',
path: 'event/session'
},
{ name: 'event', display: 'Event', path: 'event' }, { name: 'event', display: 'Event', path: 'event' },
{ name: 'hosted_file', display: 'Hosted File', path: 'hosted_file' }, { name: 'hosted_file', display: 'Hosted File', path: 'hosted_file' },
{ name: 'journal', display: 'Journal', path: 'journal' }, { name: 'journal', display: 'Journal', path: 'journal' },
{ name: 'journal_entry', display: 'Journal Entry', path: 'journal/entry' }, {
name: 'journal_entry',
display: 'Journal Entry',
path: 'journal/entry'
},
{ name: 'order_line', display: 'Order Line', path: 'order/line' }, { name: 'order_line', display: 'Order Line', path: 'order/line' },
{ name: 'order', display: 'Order', path: 'order' }, { name: 'order', display: 'Order', path: 'order' },
{ name: 'person', display: 'Person', path: 'person' }, { name: 'person', display: 'Person', path: 'person' },
{ name: 'post', display: 'Archive', path: 'post' }, { name: 'post', display: 'Archive', path: 'post' },
{ name: 'post_comment', display: 'Archive Content', path: 'post/comment' }, {
name: 'post_comment',
display: 'Archive Content',
path: 'post/comment'
},
{ name: 'user', display: 'User', path: 'user' } { name: 'user', display: 'User', path: 'user' }
]; ];

View File

@@ -76,7 +76,10 @@ export function set_obj_prop_display_name({
// console.log(known_obj_type_li_dict[i]); // console.log(known_obj_type_li_dict[i]);
if (prop_name.startsWith(known_obj_type_li_dict[i].name)) { if (prop_name.startsWith(known_obj_type_li_dict[i].name)) {
// console.log(`Found ${known_obj_type_li_dict[i].name}`); // console.log(`Found ${known_obj_type_li_dict[i].name}`);
prop_display_name = prop_name.replace(known_obj_type_li_dict[i].name, ''); prop_display_name = prop_name.replace(
known_obj_type_li_dict[i].name,
''
);
break; break;
} }
} }
@@ -101,7 +104,10 @@ export function set_obj_prop_display_name({
// console.log(`Found ${known_obj_type_li_dict[i].name}`); // console.log(`Found ${known_obj_type_li_dict[i].name}`);
found_obj_type = known_obj_type_li_dict[i].name; found_obj_type = known_obj_type_li_dict[i].name;
if (found_obj_type == obj_type) { if (found_obj_type == obj_type) {
prop_display_name = prop_name.replace(known_obj_type_li_dict[i].name, ''); prop_display_name = prop_name.replace(
known_obj_type_li_dict[i].name,
''
);
} }
break; break;

View File

@@ -20,7 +20,8 @@ export function to_title_case(text_string: string) {
array[index - 3] !== ':' && array[index - 3] !== ':' &&
array[index + 1] !== ':' && array[index + 1] !== ':' &&
/* Ignore small words that start a hyphenated phrase */ /* Ignore small words that start a hyphenated phrase */
(array[index + 1] !== '-' || (array[index - 1] === '-' && array[index + 1] === '-')) (array[index + 1] !== '-' ||
(array[index - 1] === '-' && array[index + 1] === '-'))
) { ) {
return current.toLowerCase(); return current.toLowerCase();
} }
@@ -36,9 +37,12 @@ export function to_title_case(text_string: string) {
} }
/* Capitalize the first letter */ /* Capitalize the first letter */
return current.replace(alphanumericPattern, function (match: string) { return current.replace(
return match.toUpperCase(); alphanumericPattern,
}); function (match: string) {
return match.toUpperCase();
}
);
}) })
.join(''); .join('');
} }

View File

@@ -1,5 +1,9 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { compare_access_levels, process_permission_checks, access_level_order } from './ae_utils__perm_checks'; import {
compare_access_levels,
process_permission_checks,
access_level_order
} from './ae_utils__perm_checks';
describe('Permission Hierarchy Tests', () => { describe('Permission Hierarchy Tests', () => {
describe('compare_access_levels', () => { describe('compare_access_levels', () => {
@@ -15,7 +19,9 @@ describe('Permission Hierarchy Tests', () => {
it('should correctly identify downgrades', () => { it('should correctly identify downgrades', () => {
// Low to High should return -1 // Low to High should return -1
expect(compare_access_levels('manager', 'super')).toBe(-1); expect(compare_access_levels('manager', 'super')).toBe(-1);
expect(compare_access_levels('anonymous', 'authenticated')).toBe(-1); expect(compare_access_levels('anonymous', 'authenticated')).toBe(
-1
);
}); });
it('should return 0 for equal levels', () => { it('should return 0 for equal levels', () => {
@@ -25,7 +31,9 @@ describe('Permission Hierarchy Tests', () => {
it('should handle null/empty as anonymous', () => { it('should handle null/empty as anonymous', () => {
expect(compare_access_levels('trusted', '')).toBe(1); expect(compare_access_levels('trusted', '')).toBe(1);
expect(compare_access_levels(null as any, 'authenticated')).toBe(-1); expect(compare_access_levels(null as any, 'authenticated')).toBe(
-1
);
}); });
}); });

View File

@@ -62,7 +62,9 @@ export const get_ae_obj_li_for_lu = async function get_ae_obj_li_for_lu({
log_lvl?: number; log_lvl?: number;
}) { }) {
if (log_lvl) { if (log_lvl) {
console.log(`*** get_ae_obj_li_for_lu() *** for_lu_type=${for_lu_type}`); console.log(
`*** get_ae_obj_li_for_lu() *** for_lu_type=${for_lu_type}`
);
} }
// Pass headers as-is — get_object will auto-promote the real account_id from api_cfg. // Pass headers as-is — get_object will auto-promote the real account_id from api_cfg.
@@ -331,7 +333,9 @@ export const delete_ae_obj_id_crud = async function delete_ae_obj_id_crud({
log_lvl?: number; log_lvl?: number;
}) { }) {
if (log_lvl) { if (log_lvl) {
console.log(`*** delete_ae_obj_id_crud() *** obj_type: ${obj_type} obj_id: ${obj_id}`); console.log(
`*** delete_ae_obj_id_crud() *** obj_type: ${obj_type} obj_id: ${obj_id}`
);
} }
data['super_key'] = key; data['super_key'] = key;
@@ -369,7 +373,6 @@ export const delete_ae_obj_id_crud = async function delete_ae_obj_id_crud({
return object_obj_delete_promise; return object_obj_delete_promise;
}; };
/* BEGIN: Hosted File Related */ /* BEGIN: Hosted File Related */
// Updated 2026-01-07 // Updated 2026-01-07
@@ -473,74 +476,75 @@ export const delete_hosted_file = async function delete_hosted_file({
/* BEGIN: Data Store Related */ /* BEGIN: Data Store Related */
// Updated 2023-06-29 // Updated 2023-06-29
export const get_data_store_obj_w_code = async function get_data_store_obj_w_code({ export const get_data_store_obj_w_code =
api_cfg, async function get_data_store_obj_w_code({
data_store_code, api_cfg,
data_type = 'text', data_store_code,
headers = {}, data_type = 'text',
params = {}, headers = {},
timeout = 25000, params = {},
log_lvl = 0 timeout = 25000,
}: { log_lvl = 0
api_cfg: any; }: {
data_store_code: string; api_cfg: any;
data_type?: string; data_store_code: string;
headers?: key_val; data_type?: string;
params?: key_val; headers?: key_val;
timeout?: number; params?: key_val;
log_lvl?: number; timeout?: number;
}) { log_lvl?: number;
if (log_lvl) { }) {
console.log('*** get_data_store_obj_w_code() ***'); if (log_lvl) {
} console.log('*** get_data_store_obj_w_code() ***');
}
// let get_item_result = window.localStorage.getItem(code); // let get_item_result = window.localStorage.getItem(code);
const endpoint = `/data_store/code/${data_store_code}`; const endpoint = `/data_store/code/${data_store_code}`;
let data_store_obj_get_promise = await api.get_object({ let data_store_obj_get_promise = await api.get_object({
api_cfg: api_cfg, api_cfg: api_cfg,
endpoint: endpoint,
headers: headers,
params: params,
timeout: timeout,
log_lvl: log_lvl
});
if (data_store_obj_get_promise === false) {
console.log('Data Store - RUN AGAIN WITH BACKUP');
const original_api_base_url = api_cfg['base_url'];
const temp_api = api_cfg;
temp_api['base_url'] = temp_api['base_url_bak'];
data_store_obj_get_promise = await api.get_object({
api_cfg: temp_api,
endpoint: endpoint, endpoint: endpoint,
headers: headers, headers: headers,
params: params, params: params,
timeout: timeout, timeout: timeout,
log_lvl: log_lvl log_lvl: log_lvl
}); });
temp_api['base_url'] = original_api_base_url;
}
const data_store_obj = data_store_obj_get_promise; if (data_store_obj_get_promise === false) {
console.log('Data Store - RUN AGAIN WITH BACKUP');
const original_api_base_url = api_cfg['base_url'];
if (data_type == 'text') { const temp_api = api_cfg;
// console.log(data_store_obj.text); temp_api['base_url'] = temp_api['base_url_bak'];
// window.localStorage.setItem(data_store_code, data_store_obj.text);
// localStorage.setItem(data_store_code, data_store_obj.text);
} else if (data_type == 'json') {
// console.log(data_store_obj.json);
// window.localStorage.setItem(data_store_code, JSON.stringify(data_store_obj.json));
// localStorage.setItem(data_store_code, JSON.stringify(data_store_obj.json));
}
if (log_lvl > 1) { data_store_obj_get_promise = await api.get_object({
console.log('Response Data:', data_store_obj); api_cfg: temp_api,
} endpoint: endpoint,
return data_store_obj; headers: headers,
}; params: params,
timeout: timeout,
log_lvl: log_lvl
});
temp_api['base_url'] = original_api_base_url;
}
const data_store_obj = data_store_obj_get_promise;
if (data_type == 'text') {
// console.log(data_store_obj.text);
// window.localStorage.setItem(data_store_code, data_store_obj.text);
// localStorage.setItem(data_store_code, data_store_obj.text);
} else if (data_type == 'json') {
// console.log(data_store_obj.json);
// window.localStorage.setItem(data_store_code, JSON.stringify(data_store_obj.json));
// localStorage.setItem(data_store_code, JSON.stringify(data_store_obj.json));
}
if (log_lvl > 1) {
console.log('Response Data:', data_store_obj);
}
return data_store_obj;
};
/* END: Data Store Related */ /* END: Data Store Related */
/* BEGIN: Utility: Email Related */ /* BEGIN: Utility: Email Related */
@@ -591,8 +595,13 @@ export const send_email = async function send_email({
// Skip email sending entirely when running in a test/Playwright environment. // Skip email sending entirely when running in a test/Playwright environment.
// Set window.__ae_test_mode = true via addInitScript to activate this guard. // Set window.__ae_test_mode = true via addInitScript to activate this guard.
if (typeof globalThis !== 'undefined' && (globalThis as any).__ae_test_mode) { if (
console.log(`[TEST MODE] send_email() suppressed — would have sent to: ${to_email}, subject: ${subject}`); typeof globalThis !== 'undefined' &&
(globalThis as any).__ae_test_mode
) {
console.log(
`[TEST MODE] send_email() suppressed — would have sent to: ${to_email}, subject: ${subject}`
);
return null; return null;
} }
@@ -663,7 +672,7 @@ const obj = {
create_nested_obj: create_nested_obj, create_nested_obj: create_nested_obj,
update_ae_obj: update_ae_obj, update_ae_obj: update_ae_obj,
update_nested_obj: update_nested_obj, update_nested_obj: update_nested_obj,
delete_ae_obj:delete_ae_obj, delete_ae_obj: delete_ae_obj,
delete_nested_ae_obj: delete_nested_ae_obj, delete_nested_ae_obj: delete_nested_ae_obj,
create_ae_obj_crud: create_ae_obj_crud, create_ae_obj_crud: create_ae_obj_crud,
update_ae_obj_id_crud: update_ae_obj_id_crud, update_ae_obj_id_crud: update_ae_obj_id_crud,

View File

@@ -1,295 +1,320 @@
<script lang="ts"> <script lang="ts">
// *** Import Svelte specific // *** Import Svelte specific
import { onDestroy, onMount, tick } from 'svelte'; import { onDestroy, onMount, tick } from 'svelte';
import { afterNavigate } from '$app/navigation'; import { afterNavigate } from '$app/navigation';
// *** Import other supporting libraries // *** Import other supporting libraries
// import { liveQuery } from "dexie"; // import { liveQuery } from "dexie";
import { Lock, LockOpen, RefreshCw, ShieldEllipsis, ShieldMinus, ShieldPlus, ShieldUser, Unlink, User, UserCheck, UserRound, Wand2 } from '@lucide/svelte'; import {
// *** Import Aether specific variables and functions Lock,
import { ae_util } from '$lib/ae_utils/ae_utils'; LockOpen,
import { ae_loc, ae_sess, ae_api, slct, slct_trigger } from '$lib/stores/ae_stores'; RefreshCw,
// import { core_func } from '$lib/ae_core/ae_core_functions'; ShieldEllipsis,
// Ideally the Event related stores should not be imported here? ShieldMinus,
import { events_loc } from '$lib/stores/ae_events_stores'; ShieldPlus,
// import { db_events } from "$lib/db_events"; ShieldUser,
Unlink,
User,
UserCheck,
UserRound,
Wand2
} from '@lucide/svelte';
// *** Import Aether specific variables and functions
import { ae_util } from '$lib/ae_utils/ae_utils';
import {
ae_loc,
ae_sess,
ae_api,
slct,
slct_trigger
} from '$lib/stores/ae_stores';
// import { core_func } from '$lib/ae_core/ae_core_functions';
// Ideally the Event related stores should not be imported here?
import { events_loc } from '$lib/stores/ae_events_stores';
// import { db_events } from "$lib/db_events";
// export let hidden: boolean = false; // export let hidden: boolean = false;
// *** Setup Svelte properties // *** Setup Svelte properties
interface Props { interface Props {
log_lvl?: number; log_lvl?: number;
hide?: null | boolean; hide?: null | boolean;
focus_input: boolean; focus_input: boolean;
expand?: boolean; expand?: boolean;
show_passcode_input: boolean; show_passcode_input: boolean;
trigger_clear_access: null | boolean; trigger_clear_access: null | boolean;
}
let {
log_lvl = $bindable(0),
hide = $bindable(false),
focus_input = $bindable(false),
expand = $bindable(false),
show_passcode_input = $bindable(false),
trigger_clear_access = $bindable(null)
}: Props = $props();
let entered_passcode: null | string = $state(null);
let checked_passcode: null | string = $state(null);
// let password_checked: boolean = $state(false);
// let entered_passcode: null|string = '';
// let show_passcode_input: boolean = $state(false);
// let show_passcode_input: boolean = false;
// let trigger: null|string|boolean = null;
let trigger: null | string | boolean = $state(null);
// const dispatch = createEventDispatcher();
// WARNING: There is a bug (I think) around here related to the entered_passcode not being cleared. There seems to be something different about how Svelte handles state in this component compared to the others. This might be related to the `$effect` or `$derived` usage. Maybe there are conflicting things trying to update the $ae_loc store at the same time.
onMount(() => {
// log_lvl = 2;
if (log_lvl > 1) {
console.log('** Element Mounted: ** Element Access Type');
}
entered_passcode = '';
trigger = null;
// /** @type {HTMLElement | null} */
// const to_focus = document.getElementById('access_passcode_input');
// to_focus?.focus();
});
onDestroy(() => {
if (log_lvl > 1) {
console.log('** Element Destroyed: ** Element Access Type');
} }
let { // Clean up any references or listeners if needed
log_lvl = $bindable(0), entered_passcode = null; // Clear the entered passcode
hide = $bindable(false), show_passcode_input = false;
focus_input = $bindable(false),
expand = $bindable(false),
show_passcode_input = $bindable(false),
trigger_clear_access = $bindable(null)
}: Props = $props();
let entered_passcode: null | string = $state(null); // Reset trigger
let checked_passcode: null | string = $state(null); trigger = null;
// let password_checked: boolean = $state(false); });
// let entered_passcode: null|string = '';
// let show_passcode_input: boolean = $state(false);
// let show_passcode_input: boolean = false;
// let trigger: null|string|boolean = null; // afterNavigate(() => {
let trigger: null | string | boolean = $state(null); // /** @type {HTMLElement | null} */
// const to_focus = document.getElementById('access_passcode_input');
// to_focus?.focus();
// });
// const dispatch = createEventDispatcher(); $effect(() => {
if (
entered_passcode &&
entered_passcode.length >= 5 &&
entered_passcode != checked_passcode
) {
checked_passcode = entered_passcode;
if (log_lvl) {
console.log(`entered_passcode=${entered_passcode}`);
}
handle_check_access_type_passcode();
}
});
// WARNING: There is a bug (I think) around here related to the entered_passcode not being cleared. There seems to be something different about how Svelte handles state in this component compared to the others. This might be related to the `$effect` or `$derived` usage. Maybe there are conflicting things trying to update the $ae_loc store at the same time. $effect(() => {
onMount(() => { if (trigger && $ae_loc.access_type) {
trigger = false;
if (log_lvl) {
console.log(`$ae_loc.access_type=${$ae_loc.access_type}`);
}
let access_checks_results = ae_util.process_permission_checks(
$ae_loc.access_type
);
$ae_loc = { ...$ae_loc, ...access_checks_results };
$ae_loc = $ae_loc;
$ae_loc.sys_menu.expand = false;
} else if (trigger) {
trigger = false;
if (log_lvl) {
console.log(`$ae_loc.access_type=not set`);
}
// Send an empty string to reset the permissions. This is the same as sending 'anonymous'.
let access_checks_results = ae_util.process_permission_checks('');
$ae_loc = { ...$ae_loc, ...access_checks_results };
$ae_loc = $ae_loc;
}
});
// This does not seem to work. I feel like it should though...?
$effect(() => {
if (!hide && focus_input) {
focus_input = false;
// log_lvl = 2; // log_lvl = 2;
// await tick();
// document.getElementById('access_passcode_input')?.focus();
if (log_lvl > 1) { if (log_lvl > 1) {
console.log('** Element Mounted: ** Element Access Type'); console.log('Effect: Setting focus on the passcode input field');
} }
entered_passcode = ''; /** @type {HTMLElement | null} */
trigger = null; const to_focus = document.getElementById('access_passcode_input');
to_focus?.focus();
}
});
// /** @type {HTMLElement | null} */ $effect(() => {
// const to_focus = document.getElementById('access_passcode_input'); if (trigger_clear_access) {
// to_focus?.focus(); trigger_clear_access = false;
}); if (log_lvl) {
console.log(`trigger_clear_access=${trigger_clear_access}`);
onDestroy(() => {
if (log_lvl > 1) {
console.log('** Element Destroyed: ** Element Access Type');
} }
handle_clear_access();
}
});
// Clean up any references or listeners if needed function handle_check_access_type_passcode() {
entered_passcode = null; // Clear the entered passcode if (log_lvl > 1) {
show_passcode_input = false; console.log(
`*** handle_check_access_type_passcode() *** passcode list:`,
$ae_loc.site_access_code_kv
);
}
// Reset trigger // Reminder: super > manager > administrator > trusted > public > authenticated > anonymous
trigger = null;
});
// afterNavigate(() => { if (entered_passcode && entered_passcode.length >= 5) {
// /** @type {HTMLElement | null} */
// const to_focus = document.getElementById('access_passcode_input');
// to_focus?.focus();
// });
$effect(() => {
if ( if (
entered_passcode && $ae_loc.site_access_code_kv.super.length >= 8 &&
entered_passcode.length >= 5 && $ae_loc.site_access_code_kv.super == entered_passcode
entered_passcode != checked_passcode
) { ) {
checked_passcode = entered_passcode; console.log('Super passcode matched');
if (log_lvl) {
console.log(`entered_passcode=${entered_passcode}`);
}
handle_check_access_type_passcode();
}
});
$effect(() => { window.localStorage.setItem('access_type', 'super');
if (trigger && $ae_loc.access_type) {
trigger = false;
if (log_lvl) {
console.log(`$ae_loc.access_type=${$ae_loc.access_type}`);
}
let access_checks_results = ae_util.process_permission_checks($ae_loc.access_type); $ae_loc.access_type = 'super';
} else if (
$ae_loc.site_access_code_kv.manager.length >= 5 &&
$ae_loc.site_access_code_kv.manager == entered_passcode
) {
console.log('Manager passcode matched');
$ae_loc = { ...$ae_loc, ...access_checks_results }; window.localStorage.setItem('access_type', 'manager');
$ae_loc = $ae_loc;
$ae_loc.sys_menu.expand = false;
} else if (trigger) {
trigger = false;
if (log_lvl) {
console.log(`$ae_loc.access_type=not set`);
}
// Send an empty string to reset the permissions. This is the same as sending 'anonymous'. $ae_loc.access_type = 'manager';
let access_checks_results = ae_util.process_permission_checks(''); } else if (
$ae_loc.site_access_code_kv.administrator.length >= 5 &&
$ae_loc.site_access_code_kv.administrator == entered_passcode
) {
console.log('Administrator passcode matched');
$ae_loc = { ...$ae_loc, ...access_checks_results }; window.localStorage.setItem('access_type', 'administrator');
$ae_loc = $ae_loc;
}
});
// This does not seem to work. I feel like it should though...? $ae_loc.access_type = 'administrator';
$effect(() => { } else if (
if (!hide && focus_input) { $ae_loc.site_access_code_kv.trusted.length >= 5 &&
focus_input = false; $ae_loc.site_access_code_kv.trusted == entered_passcode
// log_lvl = 2; ) {
// await tick(); console.log('Trusted passcode matched');
// document.getElementById('access_passcode_input')?.focus();
window.localStorage.setItem('access_type', 'trusted');
$ae_loc.access_type = 'trusted';
} else if (
$ae_loc.site_access_code_kv.public.length >= 5 &&
$ae_loc.site_access_code_kv.public == entered_passcode
) {
console.log('Public passcode matched');
window.localStorage.setItem('access_type', 'public');
$ae_loc.access_type = 'public';
} else if (
$ae_loc.site_access_code_kv.authenticated == entered_passcode
) {
console.log('Authenticated passcode matched');
window.localStorage.setItem('access_type', 'authenticated');
$ae_loc.access_type = 'authenticated';
} else {
if (log_lvl > 1) { if (log_lvl > 1) {
console.log('Effect: Setting focus on the passcode input field'); console.log(
} 'Entered passcode does not match any of the site access codes.'
/** @type {HTMLElement | null} */ );
const to_focus = document.getElementById('access_passcode_input');
to_focus?.focus();
}
});
$effect(() => {
if (trigger_clear_access) {
trigger_clear_access = false;
if (log_lvl) {
console.log(`trigger_clear_access=${trigger_clear_access}`);
}
handle_clear_access();
}
});
function handle_check_access_type_passcode() {
if (log_lvl > 1) {
console.log(
`*** handle_check_access_type_passcode() *** passcode list:`,
$ae_loc.site_access_code_kv
);
}
// Reminder: super > manager > administrator > trusted > public > authenticated > anonymous
if (entered_passcode && entered_passcode.length >= 5) {
if (
$ae_loc.site_access_code_kv.super.length >= 8 &&
$ae_loc.site_access_code_kv.super == entered_passcode
) {
console.log('Super passcode matched');
window.localStorage.setItem('access_type', 'super');
$ae_loc.access_type = 'super';
} else if (
$ae_loc.site_access_code_kv.manager.length >= 5 &&
$ae_loc.site_access_code_kv.manager == entered_passcode
) {
console.log('Manager passcode matched');
window.localStorage.setItem('access_type', 'manager');
$ae_loc.access_type = 'manager';
} else if (
$ae_loc.site_access_code_kv.administrator.length >= 5 &&
$ae_loc.site_access_code_kv.administrator == entered_passcode
) {
console.log('Administrator passcode matched');
window.localStorage.setItem('access_type', 'administrator');
$ae_loc.access_type = 'administrator';
} else if (
$ae_loc.site_access_code_kv.trusted.length >= 5 &&
$ae_loc.site_access_code_kv.trusted == entered_passcode
) {
console.log('Trusted passcode matched');
window.localStorage.setItem('access_type', 'trusted');
$ae_loc.access_type = 'trusted';
} else if (
$ae_loc.site_access_code_kv.public.length >= 5 &&
$ae_loc.site_access_code_kv.public == entered_passcode
) {
console.log('Public passcode matched');
window.localStorage.setItem('access_type', 'public');
$ae_loc.access_type = 'public';
} else if ($ae_loc.site_access_code_kv.authenticated == entered_passcode) {
console.log('Authenticated passcode matched');
window.localStorage.setItem('access_type', 'authenticated');
$ae_loc.access_type = 'authenticated';
} else {
if (log_lvl > 1) {
console.log('Entered passcode does not match any of the site access codes.');
}
if ($ae_loc.access_type != 'anonymous') {
console.log('Access type is not anonymous');
}
// window.localStorage.setItem('access_type', 'anonymous');
// $ae_loc.access_type = 'anonymous';
// trigger = 'process_permission_check';
// $ae_loc = $ae_loc; // Trigger Svelte just in case
// ae_loc.set($ae_loc);
// console.log($ae_loc);
// dispatch_access_type_changed();
return false;
} }
entered_passcode = ''; if ($ae_loc.access_type != 'anonymous') {
show_passcode_input = false; console.log('Access type is not anonymous');
trigger = 'process_permission_check';
$ae_loc.app_cfg.show_element__menu = false;
$ae_loc.app_cfg.show_element__menu_btn = true;
// WARNING 2024-08-21: For some reason the config element does not auto show or hide when the access type changes.
if (!$ae_loc.iframe && $ae_loc.authenticated_access) {
$ae_loc.app_cfg.show_element__access_type = true;
$ae_loc.app_cfg.show_element__cfg = true;
} else if ($ae_loc.iframe && $ae_loc.trusted_access) {
$ae_loc.app_cfg.show_element__access_type = true;
$ae_loc.app_cfg.show_element__cfg = true;
} else {
$ae_loc.app_cfg.show_element__access_type = true;
$ae_loc.app_cfg.show_element__cfg = false;
} }
// window.localStorage.setItem('access_type', 'anonymous');
// $ae_loc.access_type = 'anonymous';
// trigger = 'process_permission_check';
// $ae_loc = $ae_loc; // Trigger Svelte just in case
// ae_loc.set($ae_loc);
// console.log($ae_loc);
// dispatch_access_type_changed(); // dispatch_access_type_changed();
return true; return false;
} else {
if (log_lvl > 1) {
console.log('Entered passcode too short.');
}
// $ae_loc.access_type = null; // 'anonymous';
// dispatch_access_type_changed()
return null;
} }
}
function handle_clear_access() { entered_passcode = '';
// console.log('handle_clear_access()'); show_passcode_input = false;
// NOTE: I think it makes since to reset this to anonymous even if logged in as an admin or similar.
window.localStorage.setItem('access_type', 'anonymous');
// $ae_loc.access_type = null; // 'anonymous';
// Revert back to the user's access type after quick access (temporarily escalate permissions) is turned off.
$ae_loc.access_type = $ae_loc.user_access_type ?? 'anonymous';
trigger = 'process_permission_check'; trigger = 'process_permission_check';
entered_passcode = ''; // Clear the entered passcode
// Reset so the same passcode can be re-entered after clearing.
// Without this, the $effect guard (entered_passcode != checked_passcode) silently
// blocks re-entry of the same passcode until the component remounts (page refresh).
checked_passcode = null;
show_passcode_input = true;
$ae_loc.app_cfg.show_element__menu = false; $ae_loc.app_cfg.show_element__menu = false;
$ae_loc.app_cfg.show_element__menu_btn = true; $ae_loc.app_cfg.show_element__menu_btn = true;
$ae_loc.edit_mode = false; // WARNING 2024-08-21: For some reason the config element does not auto show or hide when the access type changes.
if (!$ae_loc.iframe && $ae_loc.authenticated_access) {
$ae_loc.app_cfg.show_element__access_type = true;
$ae_loc.app_cfg.show_element__cfg = true;
} else if ($ae_loc.iframe && $ae_loc.trusted_access) {
$ae_loc.app_cfg.show_element__access_type = true;
$ae_loc.app_cfg.show_element__cfg = true;
} else {
$ae_loc.app_cfg.show_element__access_type = true;
$ae_loc.app_cfg.show_element__cfg = false;
}
// dispatch_access_type_changed();
return true; return true;
} else {
if (log_lvl > 1) {
console.log('Entered passcode too short.');
}
// $ae_loc.access_type = null; // 'anonymous';
// dispatch_access_type_changed()
return null;
} }
}
function handle_clear_access() {
// console.log('handle_clear_access()');
// NOTE: I think it makes since to reset this to anonymous even if logged in as an admin or similar.
window.localStorage.setItem('access_type', 'anonymous');
// $ae_loc.access_type = null; // 'anonymous';
// Revert back to the user's access type after quick access (temporarily escalate permissions) is turned off.
$ae_loc.access_type = $ae_loc.user_access_type ?? 'anonymous';
trigger = 'process_permission_check';
entered_passcode = ''; // Clear the entered passcode
// Reset so the same passcode can be re-entered after clearing.
// Without this, the $effect guard (entered_passcode != checked_passcode) silently
// blocks re-entry of the same passcode until the component remounts (page refresh).
checked_passcode = null;
show_passcode_input = true;
$ae_loc.app_cfg.show_element__menu = false;
$ae_loc.app_cfg.show_element__menu_btn = true;
$ae_loc.edit_mode = false;
return true;
}
</script> </script>
<section <section
@@ -298,24 +323,23 @@
ae_access_type ae_access_type
hidden-print hidden-print
bg-blue-100 text-gray-900 flex w-full
dark:bg-blue-800 dark:text-gray-200 flex-col flex-wrap
flex flex-col flex-wrap gap-1 items-end justify-center gap-1 border-2
items-end justify-center border-gray-200 bg-blue-100
w-full
p-1 p-1
border-2 border-gray-200 text-gray-900
transition-all delay-150
duration-300 delay-150 hover:delay-1000 hover:ease-out duration-300 hover:delay-1000 hover:ease-out dark:bg-blue-800
transition-all dark:text-gray-200
" "
class:hidden={hide} class:hidden={hide}>
>
<!-- class:hidden={!$ae_sess.show__sign_in_out__fields} --> <!-- class:hidden={!$ae_sess.show__sign_in_out__fields} -->
<header class="ae_header hidden"> <header class="ae_header hidden">
<h2 class="text-sm text-center font-semibold">Passcode Sign In</h2> <h2 class="text-center text-sm font-semibold">Passcode Sign In</h2>
</header> </header>
<!-- Show list of authorized sessions and presentations for a user --> <!-- Show list of authorized sessions and presentations for a user -->
@@ -349,9 +373,8 @@
// tick(); // tick();
return false; return false;
}} }}
class="btn btn-sm preset-tonal-success border border-success-500 hover:preset-filled-success-500 transition-all hover:transition-all *:hover:inline" class="btn btn-sm preset-tonal-success border-success-500 hover:preset-filled-success-500 border transition-all hover:transition-all *:hover:inline"
title="Syncing the local configuration with the remote configuration." title="Syncing the local configuration with the remote configuration.">
>
<RefreshCw size="1em" class="m-1" /> <RefreshCw size="1em" class="m-1" />
<span class="hidden"> Sync </span> <span class="hidden"> Sync </span>
</button> </button>
@@ -369,9 +392,8 @@
// tick(); // tick();
return true; return true;
}} }}
class="btn btn-sm preset-tonal-warning border border-warning-500 hover:preset-filled-warning-500 transition-all hover:transition-all *:hover:inline" class="btn btn-sm preset-tonal-warning border-warning-500 hover:preset-filled-warning-500 border transition-all hover:transition-all *:hover:inline"
title="Currently not syncing with the remote server. Re-sync the local configuration with the remote configuration?" title="Currently not syncing with the remote server. Re-sync the local configuration with the remote configuration?">
>
<Unlink size="1em" class="m-1" /> <Unlink size="1em" class="m-1" />
<span class="hidden"> Re-sync? </span> <span class="hidden"> Re-sync? </span>
</button> </button>
@@ -411,7 +433,8 @@
{/if} {/if}
</div> </div>
<div class="flex flex-row flex-wrap gap-1 items-end justify-end w-full transition-all"> <div
class="flex w-full flex-row flex-wrap items-end justify-end gap-1 transition-all">
{#if $ae_loc?.access_type && $ae_loc?.access_type == 'anonymous'} {#if $ae_loc?.access_type && $ae_loc?.access_type == 'anonymous'}
<span> <span>
<button <button
@@ -421,15 +444,16 @@
show_passcode_input = !show_passcode_input; show_passcode_input = !show_passcode_input;
}} }}
class="btn btn-sm preset-tonal-success hover:preset-filled-warning-500 access_type_unlock_btn transition-all" class="btn btn-sm preset-tonal-success hover:preset-filled-warning-500 access_type_unlock_btn transition-all"
title={show_passcode_input ? 'Cancel passcode entry' : 'Enter a passcode to unlock access'} title={show_passcode_input
> ? 'Cancel passcode entry'
: 'Enter a passcode to unlock access'}>
{#if show_passcode_input} {#if show_passcode_input}
<LockOpen size="1em" class="mx-1" /> <LockOpen size="1em" class="mx-1" />
<span>Cancel</span> <span>Cancel</span>
{:else} {:else}
<Lock size="1em" class="mx-1" /> <Lock size="1em" class="mx-1" />
<span class="lock_icon">Locked</span> <span class="lock_icon">Locked</span>
<LockOpen size="1em" class="mx-1 unlock_icon hidden" /> <LockOpen size="1em" class="unlock_icon mx-1 hidden" />
<span class="unlock_text">Access?</span> <span class="unlock_text">Access?</span>
{/if} {/if}
</button> </button>
@@ -437,14 +461,13 @@
{/if} {/if}
{#if $ae_loc?.access_type && $ae_loc?.access_type != 'anonymous'} {#if $ae_loc?.access_type && $ae_loc?.access_type != 'anonymous'}
<span class="flex flex-row gap-1 items-center justify-center"> <span class="flex flex-row items-center justify-center gap-1">
<!-- <span class="fas fa-unlock mx-1"></span> --> <!-- <span class="fas fa-unlock mx-1"></span> -->
<ShieldPlus class="inline-block" /> <ShieldPlus class="inline-block" />
<span <span
class="*:hover:inline" class="*:hover:inline"
title={`Current access type/level: ${$ae_loc.access_type}`} title={`Current access type/level: ${$ae_loc.access_type}`}>
>
{#if $ae_loc.access_type == 'super'} {#if $ae_loc.access_type == 'super'}
<Wand2 size="1em" class="m-1" /> <Wand2 size="1em" class="m-1" />
<span class="hidden">Super</span> <span class="hidden">Super</span>
@@ -478,9 +501,8 @@
// trigger_clear_access = true; // trigger_clear_access = true;
show_passcode_input = true; show_passcode_input = true;
}} }}
class="btn btn-sm variant-outline-surface hover:preset-tonal-warning border border-warning-500 transition-all" class="btn btn-sm variant-outline-surface hover:preset-tonal-warning border-warning-500 border transition-all"
title={`Current user access level: "${$ae_loc.user_access_type}". Click use passcode for a different access level.`} title={`Current user access level: "${$ae_loc.user_access_type}". Click use passcode for a different access level.`}>
>
<!-- <span class="fas fa-lock mx-1"></span> --> <!-- <span class="fas fa-lock mx-1"></span> -->
<!-- <ShieldMinus /> --> <!-- <ShieldMinus /> -->
<ShieldEllipsis class="inline-block" /> <ShieldEllipsis class="inline-block" />
@@ -495,9 +517,8 @@
show_passcode_input = true; show_passcode_input = true;
// show_passcode_input = true; // show_passcode_input = true;
}} }}
class="btn btn-sm variant-outline-warning hover:preset-tonal-warning border border-warning-500 transition-all" class="btn btn-sm variant-outline-warning hover:preset-tonal-warning border-warning-500 border transition-all"
title={`Current access level: "${$ae_loc.access_type}". Click to clear the temporary access level.`} title={`Current access level: "${$ae_loc.access_type}". Click to clear the temporary access level.`}>
>
<!-- <span class="fas fa-lock mx-1"></span> --> <!-- <span class="fas fa-lock mx-1"></span> -->
<ShieldMinus class="inline-block" /> <ShieldMinus class="inline-block" />
Clear? Clear?
@@ -507,7 +528,8 @@
{/if} {/if}
{#if show_passcode_input} {#if show_passcode_input}
<span class="flex flex-row gap-1 items-center justify-between w-full"> <span
class="flex w-full flex-row items-center justify-between gap-1">
<span> <span>
<ShieldEllipsis class="inline-block text-gray-500" /> <ShieldEllipsis class="inline-block text-gray-500" />
<span class="unlock_text text-sm">Passcode:</span> <span class="unlock_text text-sm">Passcode:</span>
@@ -521,8 +543,7 @@
class:hidden={!show_passcode_input} class:hidden={!show_passcode_input}
type="text" type="text"
placeholder="Passcode" placeholder="Passcode"
autofocus={show_passcode_input} autofocus={show_passcode_input} />
/>
<!-- <div class="current_text transition-all">{$ae_loc.access_type}</div> --> <!-- <div class="current_text transition-all">{$ae_loc.access_type}</div> -->
</span> </span>
{/if} {/if}
@@ -530,8 +551,8 @@
</section> </section>
<style lang="scss"> <style lang="scss">
/* BEGIN: AE's Svelte Quick Access Type component */ /* BEGIN: AE's Svelte Quick Access Type component */
/* /*
#xxx-AE-Quick-Access-Type { #xxx-AE-Quick-Access-Type {
// position: absolute; // position: absolute;
position: fixed; position: fixed;
@@ -569,7 +590,7 @@
} }
*/ */
/* /*
#xxx-AE-Quick-Access-Type:hover { #xxx-AE-Quick-Access-Type:hover {
border-top: solid thin hsla(0,0%,0%,.95); border-top: solid thin hsla(0,0%,0%,.95);
@@ -586,21 +607,21 @@
} }
*/ */
/* #Access-Type .unlock_text { /* #Access-Type .unlock_text {
transition: width 2s, height 2s, background-color 2s, transform 2s; transition: width 2s, height 2s, background-color 2s, transform 2s;
} */ } */
/* END: Svelte Access Type component */ /* END: Svelte Access Type component */
.access_type_unlock_btn .unlock_text { .access_type_unlock_btn .unlock_text {
display: none; display: none;
} }
.access_type_unlock_btn:hover .unlock_text { .access_type_unlock_btn:hover .unlock_text {
display: initial; display: initial;
/* outline: solid thin red; */ /* outline: solid thin red; */
} }
/* /*
.ae_access_type .current_text { .ae_access_type .current_text {
display: none; display: none;
} }

View File

@@ -1,44 +1,54 @@
<script lang="ts" module> <script lang="ts" module>
declare global { declare global {
interface Window { interface Window {
dataLayer: any[]; dataLayer: any[];
gtag: (...args: any[]) => void; gtag: (...args: any[]) => void;
}
} }
}
</script> </script>
<script lang="ts"> <script lang="ts">
import { browser } from '$app/environment'; import { browser } from '$app/environment';
interface Props { interface Props {
log_lvl?: number; log_lvl?: number;
site_google_tracking_id?: string; site_google_tracking_id?: string;
} }
let { log_lvl = 0, site_google_tracking_id = $bindable('') }: Props = $props(); let { log_lvl = 0, site_google_tracking_id = $bindable('') }: Props = $props();
$effect(() => { $effect(() => {
if (browser && site_google_tracking_id) { if (browser && site_google_tracking_id) {
if (log_lvl) { if (log_lvl) {
console.log(`AE Analytics: site_google_tracking_id = `, site_google_tracking_id); console.log(
} `AE Analytics: site_google_tracking_id = `,
site_google_tracking_id
);
}
window.dataLayer = window.dataLayer || []; window.dataLayer = window.dataLayer || [];
window.gtag = window.gtag || function gtag() { window.gtag =
window.gtag ||
function gtag() {
window.dataLayer.push(arguments); window.dataLayer.push(arguments);
}; };
window.gtag('js', new Date()); window.gtag('js', new Date());
window.gtag('config', site_google_tracking_id); window.gtag('config', site_google_tracking_id);
if (log_lvl) { if (log_lvl) {
console.log(`AE Analytics: Google Analytics Tracking ID = `, site_google_tracking_id); console.log(
} `AE Analytics: Google Analytics Tracking ID = `,
site_google_tracking_id
);
} }
}); }
});
</script> </script>
<svelte:head> <svelte:head>
{#if site_google_tracking_id} {#if site_google_tracking_id}
<script async src="https://www.googletagmanager.com/gtag/js?id={site_google_tracking_id}"></script> <script
async
src="https://www.googletagmanager.com/gtag/js?id={site_google_tracking_id}"></script>
{/if} {/if}
</svelte:head> </svelte:head>

View File

@@ -1,103 +1,120 @@
<script lang="ts"> <script lang="ts">
import { Code, Eraser, LockOpen, RefreshCw, Settings, ShieldCheck, ShieldUser, Trash2, UserRound, Users } from '@lucide/svelte'; import {
import { ae_util } from '$lib/ae_utils/ae_utils'; Code,
import { ae_loc, ae_sess, ae_api, slct, slct_trigger } from '$lib/stores/ae_stores'; Eraser,
import E_app_url_builder from '$lib/app_components/e_app_url_builder.svelte'; LockOpen,
RefreshCw,
Settings,
ShieldCheck,
ShieldUser,
Trash2,
UserRound,
Users
} from '@lucide/svelte';
import { ae_util } from '$lib/ae_utils/ae_utils';
import {
ae_loc,
ae_sess,
ae_api,
slct,
slct_trigger
} from '$lib/stores/ae_stores';
import E_app_url_builder from '$lib/app_components/e_app_url_builder.svelte';
// import Element_theme from '$lib/e_app_theme.svelte'; // import Element_theme from '$lib/e_app_theme.svelte';
let notes: null | string = null; let notes: null | string = null;
let all: boolean = false; let all: boolean = false;
interface Props { interface Props {
log_lvl?: number; log_lvl?: number;
hide?: null | boolean; hide?: null | boolean;
expand?: boolean; expand?: boolean;
// export let theme_mode: null|string = null; // export let theme_mode: null|string = null;
// set_theme_mode?: null|boolean; // set_theme_mode?: null|boolean;
// export let theme_name: null|string = null; // export let theme_name: null|string = null;
// set_theme_name?: null|boolean; // set_theme_name?: null|boolean;
} }
let { let {
log_lvl = $bindable(0), log_lvl = $bindable(0),
hide = $bindable(false), hide = $bindable(false),
expand = $bindable(false) expand = $bindable(false)
// set_theme_mode = null, // set_theme_mode = null,
// set_theme_name = null // set_theme_name = null
}: Props = $props(); }: Props = $props();
// const dispatch = createEventDispatcher(); // const dispatch = createEventDispatcher();
// onMount(() => { // onMount(() => {
// // console.log('** Element Mounted: ** Element App Config'); // // console.log('** Element Mounted: ** Element App Config');
// if (set_theme_mode) { // if (set_theme_mode) {
// $slct_trigger = 'set_theme_mode'; // $slct_trigger = 'set_theme_mode';
// } // }
// if (set_theme_name) { // if (set_theme_name) {
// $slct_trigger = 'set_theme_name'; // $slct_trigger = 'set_theme_name';
// } // }
// }); // });
// $: if ($slct_trigger == 'set_theme_mode' && $ae_loc?.app_cfg?.theme_mode) { // $: if ($slct_trigger == 'set_theme_mode' && $ae_loc?.app_cfg?.theme_mode) {
// console.log(`$ae_loc.app_cfg.theme_mode=${$ae_loc?.app_cfg?.theme_mode}`); // console.log(`$ae_loc.app_cfg.theme_mode=${$ae_loc?.app_cfg?.theme_mode}`);
// $slct_trigger = null; // $slct_trigger = null;
// if ($ae_loc.app_cfg.theme_mode == 'light') { // if ($ae_loc.app_cfg.theme_mode == 'light') {
// document.documentElement.classList.remove('dark'); // document.documentElement.classList.remove('dark');
// document.documentElement.classList.add('light'); // document.documentElement.classList.add('light');
// } else if ($ae_loc.app_cfg.theme_mode == 'dark') { // } else if ($ae_loc.app_cfg.theme_mode == 'dark') {
// document.documentElement.classList.remove('light'); // document.documentElement.classList.remove('light');
// document.documentElement.classList.add('dark'); // document.documentElement.classList.add('dark');
// } // }
// } // }
// $: if ($slct_trigger == 'set_theme_name' && $ae_loc?.app_cfg?.theme_name) { // $: if ($slct_trigger == 'set_theme_name' && $ae_loc?.app_cfg?.theme_name) {
// console.log(`$ae_loc?.app_cfg?.theme_name=${$ae_loc?.app_cfg?.theme_name}`); // console.log(`$ae_loc?.app_cfg?.theme_name=${$ae_loc?.app_cfg?.theme_name}`);
// $slct_trigger = null; // $slct_trigger = null;
// // Update the body attribute named "data-theme" to the current theme name. // // Update the body attribute named "data-theme" to the current theme name.
// document.body.setAttribute('data-theme', $ae_loc?.app_cfg?.theme_name); // document.body.setAttribute('data-theme', $ae_loc?.app_cfg?.theme_name);
// } // }
// $: if (entered_passcode && entered_passcode.length >= 5) { // $: if (entered_passcode && entered_passcode.length >= 5) {
// console.log(`entered_passcode=${entered_passcode}`); // console.log(`entered_passcode=${entered_passcode}`);
// handle_check_access_type_passcode(); // handle_check_access_type_passcode();
// } // }
// $: if (trigger && $ae_loc.access_type) { // $: if (trigger && $ae_loc.access_type) {
// console.log(`$ae_loc.access_type=${$ae_loc.access_type}`); // console.log(`$ae_loc.access_type=${$ae_loc.access_type}`);
// let access_checks_results = ae_util.process_permission_checks($ae_loc.access_type); // let access_checks_results = ae_util.process_permission_checks($ae_loc.access_type);
// $ae_loc = {...$ae_loc, ...access_checks_results}; // $ae_loc = {...$ae_loc, ...access_checks_results};
// } else if (trigger) { // } else if (trigger) {
// console.log(`$ae_loc.access_type=not set`); // console.log(`$ae_loc.access_type=not set`);
// // Send an empty string to reset the permissions. This is the same as sending 'anonymous'. // // Send an empty string to reset the permissions. This is the same as sending 'anonymous'.
// let access_checks_results = ae_util.process_permission_checks(''); // let access_checks_results = ae_util.process_permission_checks('');
// $ae_loc = {...$ae_loc, ...access_checks_results}; // $ae_loc = {...$ae_loc, ...access_checks_results};
// } // }
function handle_something() { function handle_something() {
// console.log('*** handle_something() ***'); // console.log('*** handle_something() ***');
} }
function handle_clear_storage(item: null | string) { function handle_clear_storage(item: null | string) {
// console.log('*** handle_clear_storage() ***'); // console.log('*** handle_clear_storage() ***');
// window.localStorage.setItem('access_type', 'anonymous'); // window.localStorage.setItem('access_type', 'anonymous');
// return true; // return true;
} }
// function dispatch_something_changed() { // function dispatch_something_changed() {
// console.log('*** dispatch_something_changed() ***'); // console.log('*** dispatch_something_changed() ***');
// console.log(ae_util); // console.log(ae_util);
// console.log($ae_loc); // console.log($ae_loc);
// dispatch('access_type_changed', { // dispatch('access_type_changed', {
// access_type: $ae_loc.access_type // access_type: $ae_loc.access_type
// }); // });
// } // }
</script> </script>
<!-- transition duration-500 delay-1000 hover:duration-500 hover:delay-1000 hover:transition-all --> <!-- transition duration-500 delay-1000 hover:duration-500 hover:delay-1000 hover:transition-all -->
@@ -107,30 +124,28 @@
ae_app_cfg ae_app_cfg
hidden-print hidden-print
bg-blue-100 text-gray-900 flex w-72
dark:bg-blue-800 dark:text-gray-200 max-w-72 flex-col
flex flex-col flex-wrap gap-1 flex-wrap items-end justify-center gap-1
items-end justify-center
w-72 max-w-72
p-1
border-2 border-gray-200 border-2 border-gray-200
duration-300 delay-150 hover:delay-1000 hover:ease-out bg-blue-100 p-1
transition-all text-gray-900
transition-all delay-150
duration-300 hover:delay-1000 hover:ease-out dark:bg-blue-800
dark:text-gray-200
" "
class:hidden={hide} class:hidden={hide}>
>
<header class:hidden={!expand} class="ae_header w-full"> <header class:hidden={!expand} class="ae_header w-full">
<h2 class="text-sm text-center font-semibold">Config</h2> <h2 class="text-center text-sm font-semibold">Config</h2>
</header> </header>
<div <div
class="ae_cfg_content text-xs space-y-4 my-4" class="ae_cfg_content my-4 space-y-4 text-xs"
class:hidden={!expand} class:hidden={!expand}
data-sveltekit-preload-data="false" data-sveltekit-preload-data="false">
>
<section class="space-y-2"> <section class="space-y-2">
<div> <div>
<h2 class="strong">Access Type:</h2> <h2 class="strong">Access Type:</h2>
@@ -172,12 +187,16 @@
</a> </a>
{#if $ae_loc.iframe} {#if $ae_loc.iframe}
<a class="btn btn-sm preset-tonal-secondary" href="/?iframe=false"> <a
class="btn btn-sm preset-tonal-secondary"
href="/?iframe=false">
<Code size="1em" class="mx-1" /> <Code size="1em" class="mx-1" />
Exit iframe Mode Exit iframe Mode
</a> </a>
{:else} {:else}
<a class="btn btn-sm preset-tonal-secondary" href="/?iframe=true"> <a
class="btn btn-sm preset-tonal-secondary"
href="/?iframe=true">
<Code size="1em" class="mx-1" /> <Code size="1em" class="mx-1" />
Use iframe Mode Use iframe Mode
</a> </a>
@@ -190,8 +209,7 @@
onclick={() => { onclick={() => {
// $ae_loc. // $ae_loc.
window.location.reload(); window.location.reload();
}} }}>
>
<RefreshCw size="1em" class="mx-1" /> <RefreshCw size="1em" class="mx-1" />
Reload & Reload &
<Trash2 size="1em" class="mx-1" /> <Trash2 size="1em" class="mx-1" />
@@ -219,8 +237,7 @@
window.location.reload(); window.location.reload();
// alert('Local and Session Storage cleared and Indexed DBs deleted. You will probably want to refresh the page.'); // alert('Local and Session Storage cleared and Indexed DBs deleted. You will probably want to refresh the page.');
}} }}>
>
<Eraser size="1em" class="mx-1" /> <Eraser size="1em" class="mx-1" />
Clear Storage & DB Clear Storage & DB
</button> </button>
@@ -235,7 +252,7 @@
<!-- class:justify-between={expand} <!-- class:justify-between={expand}
class:justify-end={!expand} --> class:justify-end={!expand} -->
<div class="flex flex-row gap-2 items-center justify-between w-full"> <div class="flex w-full flex-row items-center justify-between gap-2">
<!-- {#if !expand} --> <!-- {#if !expand} -->
<span> <span>
{#if $ae_loc.access_type && $ae_loc.access_type != 'anonymous'} {#if $ae_loc.access_type && $ae_loc.access_type != 'anonymous'}
@@ -270,14 +287,13 @@ class:justify-end={!expand} -->
<button <button
class=" class="
ae_cfg_btn ae_cfg_btn
btn btn-sm text-sm btn btn-sm preset-tonal-warning
preset-tonal-warning group
group transition-all text-sm transition-all
" "
onclick={() => { onclick={() => {
expand = !expand; expand = !expand;
}} }}>
>
<!-- <span class="fas fa-cog m-1"></span> --> <!-- <span class="fas fa-cog m-1"></span> -->
<span class="inline-block" title="Settings"> <span class="inline-block" title="Settings">
<Settings class="m-1" /> <Settings class="m-1" />
@@ -287,8 +303,7 @@ class:justify-end={!expand} -->
cfg_text cfg_text
hidden hidden
group-hover:inline group-hover:inline
" ">
>
Settings Settings
</span> </span>
</button> </button>
@@ -296,21 +311,21 @@ class:justify-end={!expand} -->
</section> </section>
<style lang="postcss"> <style lang="postcss">
.ae_cfg_btn .cfg_text { .ae_cfg_btn .cfg_text {
/* display: none; */ /* display: none; */
} }
.ae_cfg_btn:hover .cfg_text { .ae_cfg_btn:hover .cfg_text {
/* display: initial; */ /* display: initial; */
/* outline: solid thin red; */ /* outline: solid thin red; */
} }
/* .access_type .current_text { /* .access_type .current_text {
display: none; display: none;
} */ } */
/* .access_type:hover .current_text { /* .access_type:hover .current_text {
display: initial; display: initial;
} */ } */
/* END: AE's Svelte App Config component */ /* END: AE's Svelte App Config component */
</style> </style>

View File

@@ -1,73 +1,73 @@
<script lang="ts"> <script lang="ts">
interface Props { interface Props {
children?: import('svelte').Snippet; children?: import('svelte').Snippet;
log_lvl?: number; log_lvl?: number;
value: any; value: any;
success?: boolean; success?: boolean;
btn_text?: string; btn_text?: string;
btn_title?: string; btn_title?: string;
btn_class?: string; btn_class?: string;
hide_icon?: boolean; hide_icon?: boolean;
hide_text?: boolean; hide_text?: boolean;
icon_name?: string; icon_name?: string;
}
let {
children,
log_lvl = 0,
value = $bindable(''),
success = $bindable(false),
btn_text = 'Copy to Clipboard',
btn_title = 'Copy to Clipboard',
btn_class = 'btn btn-sm preset-tonal-warning text-warning-500 m-1',
hide_icon = false,
hide_text = false,
icon_name = 'copy' // copy, check, link
}: Props = $props();
// *** Import Svelte specific
// import { browser } from '$app/environment';
// *** Import other supporting libraries
import {
// ArrowBigRight,
// CircleX,
CircleCheck,
Copy,
// Eye, EyeOff,
// Key,
Link
// LogIn, LogOut, LockKeyhole,
// Mail, MailCheck,
// Menu,
// RefreshCw, RefreshCcw, RefreshCcwDot,
// ShieldEllipsis, ShieldMinus, ShieldPlus, ShieldUser,
// User, UserCheck
} from '@lucide/svelte';
$effect(() => {
if (log_lvl) {
console.log(`Clipboard component initialized with value:`, value);
} }
});
let { // Select your trigger element
children, // const elemButton: HTMLButtonElement | null = document.querySelector('[data-button]');
log_lvl = 0,
value = $bindable(''),
success = $bindable(false),
btn_text = 'Copy to Clipboard',
btn_title = 'Copy to Clipboard',
btn_class = 'btn btn-sm preset-tonal-warning text-warning-500 m-1',
hide_icon = false,
hide_text = false,
icon_name = 'copy' // copy, check, link
}: Props = $props();
// *** Import Svelte specific // Add a click event handler to the trigger
// import { browser } from '$app/environment'; // elemButton?.addEventListener('click', () => {
// // Call the Clipboard API
// *** Import other supporting libraries // navigator.clipboard
import { // // Use the `writeText` method write content to the clipboard
// ArrowBigRight, // .writeText(value)
// CircleX, // // Handle confirmation
CircleCheck, // .then(() => {
Copy, // if (log_lvl) {
// Eye, EyeOff, // console.log(`Clipboard write successful: ${value}`);
// Key, // }
Link // success = true;
// LogIn, LogOut, LockKeyhole, // });
// Mail, MailCheck, // });
// Menu,
// RefreshCw, RefreshCcw, RefreshCcwDot,
// ShieldEllipsis, ShieldMinus, ShieldPlus, ShieldUser,
// User, UserCheck
} from '@lucide/svelte';
$effect(() => {
if (log_lvl) {
console.log(`Clipboard component initialized with value:`, value);
}
});
// Select your trigger element
// const elemButton: HTMLButtonElement | null = document.querySelector('[data-button]');
// Add a click event handler to the trigger
// elemButton?.addEventListener('click', () => {
// // Call the Clipboard API
// navigator.clipboard
// // Use the `writeText` method write content to the clipboard
// .writeText(value)
// // Handle confirmation
// .then(() => {
// if (log_lvl) {
// console.log(`Clipboard write successful: ${value}`);
// }
// success = true;
// });
// });
</script> </script>
<button <button
@@ -93,15 +93,20 @@
// } // }
}} }}
class={btn_class} class={btn_class}
title={btn_title} title={btn_title}>
>
<!-- {@render btn_text} --> <!-- {@render btn_text} -->
{#if icon_name === 'link'} {#if icon_name === 'link'}
<Link class="mx-1 {hide_icon ? 'hidden' : 'inline-block'}" size="1.2em" /> <Link
class="mx-1 {hide_icon ? 'hidden' : 'inline-block'}"
size="1.2em" />
{:else if icon_name === 'check'} {:else if icon_name === 'check'}
<CircleCheck class="mx-1 {hide_icon ? 'hidden' : 'inline-block'}" size="1.2em" /> <CircleCheck
class="mx-1 {hide_icon ? 'hidden' : 'inline-block'}"
size="1.2em" />
{:else} {:else}
<Copy class="mx-1 {hide_icon ? 'hidden' : 'inline-block'}" size="1.2em" /> <Copy
class="mx-1 {hide_icon ? 'hidden' : 'inline-block'}"
size="1.2em" />
{/if} {/if}
<span class={hide_text ? 'hidden' : 'inline-block'}> <span class={hide_text ? 'hidden' : 'inline-block'}>
{btn_text} {btn_text}

View File

@@ -1,25 +1,31 @@
<script lang="ts"> <script lang="ts">
// *** Import Svelte specific // *** Import Svelte specific
// *** Import other supporting libraries // *** Import other supporting libraries
import { Bug, CircleX, Info, ToggleLeft, ToggleRight, X } from '@lucide/svelte'; import { Bug, CircleX, Info, ToggleLeft, ToggleRight, X } from '@lucide/svelte';
// *** Import Aether specific variables and functions // *** Import Aether specific variables and functions
// import { ae_util } from '$lib/ae_utils/ae_utils'; // import { ae_util } from '$lib/ae_utils/ae_utils';
import { ae_loc, ae_sess, ae_api, slct, slct_trigger } from '$lib/stores/ae_stores'; import {
ae_loc,
ae_sess,
ae_api,
slct,
slct_trigger
} from '$lib/stores/ae_stores';
// *** Setup Svelte properties // *** Setup Svelte properties
interface Props { interface Props {
log_lvl?: number; log_lvl?: number;
hide?: null | boolean; hide?: null | boolean;
expand?: boolean; expand?: boolean;
} }
let { let {
log_lvl = $bindable(0), log_lvl = $bindable(0),
hide = $bindable(false), hide = $bindable(false),
expand = $bindable(false) expand = $bindable(false)
}: Props = $props(); }: Props = $props();
</script> </script>
<!-- App Debug Menu --> <!-- App Debug Menu -->
@@ -31,28 +37,28 @@ hover:opacity-100 -->
ae_app__debug_menu ae_app__debug_menu
hidden-print hidden-print
z-50 md:text-md
absolute bottom-0 left-0 lg:text-md xl:text-md transition-delay-1000
flex
text-sm sm:text-sm md:text-md lg:text-md xl:text-md 2xl:text-lg
dark:text-slate-400 dark:hover:text-slate-200
bg-red-100/60 dark:bg-red-800/50
hover:bg-red-200 hover:dark:bg-red-900
mx-1 my-2
max-h-min
max-w-lg
transition-all
transition-delay-1000
transition-duration-1000 transition-duration-1000
absolute bottom-0 left-0 z-50 mx-1 my-2
flex max-h-min
max-w-lg border-2
border-red-300 bg-red-100/60
text-sm transition-all
ease-in ease-in
hover:border-red-500
hover:bg-red-200
sm:text-sm
2xl:text-lg
dark:border-red-700
border-2 dark:bg-red-800/50
border-red-300 dark:border-red-700 dark:text-slate-400 hover:dark:border-red-500
hover:border-red-500 hover:dark:border-red-500 hover:dark:bg-red-900 dark:hover:text-slate-200
" "
class:top-0={expand} class:top-0={expand}
class:w-full={expand} class:w-full={expand}
@@ -60,29 +66,27 @@ hover:opacity-100 -->
class:border-transparent={!expand} class:border-transparent={!expand}
class:dark:border-transparent={!expand} class:dark:border-transparent={!expand}
class:hover:border-transparent={!expand} class:hover:border-transparent={!expand}
class:hover:bg-transparent={!expand} class:hover:bg-transparent={!expand}>
>
<div <div
class:hidden={!expand} class:hidden={!expand}
class:border-red-200={expand} class:border-red-200={expand}
class:dark:border-red-800={expand} class:dark:border-red-800={expand}
class=" class="
transition-all
transition-delay-1000 transition-delay-1000
transition-duration-1000 transition-duration-1000
ease-in
overflow-y-auto
px-1 py-4
opacity-50
hover:opacity-100
relative relative
" overflow-y-auto
> px-1
py-4 opacity-50
transition-all
ease-in
hover:opacity-100
">
<!-- flex flex-col items-center justify-center max-h-full outline --> <!-- flex flex-col items-center justify-center max-h-full outline -->
<div> <div>
<!-- <span class="fas fa-bug mx-1"></span> --> <!-- <span class="fas fa-bug mx-1"></span> -->
<Bug class="inline-block mx-1" /> <Bug class="mx-1 inline-block" />
<span>Debug</span> <span>Debug</span>
</div> </div>
@@ -91,7 +95,8 @@ hover:opacity-100 -->
</pre> </pre>
</div> </div>
<span class="absolute top-0 right-0 flex flex-row gap-1 items-center justify-center"> <span
class="absolute top-0 right-0 flex flex-row items-center justify-center gap-1">
<button <button
type="button" type="button"
onclick={() => { onclick={() => {
@@ -104,16 +109,21 @@ hover:opacity-100 -->
hover:preset-tonal-success hover:preset-tonal-success
transition-all transition-all
" "
title="Turn debug content and styles off and on" title="Turn debug content and styles off and on">
>
{#if $ae_loc?.debug_mode} {#if $ae_loc?.debug_mode}
<!-- <span class="fas fa-toggle-on mx-1"></span> --> <!-- <span class="fas fa-toggle-on mx-1"></span> -->
<ToggleRight strokeWidth="2.5" color="green" class="inline-block mx-1" /> <ToggleRight
strokeWidth="2.5"
color="green"
class="mx-1 inline-block" />
<span>Debug</span> <span>Debug</span>
<span class="hidden"> Mode On </span> <span class="hidden"> Mode On </span>
{:else} {:else}
<!-- <span class="fas fa-toggle-off mx-1"></span> --> <!-- <span class="fas fa-toggle-off mx-1"></span> -->
<ToggleLeft strokeWidth="1" color="gray" class="inline-block mx-1" /> <ToggleLeft
strokeWidth="1"
color="gray"
class="mx-1 inline-block" />
<span>Debug?</span> <span>Debug?</span>
<span class="hidden"> Mode Off </span> <span class="hidden"> Mode Off </span>
{/if} {/if}
@@ -137,10 +147,9 @@ hover:opacity-100 -->
preset-outlined-surface-400-600 preset-filled-suface-200-800 preset-outlined-surface-400-600 preset-filled-suface-200-800
hover:preset-tonal-success hover:preset-tonal-success
transition-all transition-all
" ">
>
<!-- <span class="fas fa-info-circle mx-1"></span> --> <!-- <span class="fas fa-info-circle mx-1"></span> -->
<Info class="inline-block mx-1" /> <Info class="mx-1 inline-block" />
Quick Info Quick Info
</button> </button>
@@ -157,11 +166,10 @@ hover:opacity-100 -->
hover:preset-tonal-warning hover:preset-tonal-warning
transition-all transition-all
" "
title="Turn debug content and styles off and on" title="Turn debug content and styles off and on">
>
<!-- <span class="fas fa-toggle-on mx-1"></span> --> <!-- <span class="fas fa-toggle-on mx-1"></span> -->
<!-- <ToggleRight class="inline-block mx-1" /> --> <!-- <ToggleRight class="inline-block mx-1" /> -->
<CircleX class="inline-block mx-1" /> <CircleX class="mx-1 inline-block" />
<span class="hidden"> Close </span> <span class="hidden"> Close </span>
<span>Debug</span> <span>Debug</span>
</button> </button>
@@ -177,18 +185,17 @@ hover:opacity-100 -->
id="AE-Quick-Debug" id="AE-Quick-Debug"
class=" class="
btn btn-icon btn btn-icon
text-xs
p-1
preset-outlined-surface-100-900 preset-outlined-surface-100-900
hover:preset-filled-warning-100-900 hover:preset-filled-warning-100-900
fixed
bottom-8
transition-all left-2
fixed bottom-8 left-2 p-1 text-xs text-neutral-300
text-neutral-300 hover:text-neutral-800 transition-all hover:text-neutral-800
dark:text-neutral-700 dark:hover:text-neutral-200 dark:text-neutral-700 dark:hover:text-neutral-200
" "
title="Turn debug content and styles off and on" title="Turn debug content and styles off and on">
>
<!-- absolute bottom-2 left-2 --> <!-- absolute bottom-2 left-2 -->
<!-- fixed bottom-0 left-0 --> <!-- fixed bottom-0 left-0 -->
&pi; &pi;

View File

@@ -1,80 +1,87 @@
<script lang="ts"> <script lang="ts">
interface Props { interface Props {
log_lvl?: number; log_lvl?: number;
additional_kv?: key_val; additional_kv?: key_val;
e_success?: boolean; e_success?: boolean;
e_class?: string; e_class?: string;
e_title?: string; e_title?: string;
e_text?: string; e_text?: string;
e_class_h1?: string; e_class_h1?: string;
e_class_h2?: string; e_class_h2?: string;
e_class_form_hidden?: string; e_class_form_hidden?: string;
e_class_form_showing?: string; e_class_form_showing?: string;
btn_text?: string; btn_text?: string;
btn_title?: string; btn_title?: string;
btn_class?: string; btn_class?: string;
show_btn_class?: string; show_btn_class?: string;
hide_icon?: boolean; hide_icon?: boolean;
} }
let { let {
log_lvl = $bindable(0), log_lvl = $bindable(0),
additional_kv = $bindable({}), additional_kv = $bindable({}),
e_success = $bindable(false), e_success = $bindable(false),
e_class = '', e_class = '',
// e_title = 'Technical Help', // e_title = 'Technical Help',
// e_text = 'Request technical help for this application.', // e_text = 'Request technical help for this application.',
e_class_h1 = $bindable(''), e_class_h1 = $bindable(''),
e_class_h2 = $bindable(''), e_class_h2 = $bindable(''),
e_class_form_hidden = $bindable(''), e_class_form_hidden = $bindable(''),
e_class_form_showing = $bindable(''), e_class_form_showing = $bindable(''),
btn_text = 'Technical Help', btn_text = 'Technical Help',
btn_title = 'Technical support help', btn_title = 'Technical support help',
btn_class = '', btn_class = '',
show_btn_class = '', show_btn_class = '',
hide_icon = false hide_icon = false
}: Props = $props(); }: Props = $props();
// *** Import Svelte specific // *** Import Svelte specific
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
// *** Import other supporting libraries // *** Import other supporting libraries
import { BadgeQuestionMark, ChevronDown, ChevronRight, LifeBuoy, RefreshCw, SquareX } from '@lucide/svelte'; import {
// *** Import Aether specific variables and functions BadgeQuestionMark,
import { ChevronDown,
ae_snip, ChevronRight,
ae_loc, LifeBuoy,
ae_sess, RefreshCw,
ae_api, SquareX
ae_trig, } from '@lucide/svelte';
slct, // *** Import Aether specific variables and functions
slct_trigger, import {
type key_val ae_snip,
} from '$lib/stores/ae_stores'; ae_loc,
import { api } from '$lib/api/api'; ae_sess,
ae_api,
ae_trig,
slct,
slct_trigger,
type key_val
} from '$lib/stores/ae_stores';
import { api } from '$lib/api/api';
if (log_lvl) {
console.log(`Help - technical support component loaded`);
}
let help_tech_text: string = $state('');
let hide_additional_info: boolean = $state(true);
function prevent_default<T extends Event>(fn: (event: T) => void) {
return function (event: T) {
event.preventDefault();
fn(event);
};
}
function send_help_tech_email() {
if (log_lvl) { if (log_lvl) {
console.log(`Help - technical support component loaded`); console.log(`*** send_help_tech_email() ***`);
} }
let help_tech_text: string = $state(''); let subject = `Technical Notification - ${$ae_loc.name}`;
let hide_additional_info: boolean = $state(true);
function prevent_default<T extends Event>(fn: (event: T) => void) { let body_html = `
return function (event: T) {
event.preventDefault();
fn(event);
};
}
function send_help_tech_email() {
if (log_lvl) {
console.log(`*** send_help_tech_email() ***`);
}
let subject = `Technical Notification - ${$ae_loc.name}`;
let body_html = `
<div> <div>
Technical Notification,\n\n Technical Notification,\n\n
<ul> <ul>
@@ -102,25 +109,24 @@
</div> </div>
`; `;
api.send_email({ api.send_email({
api_cfg: $ae_api, api_cfg: $ae_api,
from_email: from_email:
$ae_loc.site_cfg_json?.noreply_email ?? $ae_loc.site_cfg_json?.noreply_email ?? 'noreply+tech@oneskyit.com',
'noreply+tech@oneskyit.com', from_name: $ae_loc.site_cfg_json?.noreply_name ?? 'IT NoReply',
from_name: $ae_loc.site_cfg_json?.noreply_name ?? 'IT NoReply', // to_email: $ae_loc.site_cfg_json?.admin_email ?? 'admin+tech@oneskyit.com', // 'scott+idaabb@oneskyit.com',
// to_email: $ae_loc.site_cfg_json?.admin_email ?? 'admin+tech@oneskyit.com', // 'scott+idaabb@oneskyit.com', to_email: 'it+tech@oneskyit.com',
to_email: 'it+tech@oneskyit.com', // to_email: $idaa_slct.post_obj.email,
// to_email: $idaa_slct.post_obj.email, // to_email: 'scott+idaabb@oneskyit.com',
// to_email: 'scott+idaabb@oneskyit.com', // to_name: $ae_loc.site_cfg_json?.admin_name ?? 'IT Tech',
// to_name: $ae_loc.site_cfg_json?.admin_name ?? 'IT Tech', to_name: 'IT Tech',
to_name: 'IT Tech', // to_name: $idaa_slct.post_obj.full_name ?? 'IDAA BB Poster',
// to_name: $idaa_slct.post_obj.full_name ?? 'IDAA BB Poster', subject: subject,
subject: subject, body_html: body_html
body_html: body_html });
});
help_tech_text = ''; // Clear the text area after sending help_tech_text = ''; // Clear the text area after sending
} }
</script> </script>
<!-- class:bg-radial-[at_55%_50%]={$ae_sess.show_help_tech} <!-- class:bg-radial-[at_55%_50%]={$ae_sess.show_help_tech}
@@ -131,8 +137,8 @@ class:to-90%={$ae_sess.show_help_tech} -->
class=" class="
flex flex-row flex flex-row
items-center justify-center items-center justify-center
rounded-lg shadow-2xl rounded-lg border
border border-transparent border-transparent shadow-2xl
transition-all transition-all
{e_class} {e_class}
{!$ae_sess.show_help_tech ? e_class_form_hidden : e_class_form_showing} {!$ae_sess.show_help_tech ? e_class_form_hidden : e_class_form_showing}
@@ -148,48 +154,42 @@ class:to-90%={$ae_sess.show_help_tech} -->
class:hover:shadow-blue-200={$ae_sess.show_help_tech} class:hover:shadow-blue-200={$ae_sess.show_help_tech}
class:hover:dark:shadow-blue-800={$ae_sess.show_help_tech} class:hover:dark:shadow-blue-800={$ae_sess.show_help_tech}
class:bg-blue-100={$ae_sess.show_help_tech} class:bg-blue-100={$ae_sess.show_help_tech}
class:dark:bg-blue-900={$ae_sess.show_help_tech} class:dark:bg-blue-900={$ae_sess.show_help_tech}>
>
{#if $ae_sess.show_help_tech} {#if $ae_sess.show_help_tech}
<div <div
class=" class="
w-xl flex
flex flex-col gap-1 w-xl flex-col items-center
items-center justify-center justify-center gap-1
rounded-xl
border border-gray-500/20 bg-blue-200
p-2 p-2
border rounded-xl border-gray-500/20
bg-blue-200
dark:bg-blue-800
transition-all transition-all
" dark:bg-blue-800
> ">
<div <div
class=" class="
d-flex align-items-center justify-content-between d-flex align-items-center justify-content-between
flex flex-row gap-1 items-center justify-between flex w-full flex-row items-center justify-between
w-full gap-1
" ">
>
<h1 <h1
class=" class="
h1 h1
text-base font-semibold text-gray-800 dark:text-gray-200 w-fit text-base font-semibold text-gray-800
w-fit dark:text-gray-200
{e_class_h1} {e_class_h1}
" ">
>
{#if e_success} {#if e_success}
<span <span
class="text-lg text-green-800 dark:text-green-200 font-semibold" class="text-lg font-semibold text-green-800 dark:text-green-200">
> <BadgeQuestionMark class="mr-2 inline-block" />
<BadgeQuestionMark class="inline-block mr-2" />
Help Requested Help Requested
</span> </span>
{:else} {:else}
<span <span
class="text-lg text-gray-800 dark:text-gray-200 font-semibold" class="text-lg font-semibold text-gray-800 dark:text-gray-200">
> <BadgeQuestionMark class="mr-2 inline-block" />
<BadgeQuestionMark class="inline-block mr-2" />
<!-- Request Technical Help --> <!-- Request Technical Help -->
Notify Technical Support Notify Technical Support
</span> </span>
@@ -211,8 +211,7 @@ class:to-90%={$ae_sess.show_help_tech} -->
transition-all transition-all
{btn_class} {btn_class}
" "
title="Close Help Request Form" title="Close Help Request Form">
>
<!-- <span class="fas fa-times"></span> --> <!-- <span class="fas fa-times"></span> -->
<SquareX size="1em" /> <SquareX size="1em" />
<span class="sr-only">Close</span> <span class="sr-only">Close</span>
@@ -222,9 +221,9 @@ class:to-90%={$ae_sess.show_help_tech} -->
<form <form
class=" class="
m-1 m-1
flex flex-col gap-1 flex w-md max-w-lg
items-center justify-center flex-col items-center
w-md max-w-lg justify-center gap-1
" "
onsubmit={prevent_default(() => { onsubmit={prevent_default(() => {
// Do stuff... // Do stuff...
@@ -234,40 +233,37 @@ class:to-90%={$ae_sess.show_help_tech} -->
$ae_sess.show_help_tech = false; $ae_sess.show_help_tech = false;
alert('Notification sent to the IT team.'); alert('Notification sent to the IT team.');
})} })}>
>
<textarea <textarea
class=" class="
form-control form-control
w-full max-w-lg h-24 p-2 h-24 w-full max-w-lg rounded
border border-gray-300 rounded border border-gray-300 bg-white
text-gray-950 dark:text-gray-50 p-2 text-gray-950
bg-white dark:bg-gray-500 dark:bg-gray-500 dark:text-gray-50
hover:dark:bg-gray-50 hover:dark:bg-gray-50
hover:dark:text-gray-950 hover:dark:text-gray-950
" "
placeholder="Send with or without a description...." placeholder="Send with or without a description...."
bind:value={help_tech_text} bind:value={help_tech_text}></textarea>
></textarea>
<button <button
type="submit" type="submit"
class=" class="
btn btn-lg btn btn-lg
m-1
preset-tonal-warning preset-tonal-warning
preset-outlined-warning-100-900 preset-outlined-warning-100-900
hover:preset-filled-warning-200-800 hover:preset-filled-warning-200-800
m-1
transition-all transition-all
{btn_class} {btn_class}
" "
title={btn_title} title={btn_title}>
>
{#if !hide_icon} {#if !hide_icon}
<!-- <BadgeQuestionMark class="inline-block mr-2" /> --> <!-- <BadgeQuestionMark class="inline-block mr-2" /> -->
<LifeBuoy class="inline-block m-1" /> <LifeBuoy class="m-1 inline-block" />
{/if} {/if}
{#if !help_tech_text} {#if !help_tech_text}
@@ -280,9 +276,8 @@ class:to-90%={$ae_sess.show_help_tech} -->
<div <div
class=" class="
text-sm text-gray-700 dark:text-gray-300 text-center italic text-center text-sm text-gray-700 italic dark:text-gray-300
" ">
>
This is intended for technical issues only. Please contact your This is intended for technical issues only. Please contact your
organization's staff if you have a question about your organization's staff if you have a question about your
membership, recorded content, meetings, or posts. membership, recorded content, meetings, or posts.
@@ -290,29 +285,25 @@ class:to-90%={$ae_sess.show_help_tech} -->
<div <div
class=" class="
border border-gray-300 rounded p-2 w-full max-w-2xl overflow-scroll rounded
w-full border
max-w-2xl border-gray-300
overflow-scroll p-2
" ">
>
<div <div
class=" class="
d-flex align-items-center justify-content-between d-flex align-items-center justify-content-between
flex flex-row gap-1 items-center justify-between flex w-full flex-row items-center justify-between
w-full gap-1
" ">
>
<h2 <h2
class=" class="
h2 h2
text-base font-semibold text-gray-800 dark:text-gray-200 text-base font-semibold text-gray-800 dark:text-gray-200
{e_class_h2} {e_class_h2}
" ">
>
<span <span
class="text-base font-semibold text-gray-800 dark:text-gray-200" class="text-base font-semibold text-gray-800 dark:text-gray-200">
>
Additional Information Included Additional Information Included
</span> </span>
</h2> </h2>
@@ -325,8 +316,7 @@ class:to-90%={$ae_sess.show_help_tech} -->
" "
onclick={() => onclick={() =>
(hide_additional_info = !hide_additional_info)} (hide_additional_info = !hide_additional_info)}
title="Toggle additional information" title="Toggle additional information">
>
<span> <span>
{#if hide_additional_info} {#if hide_additional_info}
<!-- <span class="fas fa-caret-right"></span> --> <!-- <span class="fas fa-caret-right"></span> -->
@@ -341,89 +331,75 @@ class:to-90%={$ae_sess.show_help_tech} -->
</button> </button>
</div> </div>
<ul <ul
class="list-disc list-inside text-sm text-gray-800 dark:text-gray-200" class="list-inside list-disc text-sm text-gray-800 dark:text-gray-200"
class:hidden={hide_additional_info} class:hidden={hide_additional_info}>
>
<li> <li>
<span class="text-sm text-gray-500 dark:text-gray-400" <span class="text-sm text-gray-500 dark:text-gray-400"
>Datetime =</span >Datetime =</span>
>
{new Date().toISOString()} {new Date().toISOString()}
</li> </li>
<li> <li>
<span class="text-sm text-gray-500 dark:text-gray-400" <span class="text-sm text-gray-500 dark:text-gray-400"
>URL =</span >URL =</span>
>
{window.location.href} {window.location.href}
</li> </li>
<li> <li>
<span class="text-sm text-gray-500 dark:text-gray-400" <span class="text-sm text-gray-500 dark:text-gray-400"
>Browser =</span >Browser =</span>
>
{navigator.userAgent} {navigator.userAgent}
</li> </li>
<li> <li>
<span class="text-sm text-gray-500 dark:text-gray-400" <span class="text-sm text-gray-500 dark:text-gray-400"
>Viewport Size =</span >Viewport Size =</span>
>
{window.innerWidth} x {window.innerHeight} {window.innerWidth} x {window.innerHeight}
</li> </li>
<li> <li>
<span class="text-sm text-gray-500 dark:text-gray-400" <span class="text-sm text-gray-500 dark:text-gray-400"
>Screen Resolution =</span >Screen Resolution =</span>
>
{window.screen.width} x {window.screen.height} {window.screen.width} x {window.screen.height}
</li> </li>
<li> <li>
<span class="text-sm text-gray-500 dark:text-gray-400" <span class="text-sm text-gray-500 dark:text-gray-400"
>Dark mode =</span >Dark mode =</span>
>
{window?.matchMedia?.('(prefers-color-scheme:dark)') {window?.matchMedia?.('(prefers-color-scheme:dark)')
?.matches ?? false} ?.matches ?? false}
</li> </li>
<li> <li>
<span class="text-sm text-gray-500 dark:text-gray-400" <span class="text-sm text-gray-500 dark:text-gray-400"
>In iframe =</span >In iframe =</span>
>
{$ae_loc?.iframe} {$ae_loc?.iframe}
</li> </li>
<li> <li>
<span class="text-sm text-gray-500 dark:text-gray-400" <span class="text-sm text-gray-500 dark:text-gray-400"
>Theme Mode =</span >Theme Mode =</span>
>
{$ae_loc?.theme_mode} {$ae_loc?.theme_mode}
</li> </li>
<li> <li>
<span class="text-sm text-gray-500 dark:text-gray-400" <span class="text-sm text-gray-500 dark:text-gray-400"
>Theme Name =</span >Theme Name =</span>
>
{$ae_loc?.theme_name} {$ae_loc?.theme_name}
</li> </li>
<li> <li>
<span class="text-sm text-gray-500 dark:text-gray-400" <span class="text-sm text-gray-500 dark:text-gray-400"
>Account ID =</span >Account ID =</span>
>
{$slct.account_id} {$slct.account_id}
</li> </li>
<li> <li>
<span class="text-sm text-gray-500 dark:text-gray-400" <span class="text-sm text-gray-500 dark:text-gray-400"
>Access Type =</span >Access Type =</span>
>
{$ae_loc?.access_type} {$ae_loc?.access_type}
</li> </li>
{#if $ae_loc?.person_id} {#if $ae_loc?.person_id}
<li> <li>
<span <span
class="text-sm text-gray-500 dark:text-gray-400" class="text-sm text-gray-500 dark:text-gray-400"
>person_id =</span >person_id =</span>
>
{$ae_loc?.person_id} {$ae_loc?.person_id}
</li> </li>
<li> <li>
<span <span
class="text-sm text-gray-500 dark:text-gray-400" class="text-sm text-gray-500 dark:text-gray-400"
>full_name =</span >full_name =</span>
>
{$ae_loc?.full_name} {$ae_loc?.full_name}
</li> </li>
{/if} {/if}
@@ -431,29 +407,25 @@ class:to-90%={$ae_sess.show_help_tech} -->
<li> <li>
<span <span
class="text-sm text-gray-500 dark:text-gray-400" class="text-sm text-gray-500 dark:text-gray-400"
>user_id =</span >user_id =</span>
>
{$ae_loc?.user_id} {$ae_loc?.user_id}
</li> </li>
<li> <li>
<span <span
class="text-sm text-gray-500 dark:text-gray-400" class="text-sm text-gray-500 dark:text-gray-400"
>username =</span >username =</span>
>
{$ae_loc?.username} {$ae_loc?.username}
</li> </li>
<li> <li>
<span <span
class="text-sm text-gray-500 dark:text-gray-400" class="text-sm text-gray-500 dark:text-gray-400"
>email =</span >email =</span>
>
{$ae_loc?.email} {$ae_loc?.email}
</li> </li>
{/if} {/if}
<li> <li>
<span class="text-sm text-gray-500 dark:text-gray-400" <span class="text-sm text-gray-500 dark:text-gray-400"
>API Base URL =</span >API Base URL =</span>
>
{$ae_api.base_url} {$ae_api.base_url}
</li> </li>
@@ -461,13 +433,12 @@ class:to-90%={$ae_sess.show_help_tech} -->
<h2 class="text-base font-semibold text-gray-800"> <h2 class="text-base font-semibold text-gray-800">
Component Info: Component Info:
</h2> </h2>
<ul class="list-disc list-inside text-gray-800 text-sm"> <ul class="list-inside list-disc text-sm text-gray-800">
{#each Object.entries(additional_kv) as [key, value] (key)} {#each Object.entries(additional_kv) as [key, value] (key)}
<li> <li>
<span <span
class="text-sm text-gray-500 dark:text-gray-400" class="text-sm text-gray-500 dark:text-gray-400"
>{key} =</span >{key} =</span>
>
{value ?? '-- not set --'} {value ?? '-- not set --'}
</li> </li>
{/each} {/each}
@@ -475,8 +446,7 @@ class:to-90%={$ae_sess.show_help_tech} -->
{/if} {/if}
</ul> </ul>
<div <div
class="text-sm text-gray-700 dark:text-gray-300 text-center italic" class="text-center text-sm text-gray-700 italic dark:text-gray-300">
>
This information will be included in the help request to This information will be included in the help request to
assist technical support in diagnosing the issue. assist technical support in diagnosing the issue.
</div> </div>
@@ -484,11 +454,10 @@ class:to-90%={$ae_sess.show_help_tech} -->
<div <div
class=" class="
flex flex-row gap-2 items-center justify-around mt-2 flex w-full flex-row items-center
w-full justify-around
mt-2 gap-2
" ">
>
<button <button
type="button" type="button"
onclick={() => { onclick={() => {
@@ -562,8 +531,7 @@ class:to-90%={$ae_sess.show_help_tech} -->
transition-all transition-all
{btn_class} {btn_class}
" "
title="Clear App Data & Settings: Clear IndexedDB and reload. If in edit mode localStorage and sessionStorage will also be cleared." title="Clear App Data & Settings: Clear IndexedDB and reload. If in edit mode localStorage and sessionStorage will also be cleared.">
>
<!-- <span class="fas fa-eraser mx-1"></span> --> <!-- <span class="fas fa-eraser mx-1"></span> -->
<!-- <span class="fas fa-sync mx-1"></span> --> <!-- <span class="fas fa-sync mx-1"></span> -->
<RefreshCw size="1em" class="inline-block" /> <RefreshCw size="1em" class="inline-block" />
@@ -582,8 +550,7 @@ class:to-90%={$ae_sess.show_help_tech} -->
transition-all transition-all
{btn_class} {btn_class}
" "
title="Close Help Request Form" title="Close Help Request Form">
>
<!-- <span class="fas fa-times"></span> --> <!-- <span class="fas fa-times"></span> -->
<SquareX size="1em" class="inline-block" /> <SquareX size="1em" class="inline-block" />
<span class="">Cancel</span> <span class="">Cancel</span>
@@ -603,10 +570,9 @@ class:to-90%={$ae_sess.show_help_tech} -->
{btn_class} {btn_class}
{show_btn_class} {show_btn_class}
" "
title={btn_title} title={btn_title}>
>
{#if !hide_icon} {#if !hide_icon}
<BadgeQuestionMark class="inline-block m-0.5" /> <BadgeQuestionMark class="m-0.5 inline-block" />
{/if} {/if}
<span class="hidden"> <span class="hidden">
{btn_text} {btn_text}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,56 +1,63 @@
<script lang="ts"> <script lang="ts">
// *** Import Svelte specific // *** Import Svelte specific
// import { tick } from 'svelte'; // import { tick } from 'svelte';
// import { goto, invalidateAll } from '$app/navigation'; // import { goto, invalidateAll } from '$app/navigation';
// *** Import other supporting libraries // *** Import other supporting libraries
import { import {
// ArrowBigRight, // ArrowBigRight,
// Bug, // Bug,
CircleX, CircleX,
// Eye, EyeOff, // Eye, EyeOff,
// Key, // Key,
// LogIn, LogOut, LockKeyhole, // LogIn, LogOut, LockKeyhole,
// Mail, MailCheck, // Mail, MailCheck,
Menu, Menu,
// RefreshCw, RefreshCcwDot, // RefreshCw, RefreshCcwDot,
ShieldEllipsis, ShieldEllipsis,
ShieldMinus, ShieldMinus,
ShieldPlus, ShieldPlus,
ShieldUser, ShieldUser,
ToggleLeft, ToggleRight, ToggleLeft,
User, ToggleRight,
UserCheck, User,
UserCog, UserCheck,
Wand2, UserCog,
X Wand2,
} from '@lucide/svelte'; X
} from '@lucide/svelte';
// *** Import Aether specific variables and functions // *** Import Aether specific variables and functions
// import { ae_util } from '$lib/ae_utils/ae_utils'; // import { ae_util } from '$lib/ae_utils/ae_utils';
import { ae_loc, ae_sess, ae_api, slct, slct_trigger } from '$lib/stores/ae_stores'; import {
ae_loc,
ae_sess,
ae_api,
slct,
slct_trigger
} from '$lib/stores/ae_stores';
import Element_access_type from '$lib/app_components/e_app_access_type.svelte'; import Element_access_type from '$lib/app_components/e_app_access_type.svelte';
import Element_app_cfg from '$lib/app_components/e_app_cfg.svelte'; import Element_app_cfg from '$lib/app_components/e_app_cfg.svelte';
import Element_sign_in_out from '$lib/app_components/e_app_sign_in_out.svelte'; import Element_sign_in_out from '$lib/app_components/e_app_sign_in_out.svelte';
import Element_theme from '$lib/app_components/e_app_theme.svelte'; import Element_theme from '$lib/app_components/e_app_theme.svelte';
// *** Setup Svelte properties // *** Setup Svelte properties
interface Props { interface Props {
log_lvl?: number; log_lvl?: number;
data: any; data: any;
hide?: null | boolean; hide?: null | boolean;
expand?: boolean; expand?: boolean;
} }
let { let {
log_lvl = $bindable(0), log_lvl = $bindable(0),
data = null, data = null,
hide = $bindable(false), hide = $bindable(false),
expand = $bindable(false) expand = $bindable(false)
}: Props = $props(); }: Props = $props();
let trigger_clear_access: null | boolean = $state(null); let trigger_clear_access: null | boolean = $state(null);
</script> </script>
<!-- App System Menu --> <!-- App System Menu -->
@@ -93,38 +100,37 @@ max-w-max -->
ae_app__sys_menu ae_app__sys_menu
hidden-print hidden-print
md:text-md
lg:text-md xl:text-md absolute
right-0
bottom-6
z-50 z-50
absolute bottom-6 right-0 flex
opacity-90 w-min max-w-md flex-col-reverse
hover:opacity-100
w-min
max-w-md
flex flex-col-reverse gap-1
items-end justify-center items-end justify-center
text-sm sm:text-sm md:text-md lg:text-md xl:text-md 2xl:text-lg gap-1 rounded-lg border-2 border-blue-300/20 bg-blue-200/90 text-sm
bg-blue-200/90 opacity-90
border-2 rounded-lg transition-all delay-500
border-blue-300/20 dark:border-blue-700/20 duration-500 ease-in-out
hover:border-blue-500/20 hover:dark:border-blue-500/20 hover:border-blue-500/20 hover:opacity-100
transition-all hover:delay-200
delay-500 hover:delay-200 hover:duration-200 sm:text-sm
duration-500 hover:duration-200 2xl:text-lg dark:border-blue-700/20
ease-in-out hover:dark:border-blue-500/20
" "
class:top-0={expand && (1 as any) == 3} class:top-0={expand && (1 as any) == 3}
class:opacity-100={expand} class:opacity-100={expand}
class:w-full={expand} class:w-full={expand}
class:hidden={hide} class:hidden={hide}
class:border-transparent={!expand} class:border-transparent={!expand}
class:bg-transparent={!expand} class:bg-transparent={!expand}>
>
<!-- class:hidden={!expand} --> <!-- class:hidden={!expand} -->
<!-- class:preset-filled-warning-100-900={expand} --> <!-- class:preset-filled-warning-100-900={expand} -->
<div <div
@@ -134,38 +140,40 @@ max-w-max -->
class:dark:bg-blue-500={expand} class:dark:bg-blue-500={expand}
class=" class="
hidden-print hidden-print
flex flex-col items-end justify-end light:bg-blue-200/10 relative flex w-full
flex-col
items-end
justify-end
gap-1 gap-1
overflow-y-auto overflow-y-auto
p-1 p-1
w-full
light:bg-blue-200/10
dark:bg-blue-800/10
hover:opacity-100
relative
transition-all transition-all
delay-1000 hover:delay-500
duration-200 hover:duration-200 delay-1000
ease-in-out
duration-200
ease-in-out hover:opacity-100
hover:delay-500 hover:duration-200
dark:bg-blue-800/10
" "
title=" title="
ID: {$ae_loc?.person_id ?? '-- not set --'} / {$ae_loc?.user_id ?? '-- not set --'} ID: {$ae_loc?.person_id ?? '-- not set --'} / {$ae_loc?.user_id ??
'-- not set --'}
Name: {$ae_loc?.person?.full_name ?? '-- not set --'} Name: {$ae_loc?.person?.full_name ?? '-- not set --'}
Username: {$ae_loc?.user?.username ?? '-- not set --'} Username: {$ae_loc?.user?.username ?? '-- not set --'}
Email: {$ae_loc?.user?.email ?? '-- not set --'} Email: {$ae_loc?.user?.email ?? '-- not set --'}
Access Type: {$ae_loc?.access_type ?? '-- not set --'} Access Type: {$ae_loc?.access_type ?? '-- not set --'}
" ">
>
{#if $ae_loc?.person_id} {#if $ae_loc?.person_id}
<div class="flex flex-row gap-1 items-center justify-end transition-all w-full group"> <div
class="group flex w-full flex-row items-center justify-end gap-1 transition-all">
<User size="1em" class="mx-1 inline-block text-gray-500" /> <User size="1em" class="mx-1 inline-block text-gray-500" />
<span class:hidden={!expand} class="group-hover:inline-block"> <span class:hidden={!expand} class="group-hover:inline-block">
{$ae_loc?.person?.informal_name ?? $ae_loc?.person?.given_name} {$ae_loc?.person?.informal_name ??
$ae_loc?.person?.given_name}
</span> </span>
</div> </div>
{/if} {/if}
@@ -174,54 +182,60 @@ max-w-max -->
<!-- This is currently not set to show if not expanded. Saving space. --> <!-- This is currently not set to show if not expanded. Saving space. -->
<div <div
class:hidden={!expand} class:hidden={!expand}
class="flex flex-row gap-1 items-center justify-end transition-all w-full group" class="group flex w-full flex-row items-center justify-end gap-1 transition-all">
> <ShieldUser
<ShieldUser size="1em" class="mx-1 inline-block text-gray-500" /> size="1em"
class="mx-1 inline-block text-gray-500" />
<span class:hidden={!expand} class="group-hover:inline-block"> <span class:hidden={!expand} class="group-hover:inline-block">
{$ae_loc?.user?.username ?? '-- not set --'} {$ae_loc?.user?.username ?? '-- not set --'}
</span> </span>
</div> </div>
{/if} {/if}
<div class="flex flex-row gap-1 items-center justify-end transition-all w-full group"> <div
class="group flex w-full flex-row items-center justify-end gap-1 transition-all">
{#if $ae_loc.access_type && $ae_loc.access_type != 'anonymous'} {#if $ae_loc.access_type && $ae_loc.access_type != 'anonymous'}
<span <span
class="flex flex-row-reverse gap-1 group text-base" class="group flex flex-row-reverse gap-1 text-base"
title={`Current access type/level: ${$ae_loc.access_type}`} title={`Current access type/level: ${$ae_loc.access_type}`}>
>
<!-- <span class="fas fa-unlock mx-1"></span> --> <!-- <span class="fas fa-unlock mx-1"></span> -->
<!-- <ShieldPlus class="inline-block" /> --> <!-- <ShieldPlus class="inline-block" /> -->
{#if $ae_loc.access_type == 'super'} {#if $ae_loc.access_type == 'super'}
<Wand2 size="1em" class="m-1 inline-block" /> <Wand2 size="1em" class="m-1 inline-block" />
<span class:hidden={!expand} class="hidden group-hover:inline-block" <span
>Super</span class:hidden={!expand}
> class="hidden group-hover:inline-block">Super</span>
{:else if $ae_loc.access_type == 'manager'} {:else if $ae_loc.access_type == 'manager'}
<ShieldUser size="1em" class="m-1 inline-block" /> <ShieldUser size="1em" class="m-1 inline-block" />
<span class:hidden={!expand} class="hidden group-hover:inline-block" <span
>Manager</span class:hidden={!expand}
> class="hidden group-hover:inline-block"
>Manager</span>
{:else if $ae_loc.access_type == 'administrator'} {:else if $ae_loc.access_type == 'administrator'}
<UserCog size="1em" class="m-1 inline-block" /> <UserCog size="1em" class="m-1 inline-block" />
<span class:hidden={!expand} class="hidden group-hover:inline-block" <span
>Administrator</span class:hidden={!expand}
> class="hidden group-hover:inline-block"
>Administrator</span>
{:else if $ae_loc.access_type == 'trusted'} {:else if $ae_loc.access_type == 'trusted'}
<UserCheck size="1em" class="m-1 inline-block" /> <UserCheck size="1em" class="m-1 inline-block" />
<span class:hidden={!expand} class="hidden group-hover:inline-block" <span
>Trusted Access</span class:hidden={!expand}
> class="hidden group-hover:inline-block"
>Trusted Access</span>
{:else if $ae_loc.access_type == 'public'} {:else if $ae_loc.access_type == 'public'}
Public Public
<span class:hidden={!expand} class="hidden group-hover:inline-block" <span
>Access</span class:hidden={!expand}
> class="hidden group-hover:inline-block"
>Access</span>
{:else if $ae_loc.access_type == 'authenticated'} {:else if $ae_loc.access_type == 'authenticated'}
Authenticated Authenticated
<span class:hidden={!expand} class="hidden group-hover:inline-block" <span
>Access</span class:hidden={!expand}
> class="hidden group-hover:inline-block"
>Access</span>
{:else if $ae_loc.access_type == 'anonymous'} {:else if $ae_loc.access_type == 'anonymous'}
Anonymous Access Anonymous Access
{:else} {:else}
@@ -254,13 +268,13 @@ max-w-max -->
// $ae_loc.sys_menu.expand_btn = true; // $ae_loc.sys_menu.expand_btn = true;
} }
}} }}
class="btn btn-sm text-xs variant-outline-surface hover:variant-ghost-warning transition-all group" class="btn btn-sm variant-outline-surface hover:variant-ghost-warning group text-xs transition-all"
title={`Current user access level: "${$ae_loc.user_access_type}". Click use passcode for a different access level.`} title={`Current user access level: "${$ae_loc.user_access_type}". Click use passcode for a different access level.`}>
>
<!-- <span class="fas fa-lock mx-1"></span> --> <!-- <span class="fas fa-lock mx-1"></span> -->
<!-- <ShieldMinus /> --> <!-- <ShieldMinus /> -->
<ShieldEllipsis size="2em" class="inline-block" /> <ShieldEllipsis size="2em" class="inline-block" />
<span class="hidden group-hover:inline-block">Passcode?</span> <span class="hidden group-hover:inline-block"
>Passcode?</span>
</button> </button>
{:else} {:else}
<button <button
@@ -291,16 +305,17 @@ max-w-max -->
} }
}} }}
class=" class="
btn btn-sm text-xs btn btn-sm variant-outline-surface
flex-row-reverse hover:variant-ghost-warning
variant-outline-surface hover:variant-ghost-warning group flex-row-reverse
transition-all group text-xs transition-all
" "
title={`Current access level: "${$ae_loc.access_type}". Click to clear the temporary access level.`} title={`Current access level: "${$ae_loc.access_type}". Click to clear the temporary access level.`}>
>
<!-- <span class="fas fa-lock mx-1"></span> Lock? --> <!-- <span class="fas fa-lock mx-1"></span> Lock? -->
<ShieldMinus class="inline-block" /> <ShieldMinus class="inline-block" />
<span class="hidden group-hover:inline-block"> Clear? </span> <span class="hidden group-hover:inline-block">
Clear?
</span>
</button> </button>
{/if} {/if}
{:else} {:else}
@@ -320,19 +335,22 @@ max-w-max -->
// $ae_loc.app_cfg.show_element__access_type = true; // $ae_loc.app_cfg.show_element__access_type = true;
// $ae_loc.app_cfg.show_element__passcode_input = true; // $ae_loc.app_cfg.show_element__passcode_input = true;
// await tick(); // await tick();
console.log('Layout button click: Focus on passcode input!'); console.log(
'Layout button click: Focus on passcode input!'
);
/** @type {HTMLElement | null} */ /** @type {HTMLElement | null} */
const to_focus = document.getElementById('access_passcode_input'); const to_focus = document.getElementById(
'access_passcode_input'
);
to_focus?.focus(); to_focus?.focus();
}} }}
class=" class="
btn btn-sm text-xs btn btn-sm variant-outline-surface
flex-row-reverse hover:variant-ghost-success
variant-outline-surface hover:variant-ghost-success group flex-row-reverse
transition-all group text-xs transition-all
" "
title="Anonymous public access is currently set. You must Sign In or use a passcode to change your access level." title="Anonymous public access is currently set. You must Sign In or use a passcode to change your access level.">
>
<!-- <span class="fas fa-lock mx-1 lock_icon"></span> --> <!-- <span class="fas fa-lock mx-1 lock_icon"></span> -->
<!-- <span class="">Unlock?</span> --> <!-- <span class="">Unlock?</span> -->
<ShieldUser class="inline-block" /> <ShieldUser class="inline-block" />
@@ -349,19 +367,21 @@ max-w-max -->
// dispatch_edit_mode_changed(); // dispatch_edit_mode_changed();
}} }}
class=" class="
btn btn-base text-sm btn btn-base preset-tonal-warning
preset-tonal-warning
preset-outlined-warning-800-200 preset-outlined-warning-800-200
hover:preset-tonal-success hover:preset-tonal-success
transition-all group group
min-w-22 md:min-w-30 w-full max-w-fit w-full max-w-fit
gap-1 min-w-22 gap-1 text-sm transition-all
md:min-w-30
" "
title="Click to turn off edit mode. Edit mode is currently on." title="Click to turn off edit mode. Edit mode is currently on.">
>
<ToggleRight size="1em" class="m-1 inline-block" /> <ToggleRight size="1em" class="m-1 inline-block" />
<span class="text-xs">Edit</span> <span class="text-xs">Edit</span>
<span class="hidden group-hover:inline-block group-hover:text-xs"> Off </span> <span
class="hidden group-hover:inline-block group-hover:text-xs">
Off
</span>
</button> </button>
{:else if $ae_loc.authenticated_access} {:else if $ae_loc.authenticated_access}
<button <button
@@ -371,19 +391,21 @@ max-w-max -->
// dispatch_edit_mode_changed(); // dispatch_edit_mode_changed();
}} }}
class=" class="
btn btn-base text-sm btn btn-base preset-tonal-surface
preset-tonal-surface
preset-outlined-warning-400-600 preset-outlined-warning-400-600
hover:preset-tonal-warning hover:preset-tonal-warning
transition-all group group
min-w-22 md:min-w-30 w-full max-w-fit w-full max-w-fit
gap-1 min-w-22 gap-1 text-sm transition-all
md:min-w-30
" "
title="Click to torn on/enable edit mode. Edit mode is currently off/disabled." title="Click to torn on/enable edit mode. Edit mode is currently off/disabled.">
>
<ToggleLeft size="1em" class="m-1 inline-block" /> <ToggleLeft size="1em" class="m-1 inline-block" />
<span class="text-xs">Edit</span> <span class="text-xs">Edit</span>
<span class="hidden group-hover:inline-block group-hover:text-xs"> On? </span> <span
class="hidden group-hover:inline-block group-hover:text-xs">
On?
</span>
</button> </button>
{/if} {/if}
@@ -392,12 +414,12 @@ max-w-max -->
type="button" type="button"
class:w-full={expand} class:w-full={expand}
class=" class="
btn btn-base text-sm btn btn-base preset-tonal-surface
preset-tonal-surface
hover:preset-tonal-primary hover:preset-tonal-primary
px-1 py-1 group
min-w-22 md:min-w-30 w-full max-w-fit w-full max-w-fit
transition-all group min-w-22 px-1 py-1 text-sm
transition-all md:min-w-30
" "
title="Cycle font size: default → larger → smaller" title="Cycle font size: default → larger → smaller"
onclick={() => { onclick={() => {
@@ -409,17 +431,19 @@ max-w-max -->
} else { } else {
$ae_loc.font_size_mode = 'default'; $ae_loc.font_size_mode = 'default';
} }
}} }}>
>
{#if !$ae_loc.font_size_mode || $ae_loc.font_size_mode === 'default'} {#if !$ae_loc.font_size_mode || $ae_loc.font_size_mode === 'default'}
<span class="font-bold text-sm leading-none">A</span> <span class="text-sm leading-none font-bold">A</span>
<span class="hidden group-hover:inline-block text-xs">Font: Normal</span> <span class="hidden text-xs group-hover:inline-block"
>Font: Normal</span>
{:else if $ae_loc.font_size_mode === 'larger'} {:else if $ae_loc.font_size_mode === 'larger'}
<span class="font-bold text-base leading-none">A+</span> <span class="text-base leading-none font-bold">A+</span>
<span class="hidden group-hover:inline-block text-xs">Font: Larger</span> <span class="hidden text-xs group-hover:inline-block"
>Font: Larger</span>
{:else} {:else}
<span class="font-bold text-xs leading-none">A</span> <span class="text-xs leading-none font-bold">A</span>
<span class="hidden group-hover:inline-block text-xs">Font: Smaller</span> <span class="hidden text-xs group-hover:inline-block"
>Font: Smaller</span>
{/if} {/if}
</button> </button>
@@ -432,13 +456,13 @@ max-w-max -->
type="button" type="button"
class:w-full={expand} class:w-full={expand}
class=" class="
btn btn-base text-sm btn btn-base preset-filled-tertiary-400-600
preset-filled-tertiary-400-600
preset-outlined-tertiary-400-600 preset-outlined-tertiary-400-600
hover:preset-filled-success active:preset-filled-success hover:preset-filled-success
px-1 py-1 active:preset-filled-success group
min-w-22 md:min-w-30 w-full max-w-fit w-full max-w-fit
transition-all group min-w-22 px-1 py-1 text-sm
transition-all md:min-w-30
" "
title="Show/Hide the system menu" title="Show/Hide the system menu"
onclick={async () => { onclick={async () => {
@@ -472,15 +496,14 @@ max-w-max -->
// $ae_loc.app_cfg.show_element__passcode_input = false; // $ae_loc.app_cfg.show_element__passcode_input = false;
} }
// $ae_loc.sys_menu.expand_btn = !expand_btn; // $ae_loc.sys_menu.expand_btn = !expand_btn;
}} }}>
>
<!-- <span class=""> --> <!-- <span class=""> -->
{#if expand} {#if expand}
<CircleX class="m-1 inline-block" /> <CircleX class="m-1 inline-block" />
{:else} {:else}
<Menu class="m-1 inline-block" /> <Menu class="m-1 inline-block" />
{/if} {/if}
<span class="text-xs hidden group-hover:inline-block"> Menu </span> <span class="hidden text-xs group-hover:inline-block"> Menu </span>
<!-- </span> --> <!-- </span> -->
</button> </button>
<!-- </div> --> <!-- </div> -->
@@ -499,27 +522,26 @@ max-w-max -->
ae_app__sys_menu ae_app__sys_menu
hidden-print hidden-print
flex flex-col z-20 flex
items-end
justify-end
gap-2
min-w-48 min-w-48
flex-col
items-end
bg-white dark:bg-gray-800 justify-end
border border-gray-200 dark:border-gray-700 gap-2 rounded-lg
rounded-lg
px-1 py-2
transition-all border border-gray-200 bg-white
delay-1000 hover:delay-100 px-1
duration-100 hover:duration-200 py-2 transition-all
ease-in-out
z-20 hover:z-30 delay-1000
" duration-100 ease-in-out
> hover:z-30 hover:delay-100
hover:duration-200
dark:border-gray-700 dark:bg-gray-800
">
<button <button
type="button" type="button"
class:w-full={expand} class:w-full={expand}
@@ -528,8 +550,8 @@ max-w-max -->
preset-filled-tertiary-400-600 preset-filled-tertiary-400-600
preset-outlined-tertiary-400-600 preset-outlined-tertiary-400-600
hover:preset-filled-success active:preset-filled-success hover:preset-filled-success active:preset-filled-success
px-6 py-1 group px-6
transition-all group py-1 transition-all
" "
title="Show/Hide the system menu" title="Show/Hide the system menu"
onclick={() => { onclick={() => {
@@ -549,8 +571,7 @@ max-w-max -->
} }
// $ae_loc.sys_menu.expand_btn = !expand_btn; // $ae_loc.sys_menu.expand_btn = !expand_btn;
// $ae_loc.sys_menu.expand = !expand; // $ae_loc.sys_menu.expand = !expand;
}} }}>
>
{#if expand} {#if expand}
<CircleX class="m-1 inline-block" /> <CircleX class="m-1 inline-block" />
<span class="hidden group-hover:inline-block"> Hide Menu </span> <span class="hidden group-hover:inline-block"> Hide Menu </span>
@@ -564,16 +585,14 @@ max-w-max -->
<span class:hidden={!$ae_loc.edit_mode}> <span class:hidden={!$ae_loc.edit_mode}>
<Element_app_cfg <Element_app_cfg
hide={$ae_loc.sys_menu.hide_app_cfg} hide={$ae_loc.sys_menu.hide_app_cfg}
expand={$ae_loc.sys_menu.expand_app_cfg} expand={$ae_loc.sys_menu.expand_app_cfg} />
/>
</span> </span>
<span class:hidden={!$ae_loc.edit_mode}> <span class:hidden={!$ae_loc.edit_mode}>
<Element_theme <Element_theme
hide={$ae_loc.sys_menu.hide_app_cfg} hide={$ae_loc.sys_menu.hide_app_cfg}
expand={$ae_loc.sys_menu.expand_app_cfg} expand={$ae_loc.sys_menu.expand_app_cfg}
set_theme_mode={true} set_theme_mode={true}
set_theme_name={true} set_theme_name={true} />
/>
</span> </span>
<!-- {/if} --> <!-- {/if} -->
@@ -582,8 +601,8 @@ max-w-max -->
<!-- expand={$ae_loc.sys_menu.expand_user} --> <!-- expand={$ae_loc.sys_menu.expand_user} -->
<Element_sign_in_out <Element_sign_in_out
{data} {data}
hidden={$ae_loc.iframe || !$ae_loc.app_cfg?.show_element__sign_in_out} hidden={$ae_loc.iframe ||
/> !$ae_loc.app_cfg?.show_element__sign_in_out} />
{/if} {/if}
{#if !$ae_loc?.sys_menu?.hide_access_type && !$ae_loc?.iframe} {#if !$ae_loc?.sys_menu?.hide_access_type && !$ae_loc?.iframe}
@@ -592,9 +611,10 @@ max-w-max -->
bind:hide={$ae_loc.sys_menu.hide_access_type} bind:hide={$ae_loc.sys_menu.hide_access_type}
bind:focus_input={$ae_sess.sys_menu.focus_passcode_input} bind:focus_input={$ae_sess.sys_menu.focus_passcode_input}
bind:expand={$ae_loc.sys_menu.expand_access_type} bind:expand={$ae_loc.sys_menu.expand_access_type}
bind:show_passcode_input={$ae_sess.app_cfg.show_element__passcode_input} bind:show_passcode_input={
bind:trigger_clear_access $ae_sess.app_cfg.show_element__passcode_input
/> }
bind:trigger_clear_access />
{/if} {/if}
</div> </div>
</section> </section>

View File

@@ -1,22 +1,28 @@
<script lang="ts"> <script lang="ts">
import { Minimize2, Moon, Sun } from '@lucide/svelte'; import { Minimize2, Moon, Sun } from '@lucide/svelte';
import { ae_loc, ae_sess, ae_api, slct, slct_trigger } from '$lib/stores/ae_stores'; import {
ae_loc,
ae_sess,
ae_api,
slct,
slct_trigger
} from '$lib/stores/ae_stores';
interface Props { interface Props {
log_lvl?: number; log_lvl?: number;
hide?: null | boolean; hide?: null | boolean;
expand?: boolean; expand?: boolean;
set_theme_mode: any; set_theme_mode: any;
set_theme_name: any; set_theme_name: any;
} }
let { let {
log_lvl = $bindable(0), log_lvl = $bindable(0),
hide = $bindable(false), hide = $bindable(false),
expand = $bindable(false), expand = $bindable(false),
set_theme_mode, set_theme_mode,
set_theme_name set_theme_name
}: Props = $props(); }: Props = $props();
</script> </script>
<!-- Change light and dark mode --> <!-- Change light and dark mode -->
@@ -37,25 +43,23 @@ if ($ae_loc.app_cfg.theme_mode == 'light') {
bg-surface-100 dark:bg-surface-800 bg-surface-100 dark:bg-surface-800
text-surface-900 dark:text-surface-100 text-surface-900 dark:text-surface-100
border border-surface-200 dark:border-surface-700 border-surface-200 dark:border-surface-700 flex
flex flex-col flex-wrap gap-1 w-72 max-w-72 flex-col flex-wrap
items-end justify-center items-end justify-center
w-72 max-w-72 gap-1 rounded-lg
border
p-1 p-1
rounded-lg
shadow-md shadow-md
duration-300 delay-150 hover:delay-1000 hover:ease-out transition-all delay-150 duration-300 hover:delay-1000
transition-all hover:ease-out
" "
class:hidden={hide} class:hidden={hide}>
>
<div <div
class:hidden={!expand} class:hidden={!expand}
class="flex flex-row flex-wrap gap-2 items-center justify-between w-full" class="flex w-full flex-row flex-wrap items-center justify-between gap-2">
>
<!-- Theme Name: --> <!-- Theme Name: -->
<span class="text-sm font-semibold"> <span class="text-sm font-semibold">
{$ae_loc.theme_name} {$ae_loc.theme_name}
@@ -74,7 +78,10 @@ if ($ae_loc.app_cfg.theme_mode == 'light') {
// document.body.setAttribute('data-theme', $ae_loc?.theme_name); // document.body.setAttribute('data-theme', $ae_loc?.theme_name);
// NEW for Tailwind v4: Update the html attribute named "data-theme" to the current theme name. // NEW for Tailwind v4: Update the html attribute named "data-theme" to the current theme name.
document.documentElement.setAttribute('data-theme', new_theme_name); document.documentElement.setAttribute(
'data-theme',
new_theme_name
);
// if ($ae_loc.theme_mode == 'light') { // if ($ae_loc.theme_mode == 'light') {
// document.documentElement.classList.remove('dark'); // document.documentElement.classList.remove('dark');
@@ -86,8 +93,7 @@ if ($ae_loc.app_cfg.theme_mode == 'light') {
}} }}
bind:value={$ae_loc.theme_name} bind:value={$ae_loc.theme_name}
class="select w-32" class="select w-32"
title="Theme name" title="Theme name">
>
<option value="">-- None --</option> <option value="">-- None --</option>
<option value="cerberus">Cerberus</option> <option value="cerberus">Cerberus</option>
<option value="concord">Concord</option> <option value="concord">Concord</option>
@@ -112,41 +118,38 @@ if ($ae_loc.app_cfg.theme_mode == 'light') {
</div> </div>
<div <div
class="flex flex-row flex-wrap gap-2 items-center w-full" class="flex w-full flex-row flex-wrap items-center gap-2"
class:justify-between={expand} class:justify-between={expand}
class:justify-end={!expand} class:justify-end={!expand}>
>
{#if expand} {#if expand}
<!-- Hide theme options --> <!-- Hide theme options -->
<button <button
class=" class="
btn btn-sm text-xs btn btn-sm preset-tonal-secondary
preset-tonal-secondary hover:preset-filled-secondary-500 hover:preset-filled-secondary-500 group
transition-all group text-xs transition-all
" "
onclick={() => { onclick={() => {
expand = !expand; expand = !expand;
}} }}
title="Hide Theme Options" title="Hide Theme Options">
>
<!-- <span class="fas fa-compress-alt"></span> --> <!-- <span class="fas fa-compress-alt"></span> -->
<Minimize2 size="1em" class="m-1" /> <Minimize2 size="1em" class="m-1" />
<span <span
class=" class="
hidden hidden
group-hover:inline-block
text-xs text-xs
" group-hover:inline-block
> ">
Hide Theme Options Hide Theme Options
</span> </span>
</button> </button>
<button <button
class=" class="
btn btn-sm text-xs btn btn-sm preset-tonal-secondary
preset-tonal-secondary hover:preset-filled-secondary-500 hover:preset-filled-secondary-500 group
transition-all group text-xs transition-all
" "
onclick={() => { onclick={() => {
if ($ae_loc.theme_mode == 'light') { if ($ae_loc.theme_mode == 'light') {
@@ -157,23 +160,24 @@ if ($ae_loc.app_cfg.theme_mode == 'light') {
// DOM sync is handled reactively by the layout effect in +layout.svelte // DOM sync is handled reactively by the layout effect in +layout.svelte
}} }}
title="Change light and dark mode" title="Change light and dark mode">
>
<!-- <span class="fas fa-adjust"></span> --> <!-- <span class="fas fa-adjust"></span> -->
{#if $ae_loc.theme_mode == 'light'} {#if $ae_loc.theme_mode == 'light'}
<Sun class="m-1" /> <Sun class="m-1" />
<span class="hidden md:inline-block group-hover:inline-block">Light Mode</span> <span
class="hidden group-hover:inline-block md:inline-block"
>Light Mode</span>
{:else if $ae_loc.theme_mode == 'dark'} {:else if $ae_loc.theme_mode == 'dark'}
<Moon class="m-1" /> <Moon class="m-1" />
<span class="hidden group-hover:inline-block">Dark Mode</span> <span class="hidden group-hover:inline-block"
>Dark Mode</span>
{/if} {/if}
<span <span
class=" class="
hidden hidden
group-hover:inline-block
text-xs text-xs
" group-hover:inline-block
> ">
Change Change
</span> </span>
</button> </button>
@@ -197,18 +201,19 @@ if ($ae_loc.app_cfg.theme_mode == 'light') {
expand = !expand; expand = !expand;
}} }}
title="Change light and dark mode" title="Change light and dark mode">
>
{#if $ae_loc.theme_mode == 'light'} {#if $ae_loc.theme_mode == 'light'}
<span class="inline-block" title="Light Mode"> <span class="inline-block" title="Light Mode">
<Sun /> <Sun />
</span> </span>
<span class="hidden group-hover:inline-block">Light Mode</span> <span class="hidden group-hover:inline-block"
>Light Mode</span>
{:else if $ae_loc.theme_mode == 'dark'} {:else if $ae_loc.theme_mode == 'dark'}
<span class="inline-block" title="Dark Mode"> <span class="inline-block" title="Dark Mode">
<Moon /> <Moon />
</span> </span>
<span class="hidden group-hover:inline-block">Dark Mode</span> <span class="hidden group-hover:inline-block"
>Dark Mode</span>
{/if} {/if}
</button> </button>
{/if} {/if}

View File

@@ -1,120 +1,136 @@
<script lang="ts"> <script lang="ts">
/** /**
* e_app_url_builder.svelte * e_app_url_builder.svelte
* URL Param Builder — lets admins construct and copy shareable URLs * URL Param Builder — lets admins construct and copy shareable URLs
* with any combination of the supported URL params applied. * with any combination of the supported URL params applied.
* *
* Core params: * Core params:
* ?iframe=true|false — persist kiosk mode (stays in URL) * ?iframe=true|false — persist kiosk mode (stays in URL)
* ?theme=<name> — set theme name on load (stripped after apply) * ?theme=<name> — set theme name on load (stripped after apply)
* ?theme_mode=light|dark — set light/dark mode on load (stripped after apply) * ?theme_mode=light|dark — set light/dark mode on load (stripped after apply)
* ?key=<access_key> — site access key * ?key=<access_key> — site access key
* *
* Launcher params (all stripped from URL after apply): * Launcher params (all stripped from URL after apply):
* ?launcher_menu=hide|show * ?launcher_menu=hide|show
* ?launcher_header=hide|show * ?launcher_header=hide|show
* ?launcher_footer=hide|show * ?launcher_footer=hide|show
*/ */
import { page } from '$app/stores'; import { page } from '$app/stores';
import { Copy, Check, Link } from '@lucide/svelte'; import { Copy, Check, Link } from '@lucide/svelte';
// --- Per-param: include this param in the output URL? // --- Per-param: include this param in the output URL?
let use_iframe = $state(false); let use_iframe = $state(false);
let use_theme = $state(false); let use_theme = $state(false);
let use_theme_mode = $state(false); let use_theme_mode = $state(false);
let use_key = $state(false); let use_key = $state(false);
let use_launcher_menu = $state(false); let use_launcher_menu = $state(false);
let use_launcher_header = $state(false); let use_launcher_header = $state(false);
let use_launcher_footer = $state(false); let use_launcher_footer = $state(false);
// --- Param values // --- Param values
let val_iframe = $state<'true' | 'false'>('true'); let val_iframe = $state<'true' | 'false'>('true');
let val_theme = $state('nouveau'); let val_theme = $state('nouveau');
let val_theme_mode = $state<'light' | 'dark'>('dark'); let val_theme_mode = $state<'light' | 'dark'>('dark');
let val_key = $state(''); let val_key = $state('');
let val_launcher_menu = $state<'hide' | 'show'>('hide'); let val_launcher_menu = $state<'hide' | 'show'>('hide');
let val_launcher_header = $state<'hide' | 'show'>('hide'); let val_launcher_header = $state<'hide' | 'show'>('hide');
let val_launcher_footer = $state<'hide' | 'show'>('hide'); let val_launcher_footer = $state<'hide' | 'show'>('hide');
// Build the output URL reactively // Build the output URL reactively
let built_url = $derived.by(() => { let built_url = $derived.by(() => {
const base = $page.url; const base = $page.url;
const u = new URL(base.pathname + base.search + base.hash, base.origin); const u = new URL(base.pathname + base.search + base.hash, base.origin);
// Remove all known params first so we start clean each time // Remove all known params first so we start clean each time
u.searchParams.delete('iframe'); u.searchParams.delete('iframe');
u.searchParams.delete('theme'); u.searchParams.delete('theme');
u.searchParams.delete('theme_mode'); u.searchParams.delete('theme_mode');
u.searchParams.delete('key'); u.searchParams.delete('key');
u.searchParams.delete('launcher_menu'); u.searchParams.delete('launcher_menu');
u.searchParams.delete('launcher_header'); u.searchParams.delete('launcher_header');
u.searchParams.delete('launcher_footer'); u.searchParams.delete('launcher_footer');
if (use_iframe) u.searchParams.set('iframe', val_iframe); if (use_iframe) u.searchParams.set('iframe', val_iframe);
if (use_theme) u.searchParams.set('theme', val_theme); if (use_theme) u.searchParams.set('theme', val_theme);
if (use_theme_mode) u.searchParams.set('theme_mode', val_theme_mode); if (use_theme_mode) u.searchParams.set('theme_mode', val_theme_mode);
if (use_key && val_key.trim()) u.searchParams.set('key', val_key.trim()); if (use_key && val_key.trim()) u.searchParams.set('key', val_key.trim());
if (use_launcher_menu) u.searchParams.set('launcher_menu', val_launcher_menu); if (use_launcher_menu)
if (use_launcher_header) u.searchParams.set('launcher_header', val_launcher_header); u.searchParams.set('launcher_menu', val_launcher_menu);
if (use_launcher_footer) u.searchParams.set('launcher_footer', val_launcher_footer); if (use_launcher_header)
u.searchParams.set('launcher_header', val_launcher_header);
if (use_launcher_footer)
u.searchParams.set('launcher_footer', val_launcher_footer);
return u.toString(); return u.toString();
});
// Output mode: full URL (default) or params-only string
let params_only = $state(false);
let output = $derived.by(() => {
if (!params_only) return built_url;
const u = new URL(built_url);
return u.search || '(no params set)';
});
let copied = $state(false);
function copy_url() {
navigator.clipboard.writeText(output).then(() => {
copied = true;
setTimeout(() => (copied = false), 2000);
}); });
}
// Output mode: full URL (default) or params-only string const theme_options = [
let params_only = $state(false); { value: 'cerberus', label: 'Cerberus' },
{ value: 'concord', label: 'Concord' },
let output = $derived.by(() => { { value: 'crimson', label: 'Crimson' },
if (!params_only) return built_url; { value: 'hamlindigo', label: 'Hamlindigo' },
const u = new URL(built_url); { value: 'modern', label: 'Modern' },
return u.search || '(no params set)'; { value: 'nouveau', label: 'Nouveau' },
}); { value: 'rocket', label: 'Rocket' },
{ value: 'terminus', label: 'Terminus' },
let copied = $state(false); { value: 'vintage', label: 'Vintage' },
{ value: 'wintry', label: 'Wintry' },
function copy_url() { { value: 'AE_OSIT_default', label: 'OSIT' },
navigator.clipboard.writeText(output).then(() => { { value: 'AE_Firefly', label: 'Firefly ✦' },
copied = true; { value: 'AE_Firefly_SteelBlue', label: 'Firefly SteelBlue ✦' },
setTimeout(() => copied = false, 2000); { value: 'AE_Firefly_Indigo', label: 'Firefly Indigo ✦' },
}); { value: 'AE_Firefly_Rainbow', label: 'Firefly Rainbow ✨' },
} { value: 'AE_c_IDAA_light', label: 'IDAA light' },
{ value: 'AE_c_LCI', label: 'LCI' }
const theme_options = [ ];
{ value: 'cerberus', label: 'Cerberus' },
{ value: 'concord', label: 'Concord' },
{ value: 'crimson', label: 'Crimson' },
{ value: 'hamlindigo', label: 'Hamlindigo' },
{ value: 'modern', label: 'Modern' },
{ value: 'nouveau', label: 'Nouveau' },
{ value: 'rocket', label: 'Rocket' },
{ value: 'terminus', label: 'Terminus' },
{ value: 'vintage', label: 'Vintage' },
{ value: 'wintry', label: 'Wintry' },
{ value: 'AE_OSIT_default', label: 'OSIT' },
{ value: 'AE_Firefly', label: 'Firefly ✦' },
{ value: 'AE_Firefly_SteelBlue', label: 'Firefly SteelBlue ✦' },
{ value: 'AE_Firefly_Indigo', label: 'Firefly Indigo ✦' },
{ value: 'AE_Firefly_Rainbow', label: 'Firefly Rainbow ✨' },
{ value: 'AE_c_IDAA_light', label: 'IDAA light' },
{ value: 'AE_c_LCI', label: 'LCI' },
];
</script> </script>
<section class="space-y-3 text-sm"> <section class="space-y-3 text-sm">
<h2
<h2 class="text-xs font-semibold uppercase tracking-widest text-surface-500 dark:text-surface-400 flex items-center gap-1"> class="text-surface-500 dark:text-surface-400 flex items-center gap-1 text-xs font-semibold tracking-widest uppercase">
<Link size="0.9em" /> URL Param Builder <Link size="0.9em" /> URL Param Builder
</h2> </h2>
<!-- ── Core params ─────────────────────────────────────────────────── --> <!-- ── Core params ─────────────────────────────────────────────────── -->
<div class="space-y-1.5"> <div class="space-y-1.5">
<p class="text-[9px] font-bold uppercase tracking-widest opacity-40 ml-0.5">Core</p> <p
class="ml-0.5 text-[9px] font-bold tracking-widest uppercase opacity-40">
Core
</p>
<!-- iframe --> <!-- iframe -->
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<input id="ubld_iframe" type="checkbox" class="checkbox checkbox-sm shrink-0" bind:checked={use_iframe} /> <input
<label for="ubld_iframe" class="w-24 text-xs font-mono cursor-pointer select-none" class:opacity-35={!use_iframe}>iframe</label> id="ubld_iframe"
<select bind:value={val_iframe} disabled={!use_iframe} class="select select-sm text-xs flex-1 disabled:opacity-40"> type="checkbox"
class="checkbox checkbox-sm shrink-0"
bind:checked={use_iframe} />
<label
for="ubld_iframe"
class="w-24 cursor-pointer font-mono text-xs select-none"
class:opacity-35={!use_iframe}>iframe</label>
<select
bind:value={val_iframe}
disabled={!use_iframe}
class="select select-sm flex-1 text-xs disabled:opacity-40">
<option value="true">true</option> <option value="true">true</option>
<option value="false">false</option> <option value="false">false</option>
</select> </select>
@@ -122,9 +138,19 @@
<!-- theme --> <!-- theme -->
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<input id="ubld_theme" type="checkbox" class="checkbox checkbox-sm shrink-0" bind:checked={use_theme} /> <input
<label for="ubld_theme" class="w-24 text-xs font-mono cursor-pointer select-none" class:opacity-35={!use_theme}>theme</label> id="ubld_theme"
<select bind:value={val_theme} disabled={!use_theme} class="select select-sm text-xs flex-1 disabled:opacity-40"> type="checkbox"
class="checkbox checkbox-sm shrink-0"
bind:checked={use_theme} />
<label
for="ubld_theme"
class="w-24 cursor-pointer font-mono text-xs select-none"
class:opacity-35={!use_theme}>theme</label>
<select
bind:value={val_theme}
disabled={!use_theme}
class="select select-sm flex-1 text-xs disabled:opacity-40">
{#each theme_options as opt} {#each theme_options as opt}
<option value={opt.value}>{opt.label}</option> <option value={opt.value}>{opt.label}</option>
{/each} {/each}
@@ -133,9 +159,19 @@
<!-- theme_mode --> <!-- theme_mode -->
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<input id="ubld_theme_mode" type="checkbox" class="checkbox checkbox-sm shrink-0" bind:checked={use_theme_mode} /> <input
<label for="ubld_theme_mode" class="w-24 text-xs font-mono cursor-pointer select-none" class:opacity-35={!use_theme_mode}>theme_mode</label> id="ubld_theme_mode"
<select bind:value={val_theme_mode} disabled={!use_theme_mode} class="select select-sm text-xs flex-1 disabled:opacity-40"> type="checkbox"
class="checkbox checkbox-sm shrink-0"
bind:checked={use_theme_mode} />
<label
for="ubld_theme_mode"
class="w-24 cursor-pointer font-mono text-xs select-none"
class:opacity-35={!use_theme_mode}>theme_mode</label>
<select
bind:value={val_theme_mode}
disabled={!use_theme_mode}
class="select select-sm flex-1 text-xs disabled:opacity-40">
<option value="light">light</option> <option value="light">light</option>
<option value="dark">dark</option> <option value="dark">dark</option>
</select> </select>
@@ -143,27 +179,46 @@
<!-- key --> <!-- key -->
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<input id="ubld_key" type="checkbox" class="checkbox checkbox-sm shrink-0" bind:checked={use_key} /> <input
<label for="ubld_key" class="w-24 text-xs font-mono cursor-pointer select-none" class:opacity-35={!use_key}>key</label> id="ubld_key"
type="checkbox"
class="checkbox checkbox-sm shrink-0"
bind:checked={use_key} />
<label
for="ubld_key"
class="w-24 cursor-pointer font-mono text-xs select-none"
class:opacity-35={!use_key}>key</label>
<input <input
type="text" type="text"
bind:value={val_key} bind:value={val_key}
disabled={!use_key} disabled={!use_key}
placeholder="access key" placeholder="access key"
class="input input-sm text-xs font-mono flex-1 disabled:opacity-40" class="input input-sm flex-1 font-mono text-xs disabled:opacity-40" />
/>
</div> </div>
</div> </div>
<!-- ── Launcher params ─────────────────────────────────────────────── --> <!-- ── Launcher params ─────────────────────────────────────────────── -->
<div class="space-y-1.5 border-t border-surface-500/20 pt-2.5"> <div class="border-surface-500/20 space-y-1.5 border-t pt-2.5">
<p class="text-[9px] font-bold uppercase tracking-widest opacity-40 ml-0.5">Launcher</p> <p
class="ml-0.5 text-[9px] font-bold tracking-widest uppercase opacity-40">
Launcher
</p>
<!-- launcher_menu --> <!-- launcher_menu -->
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<input id="ubld_launcher_menu" type="checkbox" class="checkbox checkbox-sm shrink-0" bind:checked={use_launcher_menu} /> <input
<label for="ubld_launcher_menu" class="w-24 text-xs font-mono cursor-pointer select-none" class:opacity-35={!use_launcher_menu}>menu</label> id="ubld_launcher_menu"
<select bind:value={val_launcher_menu} disabled={!use_launcher_menu} class="select select-sm text-xs flex-1 disabled:opacity-40"> type="checkbox"
class="checkbox checkbox-sm shrink-0"
bind:checked={use_launcher_menu} />
<label
for="ubld_launcher_menu"
class="w-24 cursor-pointer font-mono text-xs select-none"
class:opacity-35={!use_launcher_menu}>menu</label>
<select
bind:value={val_launcher_menu}
disabled={!use_launcher_menu}
class="select select-sm flex-1 text-xs disabled:opacity-40">
<option value="hide">hide</option> <option value="hide">hide</option>
<option value="show">show</option> <option value="show">show</option>
</select> </select>
@@ -171,9 +226,19 @@
<!-- launcher_header --> <!-- launcher_header -->
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<input id="ubld_launcher_header" type="checkbox" class="checkbox checkbox-sm shrink-0" bind:checked={use_launcher_header} /> <input
<label for="ubld_launcher_header" class="w-24 text-xs font-mono cursor-pointer select-none" class:opacity-35={!use_launcher_header}>header</label> id="ubld_launcher_header"
<select bind:value={val_launcher_header} disabled={!use_launcher_header} class="select select-sm text-xs flex-1 disabled:opacity-40"> type="checkbox"
class="checkbox checkbox-sm shrink-0"
bind:checked={use_launcher_header} />
<label
for="ubld_launcher_header"
class="w-24 cursor-pointer font-mono text-xs select-none"
class:opacity-35={!use_launcher_header}>header</label>
<select
bind:value={val_launcher_header}
disabled={!use_launcher_header}
class="select select-sm flex-1 text-xs disabled:opacity-40">
<option value="hide">hide</option> <option value="hide">hide</option>
<option value="show">show</option> <option value="show">show</option>
</select> </select>
@@ -181,9 +246,19 @@
<!-- launcher_footer --> <!-- launcher_footer -->
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<input id="ubld_launcher_footer" type="checkbox" class="checkbox checkbox-sm shrink-0" bind:checked={use_launcher_footer} /> <input
<label for="ubld_launcher_footer" class="w-24 text-xs font-mono cursor-pointer select-none" class:opacity-35={!use_launcher_footer}>footer</label> id="ubld_launcher_footer"
<select bind:value={val_launcher_footer} disabled={!use_launcher_footer} class="select select-sm text-xs flex-1 disabled:opacity-40"> type="checkbox"
class="checkbox checkbox-sm shrink-0"
bind:checked={use_launcher_footer} />
<label
for="ubld_launcher_footer"
class="w-24 cursor-pointer font-mono text-xs select-none"
class:opacity-35={!use_launcher_footer}>footer</label>
<select
bind:value={val_launcher_footer}
disabled={!use_launcher_footer}
class="select select-sm flex-1 text-xs disabled:opacity-40">
<option value="hide">hide</option> <option value="hide">hide</option>
<option value="show">show</option> <option value="show">show</option>
</select> </select>
@@ -191,26 +266,33 @@
</div> </div>
<!-- ── Output ──────────────────────────────────────────────────────── --> <!-- ── Output ──────────────────────────────────────────────────────── -->
<div class="border-t border-surface-500/20 pt-2.5 space-y-1.5"> <div class="border-surface-500/20 space-y-1.5 border-t pt-2.5">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<input id="ubld_params_only" type="checkbox" class="checkbox checkbox-sm shrink-0" bind:checked={params_only} /> <input
<label for="ubld_params_only" class="text-xs cursor-pointer select-none" class:opacity-35={!params_only}>Params only</label> id="ubld_params_only"
type="checkbox"
class="checkbox checkbox-sm shrink-0"
bind:checked={params_only} />
<label
for="ubld_params_only"
class="cursor-pointer text-xs select-none"
class:opacity-35={!params_only}>Params only</label>
</div> </div>
<div class="flex gap-1 items-stretch"> <div class="flex items-stretch gap-1">
<input <input
type="text" type="text"
readonly readonly
value={output} value={output}
class="input input-sm text-xs font-mono flex-1 bg-surface-50/50 dark:bg-surface-700/50 cursor-text" class="input input-sm bg-surface-50/50 dark:bg-surface-700/50 flex-1 cursor-text font-mono text-xs"
onclick={(e) => (e.target as HTMLInputElement).select()} onclick={(e) => (e.target as HTMLInputElement).select()}
title="Click to select all" title="Click to select all" />
/>
<button <button
class="btn btn-sm {copied ? 'preset-filled-success' : 'preset-tonal-primary'} shrink-0 transition-all" class="btn btn-sm {copied
? 'preset-filled-success'
: 'preset-tonal-primary'} shrink-0 transition-all"
onclick={copy_url} onclick={copy_url}
title="Copy URL to clipboard" title="Copy URL to clipboard">
>
{#if copied} {#if copied}
<Check size="1em" /> <Check size="1em" />
{:else} {:else}
@@ -219,5 +301,4 @@
</button> </button>
</div> </div>
</div> </div>
</section> </section>

View File

@@ -1,57 +1,62 @@
<script lang="ts" module> <script lang="ts" module>
import type { WithElementRef } from 'bits-ui'; import type { WithElementRef } from 'bits-ui';
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements'; import type {
import { type VariantProps, tv } from 'tailwind-variants'; HTMLAnchorAttributes,
HTMLButtonAttributes
} from 'svelte/elements';
import { type VariantProps, tv } from 'tailwind-variants';
export const buttonVariants = tv({ export const buttonVariants = tv({
base: 'ring-offset-background focus-visible:ring-ring inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', base: 'ring-offset-background focus-visible:ring-ring inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
variants: { variants: {
variant: { variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90', default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90', destructive:
outline: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
'border-input bg-background hover:bg-accent hover:text-accent-foreground border', outline:
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80', 'border-input bg-background hover:bg-accent hover:text-accent-foreground border',
ghost: 'hover:bg-accent hover:text-accent-foreground', secondary:
link: 'text-primary underline-offset-4 hover:underline' 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
}, ghost: 'hover:bg-accent hover:text-accent-foreground',
size: { link: 'text-primary underline-offset-4 hover:underline'
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10'
}
}, },
defaultVariants: { size: {
variant: 'default', default: 'h-10 px-4 py-2',
size: 'default' sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10'
} }
}); },
defaultVariants: {
variant: 'default',
size: 'default'
}
});
export type ButtonVariant = VariantProps<typeof buttonVariants>['variant']; export type ButtonVariant = VariantProps<typeof buttonVariants>['variant'];
export type ButtonSize = VariantProps<typeof buttonVariants>['size']; export type ButtonSize = VariantProps<typeof buttonVariants>['size'];
export type ButtonProps = WithElementRef<HTMLButtonAttributes> & export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
WithElementRef<HTMLAnchorAttributes> & { WithElementRef<HTMLAnchorAttributes> & {
variant?: ButtonVariant; variant?: ButtonVariant;
size?: ButtonSize; size?: ButtonSize;
class?: any; class?: any;
}; };
</script> </script>
<script lang="ts"> <script lang="ts">
import { cn } from '$lib/utils/utils.js'; import { cn } from '$lib/utils/utils.js';
let { let {
class: className, class: className,
variant = 'default', variant = 'default',
size = 'default', size = 'default',
ref = $bindable(null), ref = $bindable(null),
href = undefined, href = undefined,
type = 'button', type = 'button',
children, children,
...restProps ...restProps
}: ButtonProps = $props(); }: ButtonProps = $props();
</script> </script>
{#if href} {#if href}
@@ -59,8 +64,7 @@
bind:this={ref} bind:this={ref}
class={cn(buttonVariants({ variant, size, className }))} class={cn(buttonVariants({ variant, size, className }))}
{href} {href}
{...restProps} {...restProps}>
>
{@render children?.()} {@render children?.()}
</a> </a>
{:else} {:else}
@@ -68,8 +72,7 @@
bind:this={ref} bind:this={ref}
class={cn(buttonVariants({ variant, size, className }))} class={cn(buttonVariants({ variant, size, className }))}
{type} {type}
{...restProps} {...restProps}>
>
{@render children?.()} {@render children?.()}
</button> </button>
{/if} {/if}

View File

@@ -1,20 +1,23 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive, type WithoutChildrenOrChild } from 'bits-ui'; import {
import Check from 'lucide-svelte/icons/check'; DropdownMenu as DropdownMenuPrimitive,
import Minus from 'lucide-svelte/icons/minus'; type WithoutChildrenOrChild
import { cn } from '$lib/utils/utils.js'; } from 'bits-ui';
import type { Snippet } from 'svelte'; import Check from 'lucide-svelte/icons/check';
import Minus from 'lucide-svelte/icons/minus';
import { cn } from '$lib/utils/utils.js';
import type { Snippet } from 'svelte';
let { let {
ref = $bindable(null), ref = $bindable(null),
checked = $bindable(false), checked = $bindable(false),
indeterminate = $bindable(false), indeterminate = $bindable(false),
class: className, class: className,
children: childrenProp, children: childrenProp,
...restProps ...restProps
}: WithoutChildrenOrChild<DropdownMenuPrimitive.CheckboxItemProps> & { }: WithoutChildrenOrChild<DropdownMenuPrimitive.CheckboxItemProps> & {
children?: Snippet; children?: Snippet;
} = $props(); } = $props();
</script> </script>
<DropdownMenuPrimitive.CheckboxItem <DropdownMenuPrimitive.CheckboxItem
@@ -22,11 +25,10 @@
bind:checked bind:checked
bind:indeterminate bind:indeterminate
class={cn( class={cn(
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden data-disabled:pointer-events-none data-disabled:opacity-50', 'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50',
className className
)} )}
{...restProps} {...restProps}>
>
{#snippet children({ checked, indeterminate })} {#snippet children({ checked, indeterminate })}
<span class="absolute left-2 flex size-3.5 items-center justify-center"> <span class="absolute left-2 flex size-3.5 items-center justify-center">
{#if indeterminate} {#if indeterminate}

View File

@@ -1,16 +1,16 @@
<script lang="ts"> <script lang="ts">
import { cn } from '$lib/utils/utils.js'; import { cn } from '$lib/utils/utils.js';
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let { let {
ref = $bindable(null), ref = $bindable(null),
sideOffset = 4, sideOffset = 4,
portalProps, portalProps,
class: className, class: className,
...restProps ...restProps
}: DropdownMenuPrimitive.ContentProps & { }: DropdownMenuPrimitive.ContentProps & {
portalProps?: DropdownMenuPrimitive.PortalProps; portalProps?: DropdownMenuPrimitive.PortalProps;
} = $props(); } = $props();
</script> </script>
<DropdownMenuPrimitive.Portal {...portalProps}> <DropdownMenuPrimitive.Portal {...portalProps}>
@@ -21,6 +21,5 @@
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-32 overflow-hidden rounded-md border p-1 shadow-md outline-hidden', 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-32 overflow-hidden rounded-md border p-1 shadow-md outline-hidden',
className className
)} )}
{...restProps} {...restProps} />
/>
</DropdownMenuPrimitive.Portal> </DropdownMenuPrimitive.Portal>

View File

@@ -1,19 +1,18 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/utils.js'; import { cn } from '$lib/utils/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
inset, inset,
...restProps ...restProps
}: DropdownMenuPrimitive.GroupHeadingProps & { }: DropdownMenuPrimitive.GroupHeadingProps & {
inset?: boolean; inset?: boolean;
} = $props(); } = $props();
</script> </script>
<DropdownMenuPrimitive.GroupHeading <DropdownMenuPrimitive.GroupHeading
bind:ref bind:ref
class={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)} class={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
{...restProps} {...restProps} />
/>

View File

@@ -1,23 +1,22 @@
<script lang="ts"> <script lang="ts">
import { cn } from '$lib/utils/utils.js'; import { cn } from '$lib/utils/utils.js';
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
inset, inset,
...restProps ...restProps
}: DropdownMenuPrimitive.ItemProps & { }: DropdownMenuPrimitive.ItemProps & {
inset?: boolean; inset?: boolean;
} = $props(); } = $props();
</script> </script>
<DropdownMenuPrimitive.Item <DropdownMenuPrimitive.Item
bind:ref bind:ref
class={cn( class={cn(
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden transition-colors data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', 'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden transition-colors select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
inset && 'pl-8', inset && 'pl-8',
className className
)} )}
{...restProps} {...restProps} />
/>

View File

@@ -1,23 +1,22 @@
<script lang="ts"> <script lang="ts">
import { cn } from '$lib/utils/utils.js'; import { cn } from '$lib/utils/utils.js';
import { type WithElementRef } from 'bits-ui'; import { type WithElementRef } from 'bits-ui';
import type { HTMLAttributes } from 'svelte/elements'; import type { HTMLAttributes } from 'svelte/elements';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
inset, inset,
children, children,
...restProps ...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & { }: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
inset?: boolean; inset?: boolean;
} = $props(); } = $props();
</script> </script>
<div <div
bind:this={ref} bind:this={ref}
class={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)} class={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
{...restProps} {...restProps}>
>
{@render children?.()} {@render children?.()}
</div> </div>

View File

@@ -1,24 +1,26 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive, type WithoutChild } from 'bits-ui'; import {
import Circle from 'lucide-svelte/icons/circle'; DropdownMenu as DropdownMenuPrimitive,
import { cn } from '$lib/utils/utils.js'; type WithoutChild
} from 'bits-ui';
import Circle from 'lucide-svelte/icons/circle';
import { cn } from '$lib/utils/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
children: childrenProp, children: childrenProp,
...restProps ...restProps
}: WithoutChild<DropdownMenuPrimitive.RadioItemProps> = $props(); }: WithoutChild<DropdownMenuPrimitive.RadioItemProps> = $props();
</script> </script>
<DropdownMenuPrimitive.RadioItem <DropdownMenuPrimitive.RadioItem
bind:ref bind:ref
class={cn( class={cn(
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden data-disabled:pointer-events-none data-disabled:opacity-50', 'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50',
className className
)} )}
{...restProps} {...restProps}>
>
{#snippet children({ checked })} {#snippet children({ checked })}
<span class="absolute left-2 flex size-3.5 items-center justify-center"> <span class="absolute left-2 flex size-3.5 items-center justify-center">
{#if checked} {#if checked}

View File

@@ -1,16 +1,15 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/utils.js'; import { cn } from '$lib/utils/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
...restProps ...restProps
}: DropdownMenuPrimitive.SeparatorProps = $props(); }: DropdownMenuPrimitive.SeparatorProps = $props();
</script> </script>
<DropdownMenuPrimitive.Separator <DropdownMenuPrimitive.Separator
bind:ref bind:ref
class={cn('bg-muted -mx-1 my-1 h-px', className)} class={cn('bg-muted -mx-1 my-1 h-px', className)}
{...restProps} {...restProps} />
/>

View File

@@ -1,20 +1,19 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from 'svelte/elements'; import type { HTMLAttributes } from 'svelte/elements';
import { type WithElementRef } from 'bits-ui'; import { type WithElementRef } from 'bits-ui';
import { cn } from '$lib/utils/utils.js'; import { cn } from '$lib/utils/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
children, children,
...restProps ...restProps
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props(); }: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
</script> </script>
<span <span
bind:this={ref} bind:this={ref}
class={cn('ml-auto text-xs tracking-widest opacity-60', className)} class={cn('ml-auto text-xs tracking-widest opacity-60', className)}
{...restProps} {...restProps}>
>
{@render children?.()} {@render children?.()}
</span> </span>

Some files were not shown because too many files have changed in this diff Show More