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,50 +114,50 @@ 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,17 +182,17 @@ 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);
@@ -216,15 +216,15 @@ html.dark[data-theme='AE_Firefly_Indigo'] {
* =================================================================== */ * =================================================================== */
--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);
@@ -248,15 +248,15 @@ html.dark[data-theme='AE_Firefly_Indigo'] {
* 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);
@@ -278,7 +278,7 @@ html.dark[data-theme='AE_Firefly_Indigo'] {
* 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,8 +286,8 @@ 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);
@@ -307,16 +307,16 @@ html.dark[data-theme='AE_Firefly_Indigo'] {
* 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);
@@ -335,16 +335,16 @@ 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);
@@ -370,17 +370,17 @@ 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);

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,49 +114,49 @@ 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,16 +180,16 @@ 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);
@@ -212,16 +212,16 @@ 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);
@@ -244,17 +244,17 @@ 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);
@@ -275,7 +275,7 @@ html.dark[data-theme='AE_Firefly_Rainbow'] {
* 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,8 +283,8 @@ 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);
@@ -304,16 +304,16 @@ html.dark[data-theme='AE_Firefly_Rainbow'] {
* 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);
@@ -332,16 +332,16 @@ 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);
@@ -367,15 +367,15 @@ html.dark[data-theme='AE_Firefly_Rainbow'] {
* 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);

View File

@@ -61,30 +61,30 @@ html[data-theme='AE_Firefly_SteelBlue'] {
--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);
@@ -92,18 +92,18 @@ html[data-theme='AE_Firefly_SteelBlue'] {
--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,50 +111,50 @@ 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);
} }
@@ -176,15 +176,15 @@ html.dark[data-theme='AE_Firefly_SteelBlue'] {
* 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);
@@ -207,16 +207,16 @@ html.dark[data-theme='AE_Firefly_SteelBlue'] {
* 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);
@@ -241,12 +241,12 @@ html.dark[data-theme='AE_Firefly_SteelBlue'] {
--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);
@@ -268,7 +268,7 @@ html.dark[data-theme='AE_Firefly_SteelBlue'] {
* 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,8 +276,8 @@ 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);
@@ -297,16 +297,16 @@ html.dark[data-theme='AE_Firefly_SteelBlue'] {
* 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);
@@ -325,16 +325,16 @@ 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);
@@ -359,17 +359,17 @@ 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);

View File

@@ -64,49 +64,49 @@ 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,47 +114,47 @@ 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);
@@ -175,16 +175,16 @@ html.dark[data-theme='AE_Firefly'] {
* ...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);
@@ -200,17 +200,17 @@ 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);
@@ -226,16 +226,16 @@ html.dark[data-theme='AE_Firefly'] {
--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);
@@ -251,7 +251,7 @@ html.dark[data-theme='AE_Firefly'] {
--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,8 +259,8 @@ 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);
@@ -276,16 +276,16 @@ html.dark[data-theme='AE_Firefly'] {
--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);
@@ -300,16 +300,16 @@ 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);
@@ -326,13 +326,13 @@ html.dark[data-theme='AE_Firefly'] {
--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);

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';
@@ -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'];
const is_valid_bypass =
bypass_val === 'bypass' ||
bypass_val === 'Nothing to See Here' || bypass_val === 'Nothing to See Here' ||
params['key'] || params['key'] ||
bypass_val === 'direct-download'; 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,7 +138,8 @@ 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'] || headers_cleaned['JWT'] ||
api_cfg['jwt'] || api_cfg['jwt'] ||
api_cfg['headers']?.['jwt'] || api_cfg['headers']?.['jwt'] ||
@@ -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(
`AUTH DIAGNOSTICS: Headers sent for ${endpoint}:`,
{
has_auth: !!headers_cleaned['Authorization'], has_auth: !!headers_cleaned['Authorization'],
has_api_key: !!headers_cleaned['x-aether-api-key'], has_api_key:
has_account_id: !!headers_cleaned['x-account-id'], !!headers_cleaned['x-aether-api-key'],
jwt_preview: jwt ? `${jwt.slice(0, 8)}...` : 'MISSING' 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'];
const is_valid_bypass =
bypass_val === 'bypass' ||
bypass_val === 'Nothing to See Here' || bypass_val === 'Nothing to See Here' ||
params['key'] || params['key'] ||
bypass_val === 'direct-download'; 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,7 +111,8 @@ 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'] || headers_cleaned['JWT'] ||
api_cfg['jwt'] || api_cfg['jwt'] ||
api_cfg['headers']?.['jwt'] || api_cfg['headers']?.['jwt'] ||
@@ -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(
`AUTH DIAGNOSTICS (PATCH): Headers sent for ${endpoint}:`,
{
has_auth: !!headers_cleaned['Authorization'], has_auth: !!headers_cleaned['Authorization'],
has_api_key: !!headers_cleaned['x-aether-api-key'], has_api_key:
has_account_id: !!headers_cleaned['x-account-id'], !!headers_cleaned['x-aether-api-key'],
jwt_preview: jwt ? `${jwt.slice(0, 8)}...` : 'MISSING' 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'];
const is_valid_bypass =
bypass_val === 'bypass' ||
bypass_val === 'Nothing to See Here' || bypass_val === 'Nothing to See Here' ||
params['key'] || params['key'] ||
bypass_val === 'direct-download'; 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,7 +133,8 @@ 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'] || headers_cleaned['JWT'] ||
api_cfg['jwt'] || api_cfg['jwt'] ||
api_cfg['headers']?.['jwt'] || api_cfg['headers']?.['jwt'] ||
@@ -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(
`AUTH DIAGNOSTICS (POST): Headers sent for ${endpoint}:`,
{
has_auth: !!headers_cleaned['Authorization'], has_auth: !!headers_cleaned['Authorization'],
has_api_key: !!headers_cleaned['x-aether-api-key'], has_api_key:
has_account_id: !!headers_cleaned['x-account-id'], !!headers_cleaned['x-aether-api-key'],
jwt_preview: jwt ? `${jwt.slice(0, 8)}...` : 'MISSING' 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,7 +54,8 @@ 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 =
await process_ae_obj__archive_props({
obj_li: [archive_obj_get_result], obj_li: [archive_obj_get_result],
log_lvl: log_lvl log_lvl: log_lvl
}); });
@@ -76,7 +79,8 @@ 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 =
await load_ae_obj_li__archive_content({
api_cfg: api_cfg, api_cfg: api_cfg,
for_obj_type: 'archive', for_obj_type: 'archive',
for_obj_id: archive_id, for_obj_id: archive_id,
@@ -88,7 +92,8 @@ export async function load_ae_obj_id__archive({
try_cache: try_cache, try_cache: try_cache,
log_lvl: log_lvl 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,7 +168,8 @@ 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 =
await process_ae_obj__archive_props({
obj_li: archive_obj_li_get_result, obj_li: archive_obj_li_get_result,
log_lvl: log_lvl log_lvl: log_lvl
}); });
@@ -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,7 +41,8 @@ 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 =
await process_ae_obj__archive_content_props({
obj_li: [archive_content_obj_get_result], obj_li: [archive_content_obj_get_result],
log_lvl: log_lvl log_lvl: log_lvl
}); });
@@ -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,7 +127,8 @@ 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 =
await process_ae_obj__archive_content_props({
obj_li: archive_content_obj_li_get_result, obj_li: archive_content_obj_li_get_result,
log_lvl: log_lvl log_lvl: log_lvl
}); });
@@ -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,14 +1,14 @@
<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,
@@ -16,19 +16,25 @@
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;
@@ -44,9 +50,9 @@
// 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(),
@@ -58,39 +64,39 @@
// 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 // *** Functions and Logic
function prevent_default<T extends Event>(fn: (event: T) => void) { function prevent_default<T extends Event>(fn: (event: T) => void) {
return function (event: T) { return function (event: T) {
event.preventDefault(); event.preventDefault();
fn(event); fn(event);
}; };
} }
function handle_clip_video(event: Event) { function handle_clip_video(event: Event) {
console.log('*** handle_clip_video() ***'); console.log('*** handle_clip_video() ***');
submit_status = 'clipping'; submit_status = 'clipping';
@@ -107,20 +113,17 @@
let new_filename = formData.get('new_filename') 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] = {};
$ae_sess.files.processed_file_kv[hosted_file_id].submit_status = $ae_sess.files.processed_file_kv[hosted_file_id].submit_status = 'clipping';
'clipping';
$ae_sess.files.processed_file_kv[hosted_file_id].clip_complete = false; $ae_sess.files.processed_file_kv[hosted_file_id].clip_complete = false;
// $ae_sess.files.disable_submit__hosted_file_obj = true; // $ae_sess.files.disable_submit__hosted_file_obj = true;
$ae_loc.files.processed_file_kv[hosted_file_id] = {}; $ae_loc.files.processed_file_kv[hosted_file_id] = {};
$ae_loc.files.processed_file_kv[hosted_file_id].submit_status = $ae_loc.files.processed_file_kv[hosted_file_id].submit_status = 'clipping';
'clipping';
$ae_loc.files.processed_file_kv[hosted_file_id].start_time = start_time; $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].end_time = end_time;
$ae_loc.files.processed_file_kv[hosted_file_id].reencode = reencode; $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].scale_down = scale_down;
$ae_loc.files.processed_file_kv[hosted_file_id].new_filename = $ae_loc.files.processed_file_kv[hosted_file_id].new_filename = new_filename;
new_filename;
$ae_loc.files.processed_file_kv[hosted_file_id].clip_complete = false; $ae_loc.files.processed_file_kv[hosted_file_id].clip_complete = false;
let endpoint = `/hosted_file/${hosted_file_id}/clip_video`; let endpoint = `/hosted_file/${hosted_file_id}/clip_video`;
@@ -182,7 +185,7 @@
return true; 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,14 +1,14 @@
<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,
@@ -16,14 +16,14 @@
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;
@@ -31,18 +31,18 @@
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,19 +1,15 @@
<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;
@@ -26,16 +22,23 @@
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?:
| 'primary'
| 'secondary'
| 'tertiary'
| 'success'
| 'warning'
| 'error'
| 'surface';
show_divider?: boolean; show_divider?: boolean;
show_direct_download?: boolean; show_direct_download?: boolean;
require_auth?: boolean; require_auth?: boolean;
classes?: string; classes?: string;
click?: () => void | Promise<any>; click?: () => void | Promise<any>;
label?: import('svelte').Snippet; label?: import('svelte').Snippet;
} }
let { let {
log_lvl = 0, log_lvl = 0,
hosted_file_id, hosted_file_id,
hosted_file_obj, hosted_file_obj,
@@ -55,10 +58,10 @@
classes = '', classes = '',
click, click,
label label
}: Props = $props(); }: Props = $props();
// Map variant/color to classes using literal strings so Tailwind can find them // Map variant/color to classes using literal strings so Tailwind can find them
const color_map: Record<string, Record<string, string>> = { const color_map: Record<string, Record<string, string>> = {
primary: { primary: {
tonal: 'preset-tonal-primary border border-primary-500/30 hover:preset-filled-primary-500', tonal: 'preset-tonal-primary border border-primary-500/30 hover:preset-filled-primary-500',
filled: 'preset-filled-primary-500 hover:preset-filled-primary-600', filled: 'preset-filled-primary-500 hover:preset-filled-primary-600',
@@ -101,40 +104,47 @@
outline: 'border border-surface-500 hover:preset-tonal-surface', outline: 'border border-surface-500 hover:preset-tonal-surface',
ghost: 'hover:preset-tonal-surface' ghost: 'hover:preset-tonal-surface'
} }
}; };
let variant_classes = $derived.by(() => { let variant_classes = $derived.by(() => {
const base = 'btn btn-sm lg:btn-md min-w-48 transition-all overflow-hidden px-3'; 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; const style = color_map[color]?.[variant] || color_map.primary.tonal;
return `${base} ${style} ${classes}`.trim(); return `${base} ${style} ${classes}`.trim();
}); });
let show_filename_view = $state(true); let show_filename_view = $state(true);
let status_interval: any; let status_interval: any;
$effect(() => { $effect(() => {
if (log_lvl) { if (log_lvl) {
console.log( console.log(
`ae_comp__hosted_files_download_button.svelte hosted_file_id=${hosted_file_id}`, `ae_comp__hosted_files_download_button.svelte hosted_file_id=${hosted_file_id}`,
hosted_file_obj hosted_file_obj
); );
} }
}); });
let ae_promises: key_val = $state({}); let ae_promises: key_val = $state({});
$effect(() => { $effect(() => {
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 (file_id && $ae_sess?.api_download_kv[file_id]?.percent_completed) { if (file_id && $ae_sess?.api_download_kv[file_id]?.percent_completed) {
download_percent = download_percent = $ae_sess.api_download_kv[file_id].percent_completed;
$ae_sess.api_download_kv[file_id].percent_completed;
} }
}); });
// Reactive timer to alternate views during active download // Reactive timer to alternate views during active download
$effect(() => { $effect(() => {
const file_id = hosted_file_obj?.id || hosted_file_obj?.hosted_file_id || hosted_file_id; const file_id =
const is_actively_downloading = ae_promises[file_id] && download_complete === undefined; 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 (is_actively_downloading) {
if (!status_interval) { if (!status_interval) {
@@ -156,25 +166,37 @@
status_interval = null; status_interval = null;
} }
}; };
}); });
let final_filename = $derived(filename ?? hosted_file_obj?.filename ?? 'unknown'); let final_filename = $derived(
let shortened_filename = $derived(ae_util.shorten_filename({ 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 ||
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}`; 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 =
hosted_file_obj?.id ||
hosted_file_obj?.hosted_file_id ||
hosted_file_id;
download_complete = undefined; download_complete = undefined;
download_status_msg = 'Downloading...'; download_status_msg = 'Downloading...';
@@ -194,8 +216,7 @@
filename: final_filename, filename: final_filename,
auto_download: auto_download, auto_download: auto_download,
log_lvl: log_lvl log_lvl: log_lvl
}) }).then((result) => {
.then((result) => {
if (result === null) { if (result === null) {
console.log('File not found (404)'); console.log('File not found (404)');
download_complete = null; download_complete = null;
@@ -213,31 +234,42 @@
} }
return result; 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,15 +1,15 @@
<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,
@@ -17,11 +17,11 @@
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;
@@ -40,9 +40,9 @@
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 { let {
log_lvl = 0, log_lvl = 0,
link_to_type, link_to_type,
link_to_id, link_to_id,
@@ -60,30 +60,30 @@
hosted_file_obj_li = $bindable([]), hosted_file_obj_li = $bindable([]),
hosted_file_obj_kv = $bindable({}), hosted_file_obj_kv = $bindable({}),
label label
}: Props = $props(); }: Props = $props();
// Local Variables // Local Variables
let task_id: string = $state(''); let task_id: string = $state('');
let input_file_list: any = $state(null); let input_file_list: any = $state(null);
let ae_promises: key_val = $state({}); // Promise<any>; let ae_promises: key_val = $state({}); // Promise<any>;
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';
$effect(() => { $effect(() => {
if (log_lvl) { if (log_lvl) {
console.log(`*** ae_comp__hosted_files_upload.svelte ***`); console.log(`*** ae_comp__hosted_files_upload.svelte ***`);
console.log(`link_to_type: ${link_to_type} link_to_id: ${link_to_id}`); console.log(`link_to_type: ${link_to_type} link_to_id: ${link_to_id}`);
} }
}); });
$effect(() => { $effect(() => {
// Sync task_id with link_to_id prop so it resets when navigating to a different object. // Sync task_id with link_to_id prop so it resets when navigating to a different object.
task_id = link_to_id; task_id = link_to_id;
}); });
// *** Functions and Logic // *** Functions and Logic
async function handle_submit_form_files(event: SubmitEvent) { async function handle_submit_form_files(event: SubmitEvent) {
console.log('*** handle_submit_form() ***'); console.log('*** handle_submit_form() ***');
event.preventDefault(); event.preventDefault();
@@ -103,7 +103,9 @@
let hosted_file_results; let hosted_file_results;
const target = event.currentTarget as HTMLFormElement; const target = event.currentTarget as HTMLFormElement;
const file_input = target ? (target[input_element_id] as HTMLInputElement) : null; const file_input = target
? (target[input_element_id] as HTMLInputElement)
: null;
if ( if (
target && target &&
@@ -140,7 +142,10 @@
// await tick(); // await tick();
if (log_lvl) { if (log_lvl) {
console.log(`hosted_file_id_li: ${hosted_file_id_li}`, hosted_file_id_li); console.log(
`hosted_file_id_li: ${hosted_file_id_li}`,
hosted_file_id_li
);
} else if (log_lvl > 1) { } else if (log_lvl > 1) {
console.log('hosted_file_results:', hosted_file_results); console.log('hosted_file_results:', hosted_file_results);
} }
@@ -150,15 +155,15 @@
$ae_sess.files.submit_status = 'saved'; $ae_sess.files.submit_status = 'saved';
submit_status = 'saved'; submit_status = 'saved';
upload_complete = true; upload_complete = true;
} }
async function handle_input_upload_files({ async function handle_input_upload_files({
input_upload_files, input_upload_files,
task_id task_id
}: { }: {
input_upload_files: any[]; input_upload_files: any[];
task_id: string; task_id: string;
}) { }) {
console.log('*** handle_input_upload_files() ***'); console.log('*** handle_input_upload_files() ***');
const form_data = new FormData(); const form_data = new FormData();
@@ -268,14 +273,14 @@
let hosted_file_result = ae_promises.upload__hosted_file_obj; let hosted_file_result = ae_promises.upload__hosted_file_obj;
return hosted_file_result; 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,24 +1,36 @@
<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);
@@ -27,36 +39,37 @@
} }
} }
if (!cfg_json) cfg_json = {}; if (!cfg_json) cfg_json = {};
}); });
// Internal State // Internal State
let active_tab: 'visuals' | 'email' | 'ai' | 'refresh' | 'idaa' | 'raw' = $state('visuals'); let active_tab: 'visuals' | 'email' | 'ai' | 'refresh' | 'idaa' | 'raw' =
let raw_json_str = $state(''); $state('visuals');
let raw_json_str = $state('');
// Ensure we have a valid object // Ensure we have a valid object
if (!cfg_json) cfg_json = {}; if (!cfg_json) cfg_json = {};
function add_to_list(key: string) { function add_to_list(key: string) {
if (!cfg_json[key]) cfg_json[key] = []; if (!cfg_json[key]) cfg_json[key] = [];
const val = prompt('Enter Novi UUID:'); const val = prompt('Enter Novi UUID:');
if (val) cfg_json[key].push(val); if (val) cfg_json[key].push(val);
} }
function remove_from_list(key: string, index: number) { function remove_from_list(key: string, index: number) {
cfg_json[key].splice(index, 1); cfg_json[key].splice(index, 1);
} }
// Sync Raw JSON string when entering the tab // Sync Raw JSON string when entering the tab
$effect(() => { $effect(() => {
if (active_tab === 'raw') { if (active_tab === 'raw') {
untrack(() => { untrack(() => {
raw_json_str = JSON.stringify(cfg_json, null, 2); raw_json_str = JSON.stringify(cfg_json, null, 2);
}); });
} }
}); });
// Update cfg_json when raw string changes // Update cfg_json when raw string changes
$effect(() => { $effect(() => {
if (active_tab === 'raw' && raw_json_str) { if (active_tab === 'raw' && raw_json_str) {
try { try {
const parsed = JSON.parse(raw_json_str); const parsed = JSON.parse(raw_json_str);
@@ -65,171 +78,281 @@
// Ignore invalid JSON while typing // 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,7 +41,8 @@ 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 =
await process_ae_obj__account_props({
obj_li: [account_obj_get_result], obj_li: [account_obj_get_result],
log_lvl: log_lvl log_lvl: log_lvl
}); });
@@ -114,7 +117,8 @@ 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 =
await process_ae_obj__account_props({
obj_li: account_obj_li_get_result, obj_li: account_obj_li_get_result,
log_lvl: log_lvl log_lvl: log_lvl
}); });
@@ -164,7 +168,8 @@ 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 =
await process_ae_obj__account_props({
obj_li: [account_obj_create_result], obj_li: [account_obj_create_result],
log_lvl: log_lvl log_lvl: log_lvl
}); });
@@ -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,17 +23,22 @@ 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
.get_ae_obj({
api_cfg, api_cfg,
obj_type: 'address', obj_type: 'address',
obj_id: address_id, obj_id: address_id,
view, view,
params, params,
log_lvl log_lvl
}).then(async (result) => { })
.then(async (result) => {
if (result) { if (result) {
if (try_cache) { if (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',
@@ -77,7 +82,8 @@ 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
.get_ae_obj_li({
api_cfg, api_cfg,
obj_type: 'address', obj_type: 'address',
for_obj_type, for_obj_type,
@@ -89,10 +95,14 @@ export async function load_ae_obj_li__address({
offset, offset,
order_by_li, order_by_li,
log_lvl log_lvl
}).then(async (result) => { })
.then(async (result) => {
if (result && Array.isArray(result)) { if (result && Array.isArray(result)) {
if (try_cache) { if (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',
@@ -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,17 +23,22 @@ 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
.get_ae_obj({
api_cfg, api_cfg,
obj_type: 'contact', obj_type: 'contact',
obj_id: contact_id, obj_id: contact_id,
view, view,
params, params,
log_lvl log_lvl
}).then(async (result) => { })
.then(async (result) => {
if (result) { if (result) {
if (try_cache) { if (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',
@@ -75,7 +80,8 @@ 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
.get_ae_obj_li({
api_cfg, api_cfg,
obj_type: 'contact', obj_type: 'contact',
for_obj_type, for_obj_type,
@@ -87,10 +93,14 @@ export async function load_ae_obj_li__contact({
offset, offset,
order_by_li, order_by_li,
log_lvl log_lvl
}).then(async (result) => { })
.then(async (result) => {
if (result && Array.isArray(result)) { if (result && Array.isArray(result)) {
if (try_cache) { if (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',
@@ -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], 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_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,13 +173,16 @@ 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(
async function (result_li) {
if (result_li) { if (result_li) {
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_li, obj_li: result_li,
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: 'person', table_name: 'person',
@@ -180,7 +195,8 @@ export async function load_ae_obj_li__person({
} else { } else {
return []; 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,7 +499,8 @@ 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 =
await process_ae_obj__site_domain_props({
obj_li: domain_li, obj_li: domain_li,
site_id, site_id,
log_lvl log_lvl
@@ -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,7 +38,8 @@ 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 =
await process_ae_obj__hosted_file_props({
obj_li: [ae_promises.load__hosted_file_obj], obj_li: [ae_promises.load__hosted_file_obj],
log_lvl log_lvl
}); });
@@ -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,7 +116,8 @@ 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 =
await process_ae_obj__hosted_file_props({
obj_li: ae_promises.load__hosted_file_obj_li, obj_li: ae_promises.load__hosted_file_obj_li,
log_lvl log_lvl
}); });
@@ -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

@@ -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

@@ -1,20 +1,28 @@
<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)
@@ -34,9 +42,9 @@
// UI Customization // UI Customization
buttonClass?: string; buttonClass?: string;
log_lvl?: number; log_lvl?: number;
} }
let { let {
content, content,
summary = $bindable(), summary = $bindable(),
model = $bindable(), model = $bindable(),
@@ -47,25 +55,25 @@
temperature = $bindable(), temperature = $bindable(),
onSave, onSave,
onSyncConfig, onSyncConfig,
buttonClass = "btn btn-sm preset-tonal-primary shadow-lg hover:scale-105 transition-all", buttonClass = 'btn btn-sm preset-tonal-primary shadow-lg hover:scale-105 transition-all',
log_lvl = 0 log_lvl = 0
}: Props = $props(); }: Props = $props();
// Apply defaults if undefined (Safe for Svelte 5 Runes) // Apply defaults if undefined (Safe for Svelte 5 Runes)
if (model === undefined) model = 'dgrzone-deepseek-8b-quick'; if (model === undefined) model = 'dgrzone-deepseek-8b-quick';
if (baseUrl === undefined) baseUrl = 'https://ai.dgrzone.com/api'; if (baseUrl === undefined) baseUrl = 'https://ai.dgrzone.com/api';
if (token === undefined) token = ''; if (token === undefined) token = '';
if (systemPrompt === undefined) systemPrompt = 'You are a helpful assistant.'; if (systemPrompt === undefined) systemPrompt = 'You are a helpful assistant.';
if (maxTokens === undefined) maxTokens = 512; if (maxTokens === undefined) maxTokens = 512;
if (temperature === undefined) temperature = 0.7; if (temperature === undefined) temperature = 0.7;
// Internal State // Internal State
let ae_promises: any = $state(null); let ae_promises: any = $state(null);
let show_modal = $state(false); let show_modal = $state(false);
let active_tab: 'result' | 'settings' = $state('result'); let active_tab: 'result' | 'settings' = $state('result');
let tmp_summary = $state(''); let tmp_summary = $state('');
async function generate_ai_result() { async function generate_ai_result() {
if (!content) { if (!content) {
alert('No content available to analyze.'); alert('No content available to analyze.');
return; return;
@@ -93,16 +101,23 @@
}); });
try { try {
ae_promises = ai_client.chat.completions.create({ 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;
}); });
@@ -113,13 +128,13 @@
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,17 +1,22 @@
<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,
Fingerprint,
Globe,
BookHeart,
BriefcaseBusiness,
NotepadTextDashed,
Settings Settings
} from '@lucide/svelte'; } from '@lucide/svelte';
import { ae_loc } from '$lib/stores/ae_stores'; 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;
@@ -29,9 +34,9 @@
// 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,
@@ -41,18 +46,19 @@
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,5 +1,5 @@
<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
@@ -19,7 +19,7 @@
* on_delete={(method) => { ... }} * on_delete={(method) => { ... }}
* /> * />
*/ */
import { import {
Star, Star,
Eye, Eye,
EyeOff, EyeOff,
@@ -30,9 +30,9 @@
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;
@@ -56,9 +56,9 @@
// Styling // Styling
container_class?: string; container_class?: string;
} }
let { let {
obj, obj,
obj_label = 'record', obj_label = 'record',
show_alert = true, show_alert = true,
@@ -71,25 +71,26 @@
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,16 +225,19 @@ 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(
load_ae_obj_li__event_device({
api_cfg, api_cfg,
for_obj_type: 'event', for_obj_type: 'event',
for_obj_id: current_event_id, for_obj_id: current_event_id,
try_cache, try_cache,
log_lvl log_lvl
}).then(res => event_obj.event_device_obj_li = res)); }).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(
load_ae_obj_li__event_file({
api_cfg, api_cfg,
for_obj_type: 'event', for_obj_type: 'event',
for_obj_id: current_event_id, for_obj_id: current_event_id,
@@ -168,10 +245,12 @@ async function _handle_nested_loads(event_obj: any, { api_cfg, inc_device_li, in
limit: 100, limit: 100,
try_cache, try_cache,
log_lvl log_lvl
}).then(res => event_obj.event_file_li = res)); }).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(
load_ae_obj_li__event_location({
api_cfg, api_cfg,
for_obj_type: 'event', for_obj_type: 'event',
for_obj_id: current_event_id, for_obj_id: current_event_id,
@@ -179,10 +258,12 @@ async function _handle_nested_loads(event_obj: any, { api_cfg, inc_device_li, in
hidden, hidden,
try_cache, try_cache,
log_lvl log_lvl
}).then(res => event_obj.event_location_obj_li = res)); }).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(
load_ae_obj_li__event_session({
api_cfg, api_cfg,
for_obj_type: 'event', for_obj_type: 'event',
for_obj_id: current_event_id, for_obj_id: current_event_id,
@@ -192,15 +273,18 @@ async function _handle_nested_loads(event_obj: any, { api_cfg, inc_device_li, in
hidden, hidden,
try_cache, try_cache,
log_lvl log_lvl
}).then(res => event_obj.event_session_obj_li = res)); }).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(
load_ae_obj_li__event_badge_template({
api_cfg, api_cfg,
event_id: current_event_id, event_id: current_event_id,
try_cache, try_cache,
log_lvl log_lvl
}).then(res => event_obj.event_badge_template_obj_li = res)); }).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,7 +439,8 @@ 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(
(event_obj: any) => {
const current_event_id = event_obj.id || event_obj.event_id; const current_event_id = event_obj.id || event_obj.event_id;
return load_ae_obj_li__event_session({ return load_ae_obj_li__event_session({
api_cfg, api_cfg,
@@ -351,7 +451,8 @@ export async function load_ae_obj_li__event({
try_cache, try_cache,
log_lvl log_lvl
}).then((res) => (event_obj.event_session_obj_li = res)); }).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,12 +35,13 @@ 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,
@@ -52,10 +53,16 @@ export async function load_ae_obj_id__event_badge({
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(
`Saving to local cache... Event ID: ${event_id} or ${ae_promises.load__event_badge_obj.event_id}`
);
const processed_obj_li =
await process_ae_obj__event_badge_props({
obj_li: [ae_promises.load__event_badge_obj], obj_li: [ae_promises.load__event_badge_obj],
event_id: event_id || ae_promises.load__event_badge_obj.event_id, event_id:
event_id ||
ae_promises.load__event_badge_obj.event_id,
log_lvl log_lvl
}); });
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
@@ -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,9 +100,11 @@ 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 =
await load_ae_obj_id__event_badge_template({
api_cfg: api_cfg, api_cfg: api_cfg,
event_badge_template_id: current_template_id, event_badge_template_id: current_template_id,
try_cache: try_cache, try_cache: try_cache,
@@ -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,12 +150,13 @@ 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',
@@ -151,7 +172,8 @@ export async function load_ae_obj_li__event_badge({
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 =
await process_ae_obj__event_badge_props({
obj_li: ae_promises.load__event_badge_obj_li, obj_li: ae_promises.load__event_badge_obj_li,
event_id, event_id,
log_lvl log_lvl
@@ -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,7 +221,8 @@ 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 =
await load_ae_obj_id__event_badge_template({
api_cfg: api_cfg, api_cfg: api_cfg,
event_badge_template_id: current_template_id, event_badge_template_id: current_template_id,
try_cache: try_cache, try_cache: try_cache,
@@ -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,13 +534,17 @@ 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 =
await process_ae_obj__event_badge_props({
obj_li: result_li, obj_li: result_li,
event_id, event_id,
log_lvl log_lvl
@@ -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,7 +205,8 @@ 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, api_cfg,
obj_type: 'event_badge_template', obj_type: 'event_badge_template',
for_obj_type: 'event', for_obj_type: 'event',
@@ -209,7 +218,8 @@ export async function load_ae_obj_li__event_badge_template({
offset, offset,
order_by_li, order_by_li,
log_lvl 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,14 +236,18 @@ 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
.where('event_id')
.equals(event_id)
.toArray(); .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
.where('event_id')
.equals(event_id)
.toArray(); .toArray();
} }
} }
@@ -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,21 +59,25 @@ 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 =
await load_ae_obj_id__event_location({
api_cfg, api_cfg,
event_location_id: current_location_id, event_location_id: current_location_id,
try_cache, try_cache,
@@ -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,7 +175,8 @@ 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 =
await load_ae_obj_id__event_location({
api_cfg, api_cfg,
event_location_id: device.event_location_id, event_location_id: device.event_location_id,
try_cache, try_cache,
@@ -198,7 +209,9 @@ export async function create_ae_obj__event_device({
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({
@@ -254,7 +267,9 @@ export async function delete_ae_obj_id__event_device({
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({
@@ -296,7 +311,9 @@ export async function update_ae_obj__event_device({
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({
@@ -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 = {},
@@ -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({
api_cfg,
for_obj_type: 'event_location',
for_obj_id: current_location_id,
inc_file_li: inc_all_file_li, inc_file_li: inc_all_file_li,
inc_all_file_li: inc_all_file_li, inc_all_file_li: inc_all_file_li,
inc_presentation_li: inc_presentation_li, inc_presentation_li: inc_presentation_li,
inc_presenter_li: inc_presenter_li, inc_presenter_li: inc_presenter_li,
enabled: 'enabled', hidden: 'not_hidden', limit: 150, log_lvl enabled: 'enabled',
}).then(res => location_obj.event_session_obj_li = res)); 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);
@@ -251,7 +422,10 @@ export async function create_ae_obj__event_location({
}); });
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({ await db_save_ae_obj_li__ae_obj({
@@ -322,7 +496,10 @@ export async function update_ae_obj__event_location({
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({
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({ await db_save_ae_obj_li__ae_obj({
@@ -340,21 +517,66 @@ export async function update_ae_obj__event_location({
// 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,
log_lvl = 0
}: {
obj_li: any[];
log_lvl?: number;
}) {
return _process_generic_props({
obj_li,
obj_type: 'event_location',
log_lvl,
specific_processor: (obj) => {
if (obj.event_id_random) obj.event_id = obj.event_id_random; if (obj.event_id_random) obj.event_id = obj.event_id_random;
return obj; 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;
} }
@@ -228,7 +414,10 @@ export async function create_ae_obj__event_presentation({
}); });
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({ await db_save_ae_obj_li__ae_obj({
@@ -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,
log_lvl = 0
}: {
obj_li: any[];
log_lvl?: number;
}) {
return _process_generic_props({
obj_li,
obj_type: 'event_presentation',
log_lvl,
specific_processor: (obj) => {
// Ensure linking IDs are the string versions for indexing // 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_session_id_random)
obj.event_session_id = obj.event_session_id_random;
if (obj.event_id_random) obj.event_id = obj.event_id_random; if (obj.event_id_random) obj.event_id = obj.event_id_random;
return obj; 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
}); });
}); });
} }
@@ -192,9 +311,12 @@ export async function create_ae_obj__event_presenter({
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({
@@ -207,7 +329,10 @@ export async function create_ae_obj__event_presenter({
}); });
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({ await db_save_ae_obj_li__ae_obj({
@@ -239,9 +364,12 @@ export async function delete_ae_obj_id__event_presenter({
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({
@@ -273,9 +401,12 @@ export async function update_ae_obj__event_presenter({
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({
@@ -288,7 +419,10 @@ export async function update_ae_obj__event_presenter({
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({
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({ await db_save_ae_obj_li__ae_obj({
@@ -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,
log_lvl = 0
}: {
obj_li: any[];
log_lvl?: number;
}) {
return _process_generic_props({
obj_li,
obj_type: 'event_presenter',
log_lvl,
specific_processor: (obj) => {
// String-Only ID Vision: Ensure linking IDs are the string versions for indexing // 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_presenter_id_random)
if (obj.event_presentation_id_random) obj.event_presentation_id = obj.event_presentation_id_random; obj.event_presenter_id = obj.event_presenter_id_random;
if (obj.event_session_id_random) obj.event_session_id = obj.event_session_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; if (obj.event_id_random) obj.event_id = obj.event_id_random;
return obj; 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,65 +343,164 @@ 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 [];
} }
@@ -291,7 +532,10 @@ export async function create_ae_obj__event_session({
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({
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({ await db_save_ae_obj_li__ae_obj({
@@ -370,7 +614,10 @@ export async function update_ae_obj__event_session({
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({
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({ await db_save_ae_obj_li__ae_obj({
@@ -387,40 +634,118 @@ export async function update_ae_obj__event_session({
} }
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 [];
} }
@@ -317,7 +420,10 @@ export async function create_ae_obj__exhibit({
}); });
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];
if (try_cache) { if (try_cache) {
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
@@ -362,7 +468,10 @@ export async function update_ae_obj__exhibit({
}); });
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];
if (try_cache) { if (try_cache) {
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
@@ -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 [];
} }
@@ -338,7 +442,10 @@ export async function create_ae_obj__exhibit_tracking({
}); });
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];
if (try_cache) { if (try_cache) {
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
@@ -383,7 +490,10 @@ export async function update_ae_obj__exhibit_tracking({
}); });
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];
if (try_cache) { if (try_cache) {
await db_save_ae_obj_li__ae_obj({ await db_save_ae_obj_li__ae_obj({
@@ -428,7 +538,9 @@ 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`;
@@ -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,7 +16,7 @@
/* --- 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;
@@ -28,7 +28,7 @@
/* 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

@@ -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,7 +74,8 @@ 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 =
await load_ae_obj_li__post_comment({
api_cfg: api_cfg, api_cfg: api_cfg,
for_obj_type: 'post', for_obj_type: 'post',
for_obj_id: post_id, for_obj_id: post_id,
@@ -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,7 +173,8 @@ 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 =
await load_ae_obj_li__post_comment({
api_cfg: api_cfg, api_cfg: api_cfg,
for_obj_type: 'post', for_obj_type: 'post',
for_obj_id: post_obj.post_id, for_obj_id: post_obj.post_id,
@@ -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,7 +442,8 @@ 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 =
await load_ae_obj_li__post_comment({
api_cfg: api_cfg, api_cfg: api_cfg,
for_obj_type: 'post', for_obj_type: 'post',
for_obj_id: post_obj.post_id, for_obj_id: post_obj.post_id,
@@ -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,7 +41,8 @@ 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 =
await process_ae_obj__post_comment_props({
obj_li: [post_comment_obj_get_result], obj_li: [post_comment_obj_get_result],
log_lvl: log_lvl log_lvl: log_lvl
}); });
@@ -121,7 +124,8 @@ 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 =
await process_ae_obj__post_comment_props({
obj_li: post_comment_obj_li_get_result, obj_li: post_comment_obj_li_get_result,
log_lvl: log_lvl log_lvl: log_lvl
}); });
@@ -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';
@@ -41,9 +44,7 @@ export async function qry__jitsi_report({
{ 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({
@@ -61,12 +62,21 @@ export async function qry__jitsi_report({
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 (
result &&
typeof result === 'object' &&
Array.isArray((result as any).data)
) {
flat_log_list = (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 ||
!Array.isArray(flat_log_list) ||
flat_log_list.length === 0
) {
if (log_lvl)
console.log('No Jitsi activity logs found or error occurred.');
return []; return [];
} }
@@ -117,7 +127,8 @@ export async function qry__jitsi_report({
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 { } else {
// This is a discrete event log. // This is a discrete event log.
@@ -131,11 +142,18 @@ export async function qry__jitsi_report({
// 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);

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(
alphanumericPattern,
function (match: string) {
return match.toUpperCase(); 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,7 +476,8 @@ 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 =
async function get_data_store_obj_w_code({
api_cfg, api_cfg,
data_store_code, data_store_code,
data_type = 'text', data_type = 'text',
@@ -481,7 +485,7 @@ export const get_data_store_obj_w_code = async function get_data_store_obj_w_cod
params = {}, params = {},
timeout = 25000, timeout = 25000,
log_lvl = 0 log_lvl = 0
}: { }: {
api_cfg: any; api_cfg: any;
data_store_code: string; data_store_code: string;
data_type?: string; data_type?: string;
@@ -489,7 +493,7 @@ export const get_data_store_obj_w_code = async function get_data_store_obj_w_cod
params?: key_val; params?: key_val;
timeout?: number; timeout?: number;
log_lvl?: number; log_lvl?: number;
}) { }) {
if (log_lvl) { if (log_lvl) {
console.log('*** get_data_store_obj_w_code() ***'); console.log('*** get_data_store_obj_w_code() ***');
} }
@@ -540,7 +544,7 @@ export const get_data_store_obj_w_code = async function get_data_store_obj_w_cod
console.log('Response Data:', data_store_obj); console.log('Response Data:', data_store_obj);
} }
return 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,54 +1,73 @@
<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 { let {
log_lvl = $bindable(0), log_lvl = $bindable(0),
hide = $bindable(false), hide = $bindable(false),
focus_input = $bindable(false), focus_input = $bindable(false),
expand = $bindable(false), expand = $bindable(false),
show_passcode_input = $bindable(false), show_passcode_input = $bindable(false),
trigger_clear_access = $bindable(null) trigger_clear_access = $bindable(null)
}: Props = $props(); }: Props = $props();
let entered_passcode: null | string = $state(null); let entered_passcode: null | string = $state(null);
let checked_passcode: null | string = $state(null); let checked_passcode: null | string = $state(null);
// let password_checked: boolean = $state(false); // let password_checked: boolean = $state(false);
// let entered_passcode: null|string = ''; // let entered_passcode: null|string = '';
// let show_passcode_input: boolean = $state(false); // let show_passcode_input: boolean = $state(false);
// let show_passcode_input: boolean = false; // let show_passcode_input: boolean = false;
// let trigger: null|string|boolean = null; // let trigger: null|string|boolean = null;
let trigger: null | string | boolean = $state(null); let trigger: null | string | boolean = $state(null);
// const dispatch = createEventDispatcher(); // 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. // 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(() => { onMount(() => {
// log_lvl = 2; // log_lvl = 2;
if (log_lvl > 1) { if (log_lvl > 1) {
console.log('** Element Mounted: ** Element Access Type'); console.log('** Element Mounted: ** Element Access Type');
@@ -59,9 +78,9 @@
// /** @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();
}); });
onDestroy(() => { onDestroy(() => {
if (log_lvl > 1) { if (log_lvl > 1) {
console.log('** Element Destroyed: ** Element Access Type'); console.log('** Element Destroyed: ** Element Access Type');
} }
@@ -72,15 +91,15 @@
// Reset trigger // Reset trigger
trigger = null; trigger = null;
}); });
// afterNavigate(() => { // afterNavigate(() => {
// /** @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();
// }); // });
$effect(() => { $effect(() => {
if ( if (
entered_passcode && entered_passcode &&
entered_passcode.length >= 5 && entered_passcode.length >= 5 &&
@@ -92,16 +111,18 @@
} }
handle_check_access_type_passcode(); handle_check_access_type_passcode();
} }
}); });
$effect(() => { $effect(() => {
if (trigger && $ae_loc.access_type) { if (trigger && $ae_loc.access_type) {
trigger = false; trigger = false;
if (log_lvl) { if (log_lvl) {
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 };
$ae_loc = $ae_loc; $ae_loc = $ae_loc;
@@ -118,10 +139,10 @@
$ae_loc = { ...$ae_loc, ...access_checks_results }; $ae_loc = { ...$ae_loc, ...access_checks_results };
$ae_loc = $ae_loc; $ae_loc = $ae_loc;
} }
}); });
// This does not seem to work. I feel like it should though...? // This does not seem to work. I feel like it should though...?
$effect(() => { $effect(() => {
if (!hide && focus_input) { if (!hide && focus_input) {
focus_input = false; focus_input = false;
// log_lvl = 2; // log_lvl = 2;
@@ -134,9 +155,9 @@
const to_focus = document.getElementById('access_passcode_input'); const to_focus = document.getElementById('access_passcode_input');
to_focus?.focus(); to_focus?.focus();
} }
}); });
$effect(() => { $effect(() => {
if (trigger_clear_access) { if (trigger_clear_access) {
trigger_clear_access = false; trigger_clear_access = false;
if (log_lvl) { if (log_lvl) {
@@ -144,9 +165,9 @@
} }
handle_clear_access(); handle_clear_access();
} }
}); });
function handle_check_access_type_passcode() { function handle_check_access_type_passcode() {
if (log_lvl > 1) { if (log_lvl > 1) {
console.log( console.log(
`*** handle_check_access_type_passcode() *** passcode list:`, `*** handle_check_access_type_passcode() *** passcode list:`,
@@ -202,7 +223,9 @@
window.localStorage.setItem('access_type', 'public'); window.localStorage.setItem('access_type', 'public');
$ae_loc.access_type = 'public'; $ae_loc.access_type = 'public';
} else if ($ae_loc.site_access_code_kv.authenticated == entered_passcode) { } else if (
$ae_loc.site_access_code_kv.authenticated == entered_passcode
) {
console.log('Authenticated passcode matched'); console.log('Authenticated passcode matched');
window.localStorage.setItem('access_type', 'authenticated'); window.localStorage.setItem('access_type', 'authenticated');
@@ -210,7 +233,9 @@
$ae_loc.access_type = 'authenticated'; $ae_loc.access_type = 'authenticated';
} else { } else {
if (log_lvl > 1) { if (log_lvl > 1) {
console.log('Entered passcode does not match any of the site access codes.'); console.log(
'Entered passcode does not match any of the site access codes.'
);
} }
if ($ae_loc.access_type != 'anonymous') { if ($ae_loc.access_type != 'anonymous') {
@@ -264,9 +289,9 @@
return null; return null;
} }
} }
function handle_clear_access() { function handle_clear_access() {
// console.log('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. // 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'); window.localStorage.setItem('access_type', 'anonymous');
@@ -289,7 +314,7 @@
$ae_loc.edit_mode = false; $ae_loc.edit_mode = false;
return true; 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,15 +1,32 @@
<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;
@@ -17,87 +34,87 @@
// 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,5 +1,5 @@
<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;
@@ -10,9 +10,9 @@
hide_icon?: boolean; hide_icon?: boolean;
hide_text?: boolean; hide_text?: boolean;
icon_name?: string; icon_name?: string;
} }
let { let {
children, children,
log_lvl = 0, log_lvl = 0,
value = $bindable(''), value = $bindable(''),
@@ -23,13 +23,13 @@
hide_icon = false, hide_icon = false,
hide_text = false, hide_text = false,
icon_name = 'copy' // copy, check, link icon_name = 'copy' // copy, check, link
}: Props = $props(); }: Props = $props();
// *** Import Svelte specific // *** Import Svelte specific
// import { browser } from '$app/environment'; // import { browser } from '$app/environment';
// *** Import other supporting libraries // *** Import other supporting libraries
import { import {
// ArrowBigRight, // ArrowBigRight,
// CircleX, // CircleX,
CircleCheck, CircleCheck,
@@ -43,31 +43,31 @@
// RefreshCw, RefreshCcw, RefreshCcwDot, // RefreshCw, RefreshCcw, RefreshCcwDot,
// ShieldEllipsis, ShieldMinus, ShieldPlus, ShieldUser, // ShieldEllipsis, ShieldMinus, ShieldPlus, ShieldUser,
// User, UserCheck // User, UserCheck
} from '@lucide/svelte'; } from '@lucide/svelte';
$effect(() => { $effect(() => {
if (log_lvl) { if (log_lvl) {
console.log(`Clipboard component initialized with value:`, value); console.log(`Clipboard component initialized with value:`, value);
} }
}); });
// Select your trigger element // Select your trigger element
// const elemButton: HTMLButtonElement | null = document.querySelector('[data-button]'); // const elemButton: HTMLButtonElement | null = document.querySelector('[data-button]');
// Add a click event handler to the trigger // Add a click event handler to the trigger
// elemButton?.addEventListener('click', () => { // elemButton?.addEventListener('click', () => {
// // Call the Clipboard API // // Call the Clipboard API
// navigator.clipboard // navigator.clipboard
// // Use the `writeText` method write content to the clipboard // // Use the `writeText` method write content to the clipboard
// .writeText(value) // .writeText(value)
// // Handle confirmation // // Handle confirmation
// .then(() => { // .then(() => {
// if (log_lvl) { // if (log_lvl) {
// console.log(`Clipboard write successful: ${value}`); // console.log(`Clipboard write successful: ${value}`);
// } // }
// success = true; // 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,5 +1,5 @@
<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;
@@ -15,9 +15,9 @@
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),
@@ -33,15 +33,22 @@
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,
ChevronRight,
LifeBuoy,
RefreshCw,
SquareX
} from '@lucide/svelte';
// *** Import Aether specific variables and functions
import {
ae_snip, ae_snip,
ae_loc, ae_loc,
ae_sess, ae_sess,
@@ -50,24 +57,24 @@
slct, slct,
slct_trigger, slct_trigger,
type key_val type key_val
} from '$lib/stores/ae_stores'; } from '$lib/stores/ae_stores';
import { api } from '$lib/api/api'; import { api } from '$lib/api/api';
if (log_lvl) { if (log_lvl) {
console.log(`Help - technical support component loaded`); console.log(`Help - technical support component loaded`);
} }
let help_tech_text: string = $state(''); let help_tech_text: string = $state('');
let hide_additional_info: boolean = $state(true); let hide_additional_info: boolean = $state(true);
function prevent_default<T extends Event>(fn: (event: T) => void) { function prevent_default<T extends Event>(fn: (event: T) => void) {
return function (event: T) { return function (event: T) {
event.preventDefault(); event.preventDefault();
fn(event); fn(event);
}; };
} }
function send_help_tech_email() { function send_help_tech_email() {
if (log_lvl) { if (log_lvl) {
console.log(`*** send_help_tech_email() ***`); console.log(`*** send_help_tech_email() ***`);
} }
@@ -105,8 +112,7 @@
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',
@@ -120,7 +126,7 @@
}); });
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}

View File

@@ -1,12 +1,12 @@
<script lang="ts"> <script lang="ts">
import { untrack } from 'svelte'; import { untrack } from 'svelte';
// *** Import Svelte specific // *** Import Svelte specific
import { browser } from '$app/environment'; import { browser } from '$app/environment';
import { goto, invalidateAll } from '$app/navigation'; import { goto, invalidateAll } from '$app/navigation';
import { Modal } from 'flowbite-svelte'; import { Modal } from 'flowbite-svelte';
// *** Import other supporting libraries // *** Import other supporting libraries
import { import {
CircleX, CircleX,
Eye, Eye,
EyeOff, EyeOff,
@@ -17,29 +17,47 @@
Mail, Mail,
MailCheck, MailCheck,
UserCheck UserCheck
} from '@lucide/svelte'; } from '@lucide/svelte';
// *** 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_loc, ae_sess, ae_api, slct, slct_trigger } from '$lib/stores/ae_stores'; import {
import { ae_util } from '$lib/ae_utils/ae_utils'; ae_loc,
import { core_func } from '$lib/ae_core/ae_core_functions'; ae_sess,
ae_api,
slct,
slct_trigger
} from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
import { core_func } from '$lib/ae_core/ae_core_functions';
// *** Setup Svelte properties // *** Setup Svelte properties
interface Props { interface Props {
log_lvl?: number; log_lvl?: number;
data?: any; data?: any;
hidden: null | boolean; hidden: null | boolean;
} }
let { log_lvl = $bindable(0), data = null, hidden = $bindable(true) }: Props = $props(); let {
log_lvl = $bindable(0),
data = null,
hidden = $bindable(true)
}: Props = $props();
let url_user_id = $state(untrack(() => data?.url?.searchParams?.get('user_id'))); let url_user_id = $state(
let url_user_key = $state(untrack(() => data?.url?.searchParams?.get('user_key'))); // Reminder that "key" is the site's auth key. untrack(() => data?.url?.searchParams?.get('user_id'))
let url_user_username = $state(untrack(() => data?.url?.searchParams?.get('username'))); );
let url_user_email = $state(untrack(() => data?.url?.searchParams?.get('user_email'))); let url_user_key = $state(
untrack(() => data?.url?.searchParams?.get('user_key'))
); // Reminder that "key" is the site's auth key.
let url_user_username = $state(
untrack(() => data?.url?.searchParams?.get('username'))
);
let url_user_email = $state(
untrack(() => data?.url?.searchParams?.get('user_email'))
);
$effect(() => { $effect(() => {
// NOTE: Sync URL params to state. // NOTE: Sync URL params to state.
// We use untrack to prevent infinite loops if navigation triggers within this effect. // We use untrack to prevent infinite loops if navigation triggers within this effect.
// WARNING: Ensure this doesn't clobber user-entered data during background URL updates. // WARNING: Ensure this doesn't clobber user-entered data during background URL updates.
@@ -49,38 +67,38 @@
url_user_username = data?.url?.searchParams?.get('username'); url_user_username = data?.url?.searchParams?.get('username');
url_user_email = data?.url?.searchParams?.get('user_email'); url_user_email = data?.url?.searchParams?.get('user_email');
}); });
}); });
let ae_promises: key_val = {}; let ae_promises: key_val = {};
let trigger: null | boolean = $state(null); // Use $state to ensure reactivity let trigger: null | boolean = $state(null); // Use $state to ensure reactivity
let user_id: string | null = $state(null); // Use $state to ensure reactivity let user_id: string | null = $state(null); // Use $state to ensure reactivity
let user_obj: key_val = $state({}); // Use $state to ensure reactivity let user_obj: key_val = $state({}); // Use $state to ensure reactivity
let person_id: string | null = $state(null); // Use $state to ensure reactivity let person_id: string | null = $state(null); // Use $state to ensure reactivity
let person_obj: key_val = $state({}); // Use $state to ensure reactivity let person_obj: key_val = $state({}); // Use $state to ensure reactivity
$effect(() => { $effect(() => {
if (user_id && person_id && trigger) { if (user_id && person_id && trigger) {
// alert(`Ready to sign in person! \n\nuser_id: ${user_id}\nperson_id: ${person_id}\n\nThis is a test alert to confirm the values are set.`); // alert(`Ready to sign in person! \n\nuser_id: ${user_id}\nperson_id: ${person_id}\n\nThis is a test alert to confirm the values are set.`);
trigger = false; trigger = false;
sign_in(); sign_in();
} }
}); });
let user_email = $state('scott.idem@gmail.com'); // Used for quick lookup of user by email address let user_email = $state('scott.idem@gmail.com'); // Used for quick lookup of user by email address
let new_password = $state('test12345'); let new_password = $state('test12345');
let confirm_password = $state('test12345'); let confirm_password = $state('test12345');
let is_changing_password = $state(false); let is_changing_password = $state(false);
let show_password_text = $state('password'); // password or text let show_password_text = $state('password'); // password or text
// Email sign-in status: null | 'sending' | 'sent' | 'not_found' | 'no_email' | 'error' // Email sign-in status: null | 'sending' | 'sent' | 'not_found' | 'no_email' | 'error'
let email_send_status: null | string = $state(null); let email_send_status: null | string = $state(null);
// Username/password sign-in status: null | 'signing_in' | 'error' | 'no_credentials' // Username/password sign-in status: null | 'signing_in' | 'error' | 'no_credentials'
let user_signin_status: null | string = $state(null); let user_signin_status: null | string = $state(null);
let user_signin_error: null | string = $state(null); let user_signin_error: null | string = $state(null);
function sign_in() { function sign_in() {
$ae_loc.jwt = user_obj.jwt; // Store the JSON Web Token $ae_loc.jwt = user_obj.jwt; // Store the JSON Web Token
$ae_loc.person_id = person_id; // Set the person_id in the ae_loc store $ae_loc.person_id = person_id; // Set the person_id in the ae_loc store
$ae_loc.person = person_obj; // Store the full person object for reference $ae_loc.person = person_obj; // Store the full person object for reference
@@ -103,7 +121,9 @@
} }
$ae_loc.user_access_type = $ae_loc.access_type; // Used to revert back to the user's access type after quick access (temporarily escalate permissions) is turned off. $ae_loc.user_access_type = $ae_loc.access_type; // Used to revert back to the user's access type after quick access (temporarily escalate permissions) is turned off.
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
);
// WARNING: I think this is causing a loop in Svelte or something. // WARNING: I think this is causing a loop in Svelte or something.
// Last ten effects were: // Last ten effects were:
// Uncaught Svelte error: effect_update_depth_exceeded // Uncaught Svelte error: effect_update_depth_exceeded
@@ -126,9 +146,9 @@
// We need to set browser history and force all load functions to rerun. // We need to set browser history and force all load functions to rerun.
goto(new_url, { replaceState: true, invalidateAll: true }); goto(new_url, { replaceState: true, invalidateAll: true });
} }
function sign_out() { function sign_out() {
// Clear the session information // Clear the session information
$ae_loc.jwt = null; $ae_loc.jwt = null;
$ae_loc.person_id = null; $ae_loc.person_id = null;
@@ -180,9 +200,9 @@
window.location.reload(); window.location.reload();
console.log('Signed out successfully.'); console.log('Signed out successfully.');
} }
$effect(() => { $effect(() => {
if (browser) { if (browser) {
untrack(() => { untrack(() => {
if (url_user_id) { if (url_user_id) {
@@ -203,27 +223,28 @@
} }
}); });
} }
}); });
// Use core_func.send_email_auth_ae_obj__user_id // Use core_func.send_email_auth_ae_obj__user_id
function handle_send_auth_email({ user_id }: { user_id: string }) { function handle_send_auth_email({ user_id }: { user_id: string }) {
console.log('handle_send_auth_email()'); console.log('handle_send_auth_email()');
console.log($ae_loc.base_url); // URL origin console.log($ae_loc.base_url); // URL origin
console.log($ae_loc.hostname); // URL hostname console.log($ae_loc.hostname); // URL hostname
// This function creates a new auth_key and then sends an email to the user with the new auth key. // This function creates a new auth_key and then sends an email to the user with the new auth key.
ae_promises.send_email_auth_ae_obj__user_id = core_func.send_email_auth_ae_obj__user_id({ ae_promises.send_email_auth_ae_obj__user_id =
core_func.send_email_auth_ae_obj__user_id({
api_cfg: $ae_api, api_cfg: $ae_api,
account_id: $slct.account_id, account_id: $slct.account_id,
user_id: user_id, user_id: user_id,
base_url: $ae_loc.base_url, base_url: $ae_loc.base_url,
log_lvl: 0 log_lvl: 0
}); });
} }
// Use core_func.qry_ae_obj_li__user_email // Use core_func.qry_ae_obj_li__user_email
function handle_lookup_user_email({ email }: { email: string }) { function handle_lookup_user_email({ email }: { email: string }) {
console.log('handle_lookup_user_email()'); console.log('handle_lookup_user_email()');
// If a user is returned then send the new auth key email // If a user is returned then send the new auth key email
@@ -238,11 +259,15 @@
.then((user_response) => { .then((user_response) => {
if (user_response?.user_id_random) { if (user_response?.user_id_random) {
console.log(`User found for email:`, user_response); console.log(`User found for email:`, user_response);
handle_send_auth_email({ user_id: user_response.user_id_random }); handle_send_auth_email({
user_id: user_response.user_id_random
});
email_send_status = 'sent'; email_send_status = 'sent';
} else if (user_response && user_response.length > 0) { } else if (user_response && user_response.length > 0) {
console.log(`Multiple users found for email:`, user_response); console.log(`Multiple users found for email:`, user_response);
handle_send_auth_email({ user_id: user_response[0].user_id_random }); handle_send_auth_email({
user_id: user_response[0].user_id_random
});
email_send_status = 'sent'; email_send_status = 'sent';
} else { } else {
console.warn('No user found for email:', email); console.warn('No user found for email:', email);
@@ -252,9 +277,9 @@
.catch(() => { .catch(() => {
email_send_status = 'error'; email_send_status = 'error';
}); });
} }
async function handle_change_password() { async function handle_change_password() {
if (!new_password) { if (!new_password) {
alert('Password cannot be empty.'); alert('Password cannot be empty.');
return; return;
@@ -292,7 +317,8 @@
// } // }
// }); // });
ae_promises.load__user_obj_li = await core_func.qry_ae_obj_li__user_email({ ae_promises.load__user_obj_li =
await core_func.qry_ae_obj_li__user_email({
api_cfg: $ae_api, api_cfg: $ae_api,
account_id: $slct.account_id, account_id: $slct.account_id,
null_account_id: false, null_account_id: false,
@@ -308,7 +334,10 @@
console.log(`User found for email:`, ae_promises.load__user_obj_li); console.log(`User found for email:`, ae_promises.load__user_obj_li);
use_user_id = ae_promises.load__user_obj_li.user_id_random; use_user_id = ae_promises.load__user_obj_li.user_id_random;
} else if (ae_promises.load__user_obj_li.length > 0) { } else if (ae_promises.load__user_obj_li.length > 0) {
console.log(`Multiple users found for email:`, ae_promises.load__user_obj_li); console.log(
`Multiple users found for email:`,
ae_promises.load__user_obj_li
);
use_user_id = ae_promises.load__user_obj_li[0].user_id_random; use_user_id = ae_promises.load__user_obj_li[0].user_id_random;
} }
} else { } else {
@@ -323,7 +352,8 @@
} }
try { try {
ae_promises.change_password = await core_func.auth_ae_obj__user_id_change_password({ ae_promises.change_password =
await core_func.auth_ae_obj__user_id_change_password({
api_cfg: $ae_api, api_cfg: $ae_api,
account_id: $ae_loc.account_id, account_id: $ae_loc.account_id,
user_id: use_user_id, user_id: use_user_id,
@@ -353,7 +383,7 @@
console.error('Failed to change password.'); console.error('Failed to change password.');
alert('Failed to change password. Check password length.'); alert('Failed to change password. Check password length.');
} }
} }
</script> </script>
<!-- It is important to keep in mind that a Person can only fully sign in if they have a linked User record. --> <!-- It is important to keep in mind that a Person can only fully sign in if they have a linked User record. -->
@@ -364,26 +394,25 @@
ae_sign_in_out ae_sign_in_out
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 class:hidden>
> <span class="flex w-full flex-col gap-2">
<span class="flex flex-col gap-2 w-full">
{#if !$ae_loc?.person_id && !$ae_loc?.user_id} {#if !$ae_loc?.person_id && !$ae_loc?.user_id}
<!-- Form for user look up based on email address --> <!-- Form for user look up based on email address -->
<form <form
class="ae_sign_in_form flex flex-col gap-2 w-full" class="ae_sign_in_form flex w-full flex-col gap-2"
onsubmit={async (e) => { onsubmit={async (e) => {
e.preventDefault(); e.preventDefault();
if ($ae_sess.auth__entered_email) { if ($ae_sess.auth__entered_email) {
@@ -394,27 +423,36 @@
} else { } else {
email_send_status = 'no_email'; email_send_status = 'no_email';
} }
}} }}>
> <div
<div class="text-xs text-gray-400 dark:text-gray-500 font-medium">Email magic link</div> class="text-xs font-medium text-gray-400 dark:text-gray-500">
Email magic link
</div>
<input <input
type="text" type="text"
class="input w-full text-sm" class="input w-full text-sm"
placeholder="Email Address" placeholder="Email Address"
value={$ae_sess.auth__entered_email ?? ''} value={$ae_sess.auth__entered_email ?? ''}
oninput={(e) => ($ae_sess.auth__entered_email = (e.target as HTMLInputElement).value)} oninput={(e) =>
/> ($ae_sess.auth__entered_email = (
e.target as HTMLInputElement
).value)} />
<button <button
type="submit" type="submit"
class="btn btn-sm w-full justify-center transition-all class="btn btn-sm w-full justify-center transition-all
{email_send_status === 'sent' ? 'preset-filled-success-500' {email_send_status === 'sent'
: email_send_status === 'sending' ? 'preset-tonal-surface opacity-70 cursor-wait' ? 'preset-filled-success-500'
: email_send_status === 'not_found' || email_send_status === 'error' ? 'preset-tonal-error border border-error-500' : email_send_status === 'sending'
: 'preset-tonal-secondary border border-secondary-500 hover:preset-filled-secondary-500'}" ? 'preset-tonal-surface cursor-wait opacity-70'
: email_send_status === 'not_found' ||
email_send_status === 'error'
? 'preset-tonal-error border-error-500 border'
: 'preset-tonal-secondary border-secondary-500 hover:preset-filled-secondary-500 border'}"
disabled={email_send_status === 'sending'} disabled={email_send_status === 'sending'}
title="Look up user by email and send sign in email" title="Look up user by email and send sign in email"
onclick={() => { email_send_status = null; }} onclick={() => {
> email_send_status = null;
}}>
<MailCheck size="1em" class="shrink-0" /> <MailCheck size="1em" class="shrink-0" />
<span class="ml-1"> <span class="ml-1">
{#if email_send_status === 'sending'} {#if email_send_status === 'sending'}
@@ -435,15 +473,20 @@
</form> </form>
<!-- Divider between the two sign-in methods --> <!-- Divider between the two sign-in methods -->
<div class="flex items-center gap-2 text-xs text-gray-400 dark:text-gray-600"> <div
<div class="flex-1 border-t border-gray-200/60 dark:border-gray-700/60"></div> class="flex items-center gap-2 text-xs text-gray-400 dark:text-gray-600">
<div
class="flex-1 border-t border-gray-200/60 dark:border-gray-700/60">
</div>
<span>or</span> <span>or</span>
<div class="flex-1 border-t border-gray-200/60 dark:border-gray-700/60"></div> <div
class="flex-1 border-t border-gray-200/60 dark:border-gray-700/60">
</div>
</div> </div>
<!-- We need to get the person's linked User ID and auth_key or User username and password to sign in. --> <!-- We need to get the person's linked User ID and auth_key or User username and password to sign in. -->
<form <form
class="ae_sign_in_form flex flex-col gap-2 w-full" class="ae_sign_in_form flex w-full flex-col gap-2"
onsubmit={async (e) => { onsubmit={async (e) => {
e.preventDefault(); e.preventDefault();
@@ -455,7 +498,10 @@
user_signin_error = msg; user_signin_error = msg;
}; };
if ($ae_sess.auth__entered_user_id && $ae_sess.auth__entered_user_key) { if (
$ae_sess.auth__entered_user_id &&
$ae_sess.auth__entered_user_key
) {
user_signin_status = 'signing_in'; user_signin_status = 'signing_in';
user_signin_error = null; user_signin_error = null;
@@ -470,21 +516,31 @@
}) })
.then((user_response) => { .then((user_response) => {
if (user_response?.user_id) { if (user_response?.user_id) {
console.log(`Successfully authenticated with User ID and Auth Key: ${user_response.username}`, user_response); console.log(
`Successfully authenticated with User ID and Auth Key: ${user_response.username}`,
user_response
);
user_obj = user_response; user_obj = user_response;
user_id = user_obj.user_id; user_id = user_obj.user_id;
} else if (!user_response) { } else if (!user_response) {
set_error('No server response. Check your connection and retry.'); set_error(
'No server response. Check your connection and retry.'
);
} else { } else {
// API returns 'detail' for validation errors (FastAPI standard), 'error' for app-level errors // API returns 'detail' for validation errors (FastAPI standard), 'error' for app-level errors
const reason = user_response?.detail || user_response?.error || 'Invalid User ID or Auth Key.'; const reason =
user_response?.detail ||
user_response?.error ||
'Invalid User ID or Auth Key.';
set_error(reason); set_error(reason);
} }
}) })
.then(() => { .then(() => {
if (!user_id) { if (!user_id) {
// Auth failed above — status already set // Auth failed above — status already set
console.error('Auth (user_id+key): user_id not set after authentication attempt.'); console.error(
'Auth (user_id+key): user_id not set after authentication attempt.'
);
return; return;
} }
// Look up the person record linked to this user. // Look up the person record linked to this user.
@@ -503,23 +559,38 @@
.then((person_response) => { .then((person_response) => {
// V3 CRUD returns 'id' as the random identifier; 'person_id_random' is a legacy field name. // V3 CRUD returns 'id' as the random identifier; 'person_id_random' is a legacy field name.
const person_rec = person_response?.[0]; const person_rec = person_response?.[0];
if (person_rec && (person_rec.id || person_rec.person_id_random)) { if (
console.log(`Successfully loaded person (${user_id}):`, person_rec); person_rec &&
(person_rec.id ||
person_rec.person_id_random)
) {
console.log(
`Successfully loaded person (${user_id}):`,
person_rec
);
person_obj = person_rec; person_obj = person_rec;
person_id = person_rec.id ?? person_rec.person_id_random; person_id =
person_rec.id ??
person_rec.person_id_random;
trigger = true; // triggers sign_in() via $effect → page redirect trigger = true; // triggers sign_in() via $effect → page redirect
} else { } else {
set_error('No person record linked to this account. Contact your administrator.'); set_error(
'No person record linked to this account. Contact your administrator.'
);
} }
}) })
.then(() => { .then(() => {
if (!user_id || !person_id) { if (!user_id || !person_id) {
console.error('Auth (user_id+key): finished but missing user_id or person_id — sign_in() will not be called.'); console.error(
'Auth (user_id+key): finished but missing user_id or person_id — sign_in() will not be called.'
);
} }
}); });
}); });
} else if (
} else if ($ae_sess.auth__entered_username && $ae_sess.auth__entered_password) { $ae_sess.auth__entered_username &&
$ae_sess.auth__entered_password
) {
user_signin_status = 'signing_in'; user_signin_status = 'signing_in';
user_signin_error = null; user_signin_error = null;
@@ -534,21 +605,31 @@
}) })
.then((user_response) => { .then((user_response) => {
if (user_response?.user_id) { if (user_response?.user_id) {
console.log(`Successfully authenticated with Username (${user_response.username}) and Password:`, user_response); console.log(
`Successfully authenticated with Username (${user_response.username}) and Password:`,
user_response
);
user_obj = user_response; user_obj = user_response;
user_id = user_obj.user_id; user_id = user_obj.user_id;
} else if (!user_response) { } else if (!user_response) {
set_error('No server response. Check your connection and retry.'); set_error(
'No server response. Check your connection and retry.'
);
} else { } else {
// API returns 'detail' for validation errors (FastAPI standard), 'error' for app-level errors // API returns 'detail' for validation errors (FastAPI standard), 'error' for app-level errors
const reason = user_response?.detail || user_response?.error || 'Invalid username or password.'; const reason =
user_response?.detail ||
user_response?.error ||
'Invalid username or password.';
set_error(reason); set_error(reason);
} }
}) })
.then(() => { .then(() => {
if (!user_id) { if (!user_id) {
// Auth failed above — status already set // Auth failed above — status already set
console.error('Auth (username+password): user_id not set after authentication attempt.'); console.error(
'Auth (username+password): user_id not set after authentication attempt.'
);
return; return;
} }
// Look up the person record linked to this user. // Look up the person record linked to this user.
@@ -567,29 +648,41 @@
.then((person_response) => { .then((person_response) => {
// V3 CRUD returns 'id' as the random identifier; 'person_id_random' is a legacy field name. // V3 CRUD returns 'id' as the random identifier; 'person_id_random' is a legacy field name.
const person_rec = person_response?.[0]; const person_rec = person_response?.[0];
if (person_rec && (person_rec.id || person_rec.person_id_random)) { if (
console.log(`Successfully loaded person (${user_id}):`, person_rec); person_rec &&
(person_rec.id ||
person_rec.person_id_random)
) {
console.log(
`Successfully loaded person (${user_id}):`,
person_rec
);
person_obj = person_rec; person_obj = person_rec;
person_id = person_rec.id ?? person_rec.person_id_random; person_id =
person_rec.id ??
person_rec.person_id_random;
trigger = true; // triggers sign_in() via $effect → page redirect trigger = true; // triggers sign_in() via $effect → page redirect
} else { } else {
set_error('No person record linked to this account. Contact your administrator.'); set_error(
'No person record linked to this account. Contact your administrator.'
);
} }
}) })
.then(() => { .then(() => {
if (!user_id || !person_id) { if (!user_id || !person_id) {
console.error('Auth (username+password): finished but missing user_id or person_id — sign_in() will not be called.'); console.error(
'Auth (username+password): finished but missing user_id or person_id — sign_in() will not be called.'
);
} }
}); });
}); });
} else { } else {
user_signin_status = 'no_credentials'; user_signin_status = 'no_credentials';
return false; return false;
} }
}} }}>
> <div
<div class="text-xs text-gray-400 dark:text-gray-500 font-medium"> class="text-xs font-medium text-gray-400 dark:text-gray-500">
{#if $ae_sess.auth__entered_user_id} {#if $ae_sess.auth__entered_user_id}
User ID &amp; Auth Key User ID &amp; Auth Key
{:else} {:else}
@@ -602,43 +695,58 @@
class="input w-full text-sm" class="input w-full text-sm"
placeholder="User ID" placeholder="User ID"
value={$ae_sess.auth__entered_user_id ?? ''} value={$ae_sess.auth__entered_user_id ?? ''}
oninput={(e) => ($ae_sess.auth__entered_user_id = (e.target as HTMLInputElement).value)} oninput={(e) =>
/> ($ae_sess.auth__entered_user_id = (
e.target as HTMLInputElement
).value)} />
<input <input
type="text" type="text"
class="input w-full text-sm" class="input w-full text-sm"
placeholder="Auth Key" placeholder="Auth Key"
value={$ae_sess.auth__entered_user_key ?? ''} value={$ae_sess.auth__entered_user_key ?? ''}
oninput={(e) => ($ae_sess.auth__entered_user_key = (e.target as HTMLInputElement).value)} oninput={(e) =>
/> ($ae_sess.auth__entered_user_key = (
e.target as HTMLInputElement
).value)} />
{:else} {:else}
<input <input
type="text" type="text"
class="input w-full text-sm" class="input w-full text-sm"
placeholder="Username" placeholder="Username"
value={$ae_sess.auth__entered_username ?? ''} value={$ae_sess.auth__entered_username ?? ''}
oninput={(e) => ($ae_sess.auth__entered_username = (e.target as HTMLInputElement).value)} oninput={(e) =>
/> ($ae_sess.auth__entered_username = (
e.target as HTMLInputElement
).value)} />
<input <input
type="password" type="password"
class="input w-full text-sm" class="input w-full text-sm"
placeholder="Password" placeholder="Password"
value={$ae_sess.auth__entered_password ?? ''} value={$ae_sess.auth__entered_password ?? ''}
oninput={(e) => ($ae_sess.auth__entered_password = (e.target as HTMLInputElement).value)} oninput={(e) =>
/> ($ae_sess.auth__entered_password = (
e.target as HTMLInputElement
).value)} />
{/if} {/if}
<button <button
type="submit" type="submit"
class="btn btn-sm w-full justify-center transition-all class="btn btn-sm w-full justify-center transition-all
{user_signin_status === 'error' ? 'preset-tonal-error border border-error-500' {user_signin_status === 'error'
: user_signin_status === 'signing_in' ? 'preset-tonal-surface opacity-70 cursor-wait' ? 'preset-tonal-error border-error-500 border'
: user_signin_status === 'no_credentials' ? 'preset-tonal-warning border border-warning-500' : user_signin_status === 'signing_in'
: 'preset-tonal-secondary border border-secondary-500 hover:preset-filled-secondary-500'}" ? 'preset-tonal-surface cursor-wait opacity-70'
: user_signin_status === 'no_credentials'
? 'preset-tonal-warning border-warning-500 border'
: 'preset-tonal-secondary border-secondary-500 hover:preset-filled-secondary-500 border'}"
disabled={user_signin_status === 'signing_in'} disabled={user_signin_status === 'signing_in'}
title="Sign in with username and password" title="Sign in with username and password"
onclick={() => { if (user_signin_status !== 'signing_in') { user_signin_status = null; user_signin_error = null; } }} onclick={() => {
> if (user_signin_status !== 'signing_in') {
user_signin_status = null;
user_signin_error = null;
}
}}>
<UserCheck size="1em" class="shrink-0" /> <UserCheck size="1em" class="shrink-0" />
<span class="ml-1"> <span class="ml-1">
{#if user_signin_status === 'signing_in'} {#if user_signin_status === 'signing_in'}
@@ -648,20 +756,24 @@
{:else if user_signin_status === 'no_credentials'} {:else if user_signin_status === 'no_credentials'}
Enter credentials first Enter credentials first
{:else} {:else}
{$ae_sess.auth__entered_user_id ? 'User ID' : 'Username'} Sign In {$ae_sess.auth__entered_user_id
? 'User ID'
: 'Username'} Sign In
{/if} {/if}
</span> </span>
</button> </button>
{#if user_signin_error} {#if user_signin_error}
<p class="text-xs text-error-600 dark:text-error-400 text-center leading-snug"> <p
class="text-error-600 dark:text-error-400 text-center text-xs leading-snug">
{user_signin_error} {user_signin_error}
</p> </p>
{/if} {/if}
</form> </form>
{:else} {:else}
<!-- Signed in: show username, optional change password, sign out --> <!-- Signed in: show username, optional change password, sign out -->
<div class="flex flex-col gap-2 items-stretch w-full"> <div class="flex w-full flex-col items-stretch gap-2">
<div class="text-sm text-center text-gray-500 dark:text-gray-400 py-1 font-medium"> <div
class="py-1 text-center text-sm font-medium text-gray-500 dark:text-gray-400">
{$ae_loc?.user?.username ?? '-- not set --'} {$ae_loc?.user?.username ?? '-- not set --'}
</div> </div>
@@ -669,12 +781,11 @@
{#if $ae_loc.edit_mode} {#if $ae_loc.edit_mode}
<button <button
type="button" type="button"
class="btn btn-sm preset-tonal-warning border border-warning-500 hover:preset-filled-warning-500 w-full justify-center" class="btn btn-sm preset-tonal-warning border-warning-500 hover:preset-filled-warning-500 w-full justify-center border"
title="Change Password" title="Change Password"
onclick={() => { onclick={() => {
$ae_sess.show__modal_change_password = true; $ae_sess.show__modal_change_password = true;
}} }}>
>
<LockKeyhole size="1em" class="shrink-0" /> <LockKeyhole size="1em" class="shrink-0" />
<span class="ml-1">Change Password</span> <span class="ml-1">Change Password</span>
</button> </button>
@@ -683,21 +794,19 @@
<!-- Sign out --> <!-- Sign out -->
<button <button
type="button" type="button"
class="btn btn-sm preset-tonal-warning border border-warning-500 hover:preset-filled-warning-500 w-full justify-center" class="btn btn-sm preset-tonal-warning border-warning-500 hover:preset-filled-warning-500 w-full justify-center border"
title="Sign Out" title="Sign Out"
onclick={async () => { onclick={async () => {
if (confirm('Are you sure you want to sign out?')) { if (confirm('Are you sure you want to sign out?')) {
sign_out(); sign_out();
} }
}} }}>
>
<LogOut size="1em" class="shrink-0" /> <LogOut size="1em" class="shrink-0" />
<span class="ml-1">Sign Out</span> <span class="ml-1">Sign Out</span>
</button> </button>
</div> </div>
{/if} {/if}
</span> </span>
</section> </section>
<!-- Change Password Modal --> <!-- Change Password Modal -->
@@ -710,9 +819,8 @@
autoclose={false} autoclose={false}
placement="top-center" placement="top-center"
size="md" size="md"
class="top-center bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200 rounded-lg border-gray-200 dark:border-gray-700 divide-gray-200 dark:divide-gray-700 shadow-md relative mx-auto w-full divide-y" class="top-center relative mx-auto w-full divide-y divide-gray-200 rounded-lg border-gray-200 bg-white text-gray-800 shadow-md dark:divide-gray-700 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200">
> <div class="modal-box flex flex-col items-center justify-center gap-2">
<div class="modal-box flex flex-col gap-2 items-center justify-center">
<!-- If the user is a global Manager or Super then they can change the password for any user. Otherwise, they can only change their own password. Show email address field for a quick lookup to get the user.id. --> <!-- If the user is a global Manager or Super then they can change the password for any user. Otherwise, they can only change their own password. Show email address field for a quick lookup to get the user.id. -->
<div class="flex flex-col flex-wrap gap-2"> <div class="flex flex-col flex-wrap gap-2">
<span class="text-sm text-gray-500"> <span class="text-sm text-gray-500">
@@ -733,13 +841,13 @@
autocomplete="off" autocomplete="off"
placeholder="User's email address" placeholder="User's email address"
bind:value={user_email} bind:value={user_email}
class="input input-bordered w-48 max-w-full" class="input input-bordered w-48 max-w-full" />
/>
</div> </div>
{/if} {/if}
<!-- Allow for new password and confirm password fields. --> <!-- Allow for new password and confirm password fields. -->
<div class="flex flex-row flex-wrap gap-2 items-center justify-center"> <div
class="flex flex-row flex-wrap items-center justify-center gap-2">
<span class="flex flex-row flex-wrap gap-1"> <span class="flex flex-row flex-wrap gap-1">
<input <input
required required
@@ -748,8 +856,7 @@
name="new_password" name="new_password"
placeholder="New Password" placeholder="New Password"
bind:value={new_password} bind:value={new_password}
class="input input-bordered w-48 max-w-full required:border-red-500" class="input input-bordered w-48 max-w-full required:border-red-500" />
/>
<input <input
required required
type={show_password_text} type={show_password_text}
@@ -757,8 +864,7 @@
name="confirm_password" name="confirm_password"
placeholder="Confirm Password" placeholder="Confirm Password"
bind:value={confirm_password} bind:value={confirm_password}
class="input input-bordered w-48 max-w-full required:border-red-500" class="input input-bordered w-48 max-w-full required:border-red-500" />
/>
</span> </span>
<!-- Show/Hide Password Text --> <!-- Show/Hide Password Text -->
@@ -776,8 +882,7 @@
show_password_text = 'password'; show_password_text = 'password';
} }
}} }}
title="Show/Hide Password Text" title="Show/Hide Password Text">
>
{#if show_password_text === 'password'} {#if show_password_text === 'password'}
<EyeOff class="mx-1" /> <EyeOff class="mx-1" />
{:else} {:else}
@@ -790,10 +895,11 @@
</button> </button>
<div> <div>
<span class="text-sm text-warning-800 dark:text-gray-200"> <span class="text-warning-800 text-sm dark:text-gray-200">
Password must be at <strong>least 10 characters</strong> long. It is recommend Password must be at <strong>least 10 characters</strong> long.
that you use a pass phrase (multiple words as your password) or a complex password It is recommend that you use a pass phrase (multiple words
(uppercase letter, lowercase letters, numbers, and special characters). as your password) or a complex password (uppercase letter,
lowercase letters, numbers, and special characters).
</span> </span>
</div> </div>
</div> </div>
@@ -803,16 +909,15 @@
type="button" type="button"
class="btn preset-filled-warning-500" class="btn preset-filled-warning-500"
onclick={handle_change_password} onclick={handle_change_password}
disabled={is_changing_password} disabled={is_changing_password}>
>
<Key class="mx-1" /> <Key class="mx-1" />
Change Password Change Password
</button> </button>
<button <button
type="button" type="button"
class="btn preset-tonal-secondary" class="btn preset-tonal-secondary"
onclick={() => ($ae_sess.show__modal_change_password = false)} onclick={() =>
> ($ae_sess.show__modal_change_password = false)}>
<CircleX class="mx-1" /> <CircleX class="mx-1" />
Cancel Cancel
</button> </button>

View File

@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
/** /**
* e_app_sys_bar.svelte * e_app_sys_bar.svelte
* Global system bar — replaces e_app_sys_menu.svelte. * Global system bar — replaces e_app_sys_menu.svelte.
* *
@@ -12,33 +12,51 @@
* (access_type, sign_in_out). Simple controls (theme, cfg utilities) * (access_type, sign_in_out). Simple controls (theme, cfg utilities)
* are inlined here for clean panel layout without blue-bg conflicts. * are inlined here for clean panel layout without blue-bg conflicts.
*/ */
import { Bug, CircleX, Eraser, LogOut, Maximize2, Menu, Minimize2, Moon, Palette, RefreshCw, ShieldEllipsis, ShieldMinus, ShieldUser, Sun, ToggleLeft, ToggleRight, User } from '@lucide/svelte'; import {
import { ae_loc, ae_sess, ae_api } from '$lib/stores/ae_stores'; Bug,
CircleX,
Eraser,
LogOut,
Maximize2,
Menu,
Minimize2,
Moon,
Palette,
RefreshCw,
ShieldEllipsis,
ShieldMinus,
ShieldUser,
Sun,
ToggleLeft,
ToggleRight,
User
} from '@lucide/svelte';
import { ae_loc, ae_sess, ae_api } 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_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 E_app_url_builder from '$lib/app_components/e_app_url_builder.svelte'; import E_app_url_builder from '$lib/app_components/e_app_url_builder.svelte';
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();
// Passed down to Element_access_type — triggers handle_clear_access() there. // Passed down to Element_access_type — triggers handle_clear_access() there.
// See e_app_access_type.svelte for the full clear logic (reverts to user_access_type). // See e_app_access_type.svelte for the full clear logic (reverts to user_access_type).
let trigger_clear_access: null | boolean = $state(null); let trigger_clear_access: null | boolean = $state(null);
// ── Accessibility: font size cycler ───────────────────────────────────── // ── Accessibility: font size cycler ─────────────────────────────────────
function cycle_font_size() { function cycle_font_size() {
const mode = $ae_loc.font_size_mode; const mode = $ae_loc.font_size_mode;
if (!mode || mode === 'default') { if (!mode || mode === 'default') {
$ae_loc.font_size_mode = 'larger'; $ae_loc.font_size_mode = 'larger';
@@ -47,21 +65,26 @@
} else { } else {
$ae_loc.font_size_mode = 'default'; $ae_loc.font_size_mode = 'default';
} }
} }
// ── Accessibility: dark / light toggle ────────────────────────────────── // ── Accessibility: dark / light toggle ──────────────────────────────────
function toggle_theme_mode() { function toggle_theme_mode() {
if ($ae_loc.theme_mode === 'light') { if ($ae_loc.theme_mode === 'light') {
$ae_loc.theme_mode = 'dark'; $ae_loc.theme_mode = 'dark';
} else { } else {
$ae_loc.theme_mode = 'light'; $ae_loc.theme_mode = 'light';
} }
// DOM sync (class) is handled reactively in +layout.svelte effect #3 // DOM sync (class) is handled reactively in +layout.svelte effect #3
} }
// ── Dev: clear all browser storage + key IndexedDB tables, then reload ── // ── Dev: clear all browser storage + key IndexedDB tables, then reload ──
function handle_clear_storage_db() { function handle_clear_storage_db() {
if (!confirm('Clear all local/session storage and IndexedDB? The page will reload.')) return; if (
!confirm(
'Clear all local/session storage and IndexedDB? The page will reload.'
)
)
return;
localStorage.clear(); localStorage.clear();
sessionStorage.clear(); sessionStorage.clear();
indexedDB.deleteDatabase('ae_archives_db'); indexedDB.deleteDatabase('ae_archives_db');
@@ -71,10 +94,10 @@
indexedDB.deleteDatabase('ae_posts_db'); indexedDB.deleteDatabase('ae_posts_db');
indexedDB.deleteDatabase('ae_sponsorships_db'); indexedDB.deleteDatabase('ae_sponsorships_db');
window.location.reload(); window.location.reload();
} }
// ── Menu expand / collapse ─────────────────────────────────────────────── // ── Menu expand / collapse ───────────────────────────────────────────────
function toggle_expand() { function toggle_expand() {
if (!expand) { if (!expand) {
expand = true; expand = true;
$ae_sess.sys_menu.expand = true; $ae_sess.sys_menu.expand = true;
@@ -93,13 +116,16 @@
expand = false; expand = false;
$ae_sess.sys_menu.expand = false; $ae_sess.sys_menu.expand = false;
} }
} }
// ── Auth shield: click behaviour depends on current auth state ─────────── // ── Auth shield: click behaviour depends on current auth state ───────────
// Mirrors the three-state shield logic from e_app_sys_menu.svelte. // Mirrors the three-state shield logic from e_app_sys_menu.svelte.
function handle_shield_click() { function handle_shield_click() {
if ($ae_loc.access_type && $ae_loc.access_type !== 'anonymous') { if ($ae_loc.access_type && $ae_loc.access_type !== 'anonymous') {
if ($ae_loc?.user_access_type && $ae_loc?.access_type === $ae_loc?.user_access_type) { if (
$ae_loc?.user_access_type &&
$ae_loc?.access_type === $ae_loc?.user_access_type
) {
// At user's own level — offer passcode upgrade // At user's own level — offer passcode upgrade
if (!expand) { if (!expand) {
expand = true; expand = true;
@@ -134,44 +160,50 @@
// Note: focus is handled reactively by the focus_input binding in Element_access_type. // Note: focus is handled reactively by the focus_input binding in Element_access_type.
// Direct getElementById here would fail — the panel DOM doesn't exist yet at this point. // Direct getElementById here would fail — the panel DOM doesn't exist yet at this point.
} }
} }
// ── Panel section collapse state ───────────────────────────────────────── // ── Panel section collapse state ─────────────────────────────────────────
// Open/closed per section — access open by default (most common action). // Open/closed per section — access open by default (most common action).
let sec_access = $state(true); let sec_access = $state(true);
let sec_signin = $state(false); let sec_signin = $state(false);
let sec_appearance = $state(false); let sec_appearance = $state(false);
let sec_dev = $state(false); let sec_dev = $state(false);
// ── Derived display helpers ────────────────────────────────────────────── // ── Derived display helpers ──────────────────────────────────────────────
let font_label = $derived.by(() => { let font_label = $derived.by(() => {
if ($ae_loc.font_size_mode === 'larger') return 'A+'; if ($ae_loc.font_size_mode === 'larger') return 'A+';
if ($ae_loc.font_size_mode === 'smaller') return 'A'; if ($ae_loc.font_size_mode === 'smaller') return 'A';
return 'A'; return 'A';
}); });
let font_title = $derived.by(() => { let font_title = $derived.by(() => {
if ($ae_loc.font_size_mode === 'larger') return 'Font: Larger — click for Smaller'; if ($ae_loc.font_size_mode === 'larger')
if ($ae_loc.font_size_mode === 'smaller') return 'Font: Smaller — click for Normal'; return 'Font: Larger — click for Smaller';
if ($ae_loc.font_size_mode === 'smaller')
return 'Font: Smaller — click for Normal';
return 'Font: Normal — click for Larger'; return 'Font: Normal — click for Larger';
}); });
let person_display = $derived( let person_display = $derived(
$ae_loc?.person?.informal_name ?? $ae_loc?.person?.given_name ?? null $ae_loc?.person?.informal_name ?? $ae_loc?.person?.given_name ?? null
); );
let access_label = $derived.by(() => { let access_label = $derived.by(() => {
const t = $ae_loc?.access_type; const t = $ae_loc?.access_type;
if (!t || t === 'anonymous') return null; if (!t || t === 'anonymous') return null;
const map: Record<string, string> = { const map: Record<string, string> = {
super: 'Super', manager: 'Manager', administrator: 'Admin', super: 'Super',
trusted: 'Trusted', public: 'Public', authenticated: 'Auth\'d' manager: 'Manager',
administrator: 'Admin',
trusted: 'Trusted',
public: 'Public',
authenticated: "Auth'd"
}; };
return map[t] ?? t; return map[t] ?? t;
}); });
// Theme options — keep in sync with e_app_url_builder.svelte // Theme options — keep in sync with e_app_url_builder.svelte
const theme_options = [ const theme_options = [
{ value: '', label: '-- None --' }, { value: '', label: '-- None --' },
{ value: 'cerberus', label: 'Cerberus' }, { value: 'cerberus', label: 'Cerberus' },
{ value: 'concord', label: 'Concord' }, { value: 'concord', label: 'Concord' },
@@ -189,8 +221,8 @@
{ value: 'AE_Firefly_Indigo', label: 'Firefly Indigo ✦' }, { value: 'AE_Firefly_Indigo', label: 'Firefly Indigo ✦' },
{ value: 'AE_Firefly_Rainbow', label: 'Firefly Rainbow ✨' }, { value: 'AE_Firefly_Rainbow', label: 'Firefly Rainbow ✨' },
{ value: 'AE_c_IDAA_light', label: 'IDAA light' }, { value: 'AE_c_IDAA_light', label: 'IDAA light' },
{ value: 'AE_c_LCI', label: 'LCI' }, { value: 'AE_c_LCI', label: 'LCI' }
]; ];
</script> </script>
<!-- ══════════════════════════════════════════════════════════════════════════ <!-- ══════════════════════════════════════════════════════════════════════════
@@ -200,37 +232,37 @@
<div <div
class=" class="
ae_sys_bar ae_sys_bar
print:hidden fixed
fixed bottom-12 right-2 right-2 bottom-12 z-50
z-50 flex
flex flex-col items-end gap-1 flex-col items-end gap-1 print:hidden
" "
class:hidden={hide} class:hidden={hide}>
>
<!-- ── EXPANDED PANEL ──────────────────────────────────────────────────── --> <!-- ── EXPANDED PANEL ──────────────────────────────────────────────────── -->
{#if expand} {#if expand}
<div <div
class=" class="
ae_sys_panel ae_sys_panel
flex flex-col items-stretch gap-0 flex max-h-[80vh] w-80 max-w-[94vw]
w-80 max-w-[94vw] flex-col items-stretch
max-h-[80vh] overflow-y-auto overflow-x-hidden gap-0 overflow-x-hidden overflow-y-auto
bg-white/85 dark:bg-gray-900/85 text-gray-900 dark:text-gray-100 backdrop-blur-md rounded-xl border border-gray-200/70 bg-white/85 text-sm
border border-gray-200/70 dark:border-gray-700/70 text-gray-900 shadow-2xl backdrop-blur-md
rounded-xl shadow-2xl dark:border-gray-700/70 dark:bg-gray-900/85
text-sm dark:text-gray-100
" ">
>
<!-- Panel header: person info + close --> <!-- Panel header: person info + close -->
<div class="flex items-center justify-between px-3 py-2 border-b border-gray-100/70 dark:border-gray-800/70 sticky top-0 bg-white/85 dark:bg-gray-900/85 backdrop-blur-md z-10"> <div
<div class="flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400 min-w-0"> class="sticky top-0 z-10 flex items-center justify-between border-b border-gray-100/70 bg-white/85 px-3 py-2 backdrop-blur-md dark:border-gray-800/70 dark:bg-gray-900/85">
<div
class="flex min-w-0 items-center gap-2 text-xs text-gray-500 dark:text-gray-400">
{#if $ae_loc?.person_id} {#if $ae_loc?.person_id}
<User size="0.9em" class="shrink-0" /> <User size="0.9em" class="shrink-0" />
<span class="truncate">{person_display ?? '—'}</span> <span class="truncate">{person_display ?? '—'}</span>
{/if} {/if}
{#if access_label} {#if access_label}
<span class="font-semibold text-primary-600 dark:text-primary-400 shrink-0"> <span
class="text-primary-600 dark:text-primary-400 shrink-0 font-semibold">
· {access_label} · {access_label}
</span> </span>
{/if} {/if}
@@ -240,10 +272,9 @@
</div> </div>
<button <button
type="button" type="button"
class="btn btn-icon btn-sm preset-tonal-surface hover:preset-filled-error transition-all shrink-0 ml-2" class="btn btn-icon btn-sm preset-tonal-surface hover:preset-filled-error ml-2 shrink-0 transition-all"
onclick={toggle_expand} onclick={toggle_expand}
title="Close menu" title="Close menu">
>
<CircleX size="1.1em" /> <CircleX size="1.1em" />
</button> </button>
</div> </div>
@@ -253,18 +284,24 @@
<div class="border-b border-gray-100 dark:border-gray-800"> <div class="border-b border-gray-100 dark:border-gray-800">
<button <button
type="button" type="button"
class="w-full flex items-center justify-between px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors" class="flex w-full items-center justify-between px-3 py-2 text-xs font-semibold tracking-wider text-gray-500 uppercase transition-colors hover:bg-gray-50 dark:text-gray-400 dark:hover:bg-gray-800"
onclick={() => sec_signin = !sec_signin} onclick={() => (sec_signin = !sec_signin)}>
> <span class="flex min-w-0 items-center gap-1.5">
<span class="flex items-center gap-1.5 min-w-0">
{#if $ae_loc?.person_id && $ae_loc?.user_id} {#if $ae_loc?.person_id && $ae_loc?.user_id}
<LogOut size="0.85em" class="opacity-60 shrink-0" /> <LogOut
size="0.85em"
class="shrink-0 opacity-60" />
<span>Sign Out</span> <span>Sign Out</span>
<span class="normal-case font-normal opacity-70 truncate"> <span
{$ae_loc?.user?.username ?? person_display ?? ''} class="truncate font-normal normal-case opacity-70">
{$ae_loc?.user?.username ??
person_display ??
''}
</span> </span>
{:else} {:else}
<ShieldUser size="0.85em" class="opacity-60 shrink-0" /> <ShieldUser
size="0.85em"
class="shrink-0 opacity-60" />
<span>User Sign In</span> <span>User Sign In</span>
{/if} {/if}
</span> </span>
@@ -274,8 +311,9 @@
<div class="px-2 pb-2"> <div class="px-2 pb-2">
<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} />
</div> </div>
{/if} {/if}
</div> </div>
@@ -286,22 +324,31 @@
<div class="border-b border-gray-100 dark:border-gray-800"> <div class="border-b border-gray-100 dark:border-gray-800">
<button <button
type="button" type="button"
class="w-full flex items-center justify-between px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors" class="flex w-full items-center justify-between px-3 py-2 text-xs font-semibold tracking-wider text-gray-500 uppercase transition-colors hover:bg-gray-50 dark:text-gray-400 dark:hover:bg-gray-800"
onclick={() => sec_access = !sec_access} onclick={() => (sec_access = !sec_access)}>
> <span class="flex min-w-0 items-center gap-1.5">
<span class="flex items-center gap-1.5 min-w-0">
{#if $ae_loc?.access_type && $ae_loc?.access_type !== 'anonymous'} {#if $ae_loc?.access_type && $ae_loc?.access_type !== 'anonymous'}
{#if $ae_loc?.user_access_type && $ae_loc?.access_type !== $ae_loc?.user_access_type} {#if $ae_loc?.user_access_type && $ae_loc?.access_type !== $ae_loc?.user_access_type}
<ShieldMinus size="0.85em" class="opacity-60 shrink-0" /> <ShieldMinus
size="0.85em"
class="shrink-0 opacity-60" />
<span>Elevated Access</span> <span>Elevated Access</span>
<span class="normal-case font-normal text-primary-600 dark:text-primary-400 truncate">({access_label})</span> <span
class="text-primary-600 dark:text-primary-400 truncate font-normal normal-case"
>({access_label})</span>
{:else} {:else}
<ShieldEllipsis size="0.85em" class="opacity-60 shrink-0" /> <ShieldEllipsis
size="0.85em"
class="shrink-0 opacity-60" />
<span>Access</span> <span>Access</span>
<span class="normal-case font-normal text-primary-600 dark:text-primary-400 truncate">({access_label})</span> <span
class="text-primary-600 dark:text-primary-400 truncate font-normal normal-case"
>({access_label})</span>
{/if} {/if}
{:else} {:else}
<ShieldUser size="0.85em" class="opacity-60 shrink-0" /> <ShieldUser
size="0.85em"
class="shrink-0 opacity-60" />
<span>Enter Passcode</span> <span>Enter Passcode</span>
{/if} {/if}
</span> </span>
@@ -311,11 +358,17 @@
<div class="px-2 pb-2"> <div class="px-2 pb-2">
<Element_access_type <Element_access_type
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={
bind:expand={$ae_loc.sys_menu.expand_access_type} $ae_sess.sys_menu.focus_passcode_input
bind:show_passcode_input={$ae_sess.app_cfg.show_element__passcode_input} }
bind:trigger_clear_access bind:expand={
/> $ae_loc.sys_menu.expand_access_type
}
bind:show_passcode_input={
$ae_sess.app_cfg
.show_element__passcode_input
}
bind:trigger_clear_access />
</div> </div>
{/if} {/if}
</div> </div>
@@ -325,21 +378,20 @@
<div class="border-b border-gray-100 dark:border-gray-800"> <div class="border-b border-gray-100 dark:border-gray-800">
<button <button
type="button" type="button"
class="w-full flex items-center justify-between px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors" class="flex w-full items-center justify-between px-3 py-2 text-xs font-semibold tracking-wider text-gray-500 uppercase transition-colors hover:bg-gray-50 dark:text-gray-400 dark:hover:bg-gray-800"
onclick={() => sec_appearance = !sec_appearance} onclick={() => (sec_appearance = !sec_appearance)}>
>
<span class="flex items-center gap-1"> <span class="flex items-center gap-1">
<Palette size="1em" class="opacity-60" /> <Palette size="1em" class="opacity-60" />
Appearance Appearance
<span class="normal-case font-normal opacity-60 ml-1"> <span class="ml-1 font-normal normal-case opacity-60">
({$ae_loc?.theme_name ?? '—'} · {$ae_loc?.theme_mode ?? '—'}) ({$ae_loc?.theme_name ?? '—'} · {$ae_loc?.theme_mode ??
'—'})
</span> </span>
</span> </span>
<span class="opacity-50">{sec_appearance ? '▲' : '▼'}</span> <span class="opacity-50">{sec_appearance ? '▲' : '▼'}</span>
</button> </button>
{#if sec_appearance} {#if sec_appearance}
<div class="px-3 pb-3 pt-1 space-y-3"> <div class="space-y-3 px-3 pt-1 pb-3">
<!-- Quick actions row: mode + font (most commonly used) --> <!-- Quick actions row: mode + font (most commonly used) -->
<div class="flex gap-2"> <div class="flex gap-2">
<button <button
@@ -349,41 +401,48 @@
? 'preset-tonal-secondary hover:preset-filled-secondary-500' ? 'preset-tonal-secondary hover:preset-filled-secondary-500'
: 'preset-tonal-warning hover:preset-filled-warning-500'}" : 'preset-tonal-warning hover:preset-filled-warning-500'}"
onclick={toggle_theme_mode} onclick={toggle_theme_mode}
title="Toggle light/dark mode (currently: {$ae_loc?.theme_mode ?? 'unknown'})" title="Toggle light/dark mode (currently: {$ae_loc?.theme_mode ??
> 'unknown'})">
{#if $ae_loc?.theme_mode === 'dark'} {#if $ae_loc?.theme_mode === 'dark'}
<Moon size="1em" class="shrink-0" /> <Moon size="1em" class="shrink-0" />
<span class="text-xs ml-1">Dark</span> <span class="ml-1 text-xs">Dark</span>
{:else} {:else}
<Sun size="1em" class="shrink-0" /> <Sun size="1em" class="shrink-0" />
<span class="text-xs ml-1">Light</span> <span class="ml-1 text-xs">Light</span>
{/if} {/if}
</button> </button>
<button <button
type="button" type="button"
class="btn btn-sm flex-1 preset-tonal-surface hover:preset-tonal-primary transition-all" class="btn btn-sm preset-tonal-surface hover:preset-tonal-primary flex-1 transition-all"
onclick={cycle_font_size} onclick={cycle_font_size}
title={font_title} title={font_title}>
> <span class="text-sm leading-none font-bold"
<span class="font-bold text-sm leading-none">{font_label}</span> >{font_label}</span>
<span class="text-xs ml-1 opacity-70">Font</span> <span class="ml-1 text-xs opacity-70"
>Font</span>
</button> </button>
</div> </div>
<!-- Theme name select --> <!-- Theme name select -->
<div class="space-y-1.5"> <div class="space-y-1.5">
<div class="text-xs text-gray-400 dark:text-gray-500 font-medium">Theme</div> <div
class="text-xs font-medium text-gray-400 dark:text-gray-500">
Theme
</div>
<select <select
bind:value={$ae_loc.theme_name} bind:value={$ae_loc.theme_name}
onchange={(e) => document.documentElement.setAttribute('data-theme', (e.target as HTMLSelectElement).value)} onchange={(e) =>
class="select w-full text-sm text-gray-900 dark:text-gray-100 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600" document.documentElement.setAttribute(
> 'data-theme',
(e.target as HTMLSelectElement).value
)}
class="select w-full border border-gray-300 bg-white text-sm text-gray-900 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100">
{#each theme_options as opt (opt.value)} {#each theme_options as opt (opt.value)}
<option value={opt.value}>{opt.label}</option> <option value={opt.value}
>{opt.label}</option>
{/each} {/each}
</select> </select>
</div> </div>
</div> </div>
{/if} {/if}
</div> </div>
@@ -398,9 +457,8 @@
<div class="border-b border-gray-100 dark:border-gray-800"> <div class="border-b border-gray-100 dark:border-gray-800">
<button <button
type="button" type="button"
class="w-full flex items-center justify-between px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors" class="flex w-full items-center justify-between px-3 py-2 text-xs font-semibold tracking-wider text-gray-500 uppercase transition-colors hover:bg-gray-50 dark:text-gray-400 dark:hover:bg-gray-800"
onclick={() => sec_dev = !sec_dev} onclick={() => (sec_dev = !sec_dev)}>
>
<span class="flex items-center gap-1"> <span class="flex items-center gap-1">
<Bug size="0.9em" class="opacity-60" /> <Bug size="0.9em" class="opacity-60" />
Dev / Tools Dev / Tools
@@ -408,16 +466,19 @@
<span class="opacity-50">{sec_dev ? '▲' : '▼'}</span> <span class="opacity-50">{sec_dev ? '▲' : '▼'}</span>
</button> </button>
{#if sec_dev} {#if sec_dev}
<div class="px-3 pb-3 space-y-2"> <div class="space-y-2 px-3 pb-3">
<!-- iframe mode toggle --> <!-- iframe mode toggle -->
{#if $ae_loc.iframe} {#if $ae_loc.iframe}
<a class="btn btn-sm preset-tonal-secondary w-full justify-end" href="/?iframe=false"> <a
class="btn btn-sm preset-tonal-secondary w-full justify-end"
href="/?iframe=false">
<Minimize2 size="1em" class="opacity-60" /> <Minimize2 size="1em" class="opacity-60" />
Exit iframe Mode Exit iframe Mode
</a> </a>
{:else} {:else}
<a class="btn btn-sm preset-tonal-secondary w-full justify-end" href="/?iframe=true"> <a
class="btn btn-sm preset-tonal-secondary w-full justify-end"
href="/?iframe=true">
<Maximize2 size="1em" class="opacity-60" /> <Maximize2 size="1em" class="opacity-60" />
Enable iframe Mode Enable iframe Mode
</a> </a>
@@ -428,8 +489,7 @@
type="button" type="button"
class="btn btn-sm preset-tonal-warning w-full justify-end" class="btn btn-sm preset-tonal-warning w-full justify-end"
onclick={() => window.location.reload()} onclick={() => window.location.reload()}
title="Hard reload the page" title="Hard reload the page">
>
<RefreshCw size="1em" class="opacity-60" /> <RefreshCw size="1em" class="opacity-60" />
Reload Page Reload Page
</button> </button>
@@ -439,42 +499,47 @@
type="button" type="button"
class="btn btn-sm preset-tonal-error w-full justify-end" class="btn btn-sm preset-tonal-error w-full justify-end"
onclick={handle_clear_storage_db} onclick={handle_clear_storage_db}
title="Clear localStorage, sessionStorage, and all IndexedDB tables then reload" title="Clear localStorage, sessionStorage, and all IndexedDB tables then reload">
>
<Eraser size="1em" class="opacity-60" /> <Eraser size="1em" class="opacity-60" />
Clear Storage & DB Clear Storage & DB
</button> </button>
<!-- URL param builder --> <!-- URL param builder -->
<div class="pt-1 border-t border-gray-100 dark:border-gray-800"> <div
class="border-t border-gray-100 pt-1 dark:border-gray-800">
<E_app_url_builder /> <E_app_url_builder />
</div> </div>
<!-- Debug overlay toggle --> <!-- Debug overlay toggle -->
<div class="pt-1 border-t border-gray-100 dark:border-gray-800"> <div
class="border-t border-gray-100 pt-1 dark:border-gray-800">
<button <button
type="button" type="button"
class="btn btn-sm w-full justify-end {$ae_loc.debug_menu.expand ? 'preset-filled-error-500' : 'preset-outlined-error-400-600'} hover:preset-tonal-error transition-all" class="btn btn-sm w-full justify-end {$ae_loc
onclick={() => { $ae_loc.debug_menu.expand = !$ae_loc.debug_menu.expand; }} .debug_menu.expand
title="Toggle debug overlay ($ae_loc dump)" ? 'preset-filled-error-500'
> : 'preset-outlined-error-400-600'} hover:preset-tonal-error transition-all"
onclick={() => {
$ae_loc.debug_menu.expand =
!$ae_loc.debug_menu.expand;
}}
title="Toggle debug overlay ($ae_loc dump)">
<Bug size="1em" class="shrink-0" /> <Bug size="1em" class="shrink-0" />
<span class="text-xs ml-1"> <span class="ml-1 text-xs">
{$ae_loc.debug_menu.expand ? 'Hide' : 'Show'} Debug Overlay {$ae_loc.debug_menu.expand
? 'Hide'
: 'Show'} Debug Overlay
</span> </span>
</button> </button>
</div> </div>
</div> </div>
{/if} {/if}
</div> </div>
{/if} {/if}
</div> </div>
{/if} {/if}
<!-- END: Expanded panel --> <!-- END: Expanded panel -->
<!-- ── COMPACT BAR ────────────────────────────────────────────────────── --> <!-- ── COMPACT BAR ────────────────────────────────────────────────────── -->
<!-- <!--
Outer div is `group` for group-hover. Outer div is `group` for group-hover.
@@ -482,32 +547,32 @@
Hover labels on individual buttons use nested group/[name] variants. Hover labels on individual buttons use nested group/[name] variants.
delay-500 on the info strip prevents it flashing on quick mouse-overs. delay-500 on the info strip prevents it flashing on quick mouse-overs.
--> -->
<div class="relative group"> <div class="group relative">
<!-- Hover info strip — always in DOM (no mount flash), opacity-only transition. <!-- Hover info strip — always in DOM (no mount flash), opacity-only transition.
pointer-events-none so it never blocks clicks. --> pointer-events-none so it never blocks clicks. -->
<div <div
class=" class="
absolute bottom-full right-0 mb-1 pointer-events-none absolute right-0 bottom-full
pointer-events-none mb-1
transition-opacity duration-200 delay-500
flex items-center gap-1.5 flex items-center gap-1.5
bg-white/30 dark:bg-gray-900/30 rounded-lg border border-gray-200/60
border border-gray-200/60 dark:border-gray-700/60 bg-white/30 px-2
backdrop-blur-sm rounded-lg py-1 text-xs whitespace-nowrap
px-2 py-1 text-gray-500 shadow-md
text-xs text-gray-500 dark:text-gray-400 backdrop-blur-sm transition-opacity
whitespace-nowrap shadow-md delay-500 duration-200 dark:border-gray-700/60
dark:bg-gray-900/30 dark:text-gray-400
" "
class:opacity-0={expand || (!person_display && !access_label)} class:opacity-0={expand || (!person_display && !access_label)}
class:group-hover:opacity-100={!expand && (!!person_display || !!access_label)} class:group-hover:opacity-100={!expand &&
> (!!person_display || !!access_label)}>
{#if person_display} {#if person_display}
<User size="0.8em" class="shrink-0" /> <User size="0.8em" class="shrink-0" />
<span>{person_display}</span> <span>{person_display}</span>
{/if} {/if}
{#if access_label} {#if access_label}
<span class="font-semibold text-primary-600 dark:text-primary-400"> <span
class="text-primary-600 dark:text-primary-400 font-semibold">
· {access_label} · {access_label}
</span> </span>
{/if} {/if}
@@ -517,39 +582,38 @@
<div <div
class=" class="
ae_sys_bar__strip ae_sys_bar__strip
flex flex-row items-center gap-1 flex h-9 flex-row items-center
h-9 gap-1
bg-white/30 dark:bg-gray-900/30 rounded-xl border
border-gray-200/60
bg-white/30 px-2 shadow-lg
backdrop-blur-sm backdrop-blur-sm
border border-gray-200/60 dark:border-gray-700/60 transition-colors
rounded-xl duration-300
px-2 dark:border-gray-700/60 dark:bg-gray-900/30
shadow-lg
transition-colors duration-300
" "
class:border-primary-400={expand} class:border-primary-400={expand}>
>
<!-- AUTH STATUS SHIELD ───────────────────────────────────────── --> <!-- AUTH STATUS SHIELD ───────────────────────────────────────── -->
<button <button
type="button" type="button"
class=" class="
btn btn-sm transition-all duration-200 group/shield btn btn-sm group/shield transition-all duration-200
{$ae_loc?.access_type && $ae_loc?.access_type !== 'anonymous' {$ae_loc?.access_type &&
? ($ae_loc?.user_access_type && $ae_loc?.access_type === $ae_loc?.user_access_type $ae_loc?.access_type !== 'anonymous'
? $ae_loc?.user_access_type &&
$ae_loc?.access_type === $ae_loc?.user_access_type
? 'variant-outline-surface hover:variant-ghost-warning' ? 'variant-outline-surface hover:variant-ghost-warning'
: 'variant-outline-warning hover:variant-ghost-warning') : 'variant-outline-warning hover:variant-ghost-warning'
: 'variant-outline-surface hover:variant-ghost-success'} : 'variant-outline-surface hover:variant-ghost-success'}
" "
onclick={handle_shield_click} onclick={handle_shield_click}
title={ title={$ae_loc?.access_type &&
$ae_loc?.access_type && $ae_loc?.access_type !== 'anonymous' $ae_loc?.access_type !== 'anonymous'
? ($ae_loc?.user_access_type && $ae_loc?.access_type === $ae_loc?.user_access_type ? $ae_loc?.user_access_type &&
$ae_loc?.access_type === $ae_loc?.user_access_type
? `Access: ${$ae_loc?.access_type}. Click to use a passcode.` ? `Access: ${$ae_loc?.access_type}. Click to use a passcode.`
: `Elevated access: ${$ae_loc?.access_type}. Click to clear.`) : `Elevated access: ${$ae_loc?.access_type}. Click to clear.`
: 'Anonymous. Click to sign in or enter a passcode.' : 'Anonymous. Click to sign in or enter a passcode.'}>
}
>
{#if $ae_loc?.access_type && $ae_loc?.access_type !== 'anonymous'} {#if $ae_loc?.access_type && $ae_loc?.access_type !== 'anonymous'}
{#if $ae_loc?.user_access_type && $ae_loc?.access_type === $ae_loc?.user_access_type} {#if $ae_loc?.user_access_type && $ae_loc?.access_type === $ae_loc?.user_access_type}
<ShieldEllipsis size="1.1em" class="shrink-0" /> <ShieldEllipsis size="1.1em" class="shrink-0" />
@@ -559,7 +623,8 @@
{:else} {:else}
<ShieldUser size="1.1em" class="shrink-0" /> <ShieldUser size="1.1em" class="shrink-0" />
{/if} {/if}
<span class="btn-label max-w-0 overflow-hidden opacity-0 group-hover/shield:max-w-20 group-hover/shield:opacity-100 transition-all duration-300 ease-in-out text-xs pl-1"> <span
class="btn-label max-w-0 overflow-hidden pl-1 text-xs opacity-0 transition-all duration-300 ease-in-out group-hover/shield:max-w-20 group-hover/shield:opacity-100">
{access_label ?? 'Auth?'} {access_label ?? 'Auth?'}
</span> </span>
</button> </button>
@@ -567,27 +632,32 @@
<!-- FONT SIZE CYCLER ─────────────────────────────────────────── --> <!-- FONT SIZE CYCLER ─────────────────────────────────────────── -->
<button <button
type="button" type="button"
class="btn btn-sm preset-tonal-surface hover:preset-tonal-primary transition-all duration-200 group/font" class="btn btn-sm preset-tonal-surface hover:preset-tonal-primary group/font transition-all duration-200"
onclick={cycle_font_size} onclick={cycle_font_size}
title={font_title} title={font_title}>
> <span class="text-sm leading-none font-bold">{font_label}</span>
<span class="font-bold leading-none text-sm">{font_label}</span> <span
<span class="btn-label max-w-0 overflow-hidden opacity-0 group-hover/font:max-w-20 group-hover/font:opacity-100 transition-all duration-300 ease-in-out text-xs pl-1">Font</span> class="btn-label max-w-0 overflow-hidden pl-1 text-xs opacity-0 transition-all duration-300 ease-in-out group-hover/font:max-w-20 group-hover/font:opacity-100"
>Font</span>
</button> </button>
<!-- DARK / LIGHT TOGGLE ──────────────────────────────────────── --> <!-- DARK / LIGHT TOGGLE ──────────────────────────────────────── -->
<button <button
type="button" type="button"
class="btn btn-sm preset-tonal-surface hover:preset-tonal-secondary transition-all duration-200 group/mode" class="btn btn-sm preset-tonal-surface hover:preset-tonal-secondary group/mode transition-all duration-200"
onclick={toggle_theme_mode} onclick={toggle_theme_mode}
title="Toggle light/dark mode (currently: {$ae_loc?.theme_mode ?? 'unknown'})" title="Toggle light/dark mode (currently: {$ae_loc?.theme_mode ??
> 'unknown'})">
{#if $ae_loc?.theme_mode === 'dark'} {#if $ae_loc?.theme_mode === 'dark'}
<Moon size="1.1em" class="shrink-0" /> <Moon size="1.1em" class="shrink-0" />
<span class="btn-label max-w-0 overflow-hidden opacity-0 group-hover/mode:max-w-20 group-hover/mode:opacity-100 transition-all duration-300 ease-in-out text-xs pl-1">Dark</span> <span
class="btn-label max-w-0 overflow-hidden pl-1 text-xs opacity-0 transition-all duration-300 ease-in-out group-hover/mode:max-w-20 group-hover/mode:opacity-100"
>Dark</span>
{:else} {:else}
<Sun size="1.1em" class="shrink-0" /> <Sun size="1.1em" class="shrink-0" />
<span class="btn-label max-w-0 overflow-hidden opacity-0 group-hover/mode:max-w-20 group-hover/mode:opacity-100 transition-all duration-300 ease-in-out text-xs pl-1">Light</span> <span
class="btn-label max-w-0 overflow-hidden pl-1 text-xs opacity-0 transition-all duration-300 ease-in-out group-hover/mode:max-w-20 group-hover/mode:opacity-100"
>Light</span>
{/if} {/if}
</button> </button>
@@ -596,22 +666,30 @@
{#if $ae_loc.edit_mode} {#if $ae_loc.edit_mode}
<button <button
type="button" type="button"
class="btn btn-sm preset-tonal-warning hover:preset-tonal-success transition-all duration-200 group/edit" class="btn btn-sm preset-tonal-warning hover:preset-tonal-success group/edit transition-all duration-200"
onclick={() => { $ae_loc.edit_mode = false; }} onclick={() => {
title="Edit mode ON click to turn off" $ae_loc.edit_mode = false;
> }}
<ToggleRight size="1em" class="text-sm inline-block" /> title="Edit mode ON click to turn off">
<span class="btn-label max-w-0 overflow-hidden opacity-0 group-hover/edit:max-w-20 group-hover/edit:opacity-100 transition-all duration-300 ease-in-out text-xs pl-1">Edit</span> <ToggleRight size="1em" class="inline-block text-sm" />
<span
class="btn-label max-w-0 overflow-hidden pl-1 text-xs opacity-0 transition-all duration-300 ease-in-out group-hover/edit:max-w-20 group-hover/edit:opacity-100"
>Edit</span>
</button> </button>
{:else} {:else}
<button <button
type="button" type="button"
class="btn btn-sm preset-tonal-surface hover:preset-tonal-warning transition-all duration-200 group/edit" class="btn btn-sm preset-tonal-surface hover:preset-tonal-warning group/edit transition-all duration-200"
onclick={() => { $ae_loc.edit_mode = true; }} onclick={() => {
title="Edit mode OFF click to turn on" $ae_loc.edit_mode = true;
> }}
<ToggleLeft size="1em" class="text-sm inline-block opacity-50" /> title="Edit mode OFF click to turn on">
<span class="btn-label max-w-0 overflow-hidden opacity-0 group-hover/edit:max-w-20 group-hover/edit:opacity-100 transition-all duration-300 ease-in-out text-xs pl-1">Edit</span> <ToggleLeft
size="1em"
class="inline-block text-sm opacity-50" />
<span
class="btn-label max-w-0 overflow-hidden pl-1 text-xs opacity-0 transition-all duration-300 ease-in-out group-hover/edit:max-w-20 group-hover/edit:opacity-100"
>Edit</span>
</button> </button>
{/if} {/if}
{/if} {/if}
@@ -619,42 +697,43 @@
<!-- MENU EXPAND / COLLAPSE ───────────────────────────────────── --> <!-- MENU EXPAND / COLLAPSE ───────────────────────────────────── -->
<button <button
type="button" type="button"
class="btn btn-sm preset-filled-tertiary-400-600 hover:preset-filled-success transition-all duration-200 group/menu" class="btn btn-sm preset-filled-tertiary-400-600 hover:preset-filled-success group/menu transition-all duration-200"
onclick={toggle_expand} onclick={toggle_expand}
title={expand ? 'Close menu' : 'Open menu'} title={expand ? 'Close menu' : 'Open menu'}>
>
{#if expand} {#if expand}
<CircleX size="1.1em" class="shrink-0" /> <CircleX size="1.1em" class="shrink-0" />
<span class="btn-label max-w-0 overflow-hidden opacity-0 group-hover/menu:max-w-20 group-hover/menu:opacity-100 transition-all duration-300 ease-in-out text-xs pl-1">Close</span> <span
class="btn-label max-w-0 overflow-hidden pl-1 text-xs opacity-0 transition-all duration-300 ease-in-out group-hover/menu:max-w-20 group-hover/menu:opacity-100"
>Close</span>
{:else} {:else}
<Menu size="1.1em" class="shrink-0" /> <Menu size="1.1em" class="shrink-0" />
<span class="btn-label max-w-0 overflow-hidden opacity-0 group-hover/menu:max-w-20 group-hover/menu:opacity-100 transition-all duration-300 ease-in-out text-xs pl-1">Menu</span> <span
class="btn-label max-w-0 overflow-hidden pl-1 text-xs opacity-0 transition-all duration-300 ease-in-out group-hover/menu:max-w-20 group-hover/menu:opacity-100"
>Menu</span>
{/if} {/if}
</button> </button>
</div> </div>
</div> </div>
<!-- END: Compact bar --> <!-- END: Compact bar -->
</div> </div>
<style> <style>
/* /*
* Strip blue backgrounds and fixed widths from sub-components when * Strip blue backgrounds and fixed widths from sub-components when
* rendered inside the sys panel. The panel provides its own white/dark * rendered inside the sys panel. The panel provides its own white/dark
* background and full-width layout — the sub-components' default styling * background and full-width layout — the sub-components' default styling
* was designed for standalone use and clashes inside the panel. * was designed for standalone use and clashes inside the panel.
*/ */
:global(.ae_sys_panel .ae_access_type), :global(.ae_sys_panel .ae_access_type),
:global(.ae_sys_panel .ae_sign_in_out) { :global(.ae_sys_panel .ae_sign_in_out) {
background: transparent !important; background: transparent !important;
border: none !important; border: none !important;
width: 100% !important; width: 100% !important;
max-width: 100% !important; max-width: 100% !important;
padding: 0 !important; padding: 0 !important;
} }
/* /*
* Fix native browser rendering of <select> options in dark mode. * Fix native browser rendering of <select> options in dark mode.
* Without color-scheme, the browser renders <option> elements with its * Without color-scheme, the browser renders <option> elements with its
* default (light) chrome even on a dark background — white text on * default (light) chrome even on a dark background — white text on
@@ -663,14 +742,14 @@
* Applied globally to the panel so it covers sub-components (URL builder, * Applied globally to the panel so it covers sub-components (URL builder,
* access type, etc.) without touching every individual element. * access type, etc.) without touching every individual element.
*/ */
:global(.ae_sys_panel) { :global(.ae_sys_panel) {
color-scheme: light; color-scheme: light;
} }
:global(.dark .ae_sys_panel) { :global(.dark .ae_sys_panel) {
color-scheme: dark; color-scheme: dark;
} }
/* /*
* Dark mode: Skeleton UI v3 form classes (select, input) do not carry * Dark mode: Skeleton UI v3 form classes (select, input) do not carry
* dark-mode color tokens, so without these overrides the elements render * dark-mode color tokens, so without these overrides the elements render
* with a light/white background and dark text — unreadable on the dark * with a light/white background and dark text — unreadable on the dark
@@ -678,19 +757,19 @@
* every select and text-type input inside the panel (sub-components * every select and text-type input inside the panel (sub-components
* included) without needing per-element dark: classes. * included) without needing per-element dark: classes.
*/ */
:global(.dark .ae_sys_panel select), :global(.dark .ae_sys_panel select),
:global(.dark .ae_sys_panel option) { :global(.dark .ae_sys_panel option) {
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 */
} }
:global(.dark .ae_sys_panel input:not([type='checkbox']):not([type='radio'])) { :global(.dark .ae_sys_panel input:not([type='checkbox']):not([type='radio'])) {
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 */
} }
/* Placeholder text should still be legible at reduced opacity */ /* Placeholder text should still be legible at reduced opacity */
:global(.dark .ae_sys_panel input::placeholder) { :global(.dark .ae_sys_panel input::placeholder) {
color: rgb(156 163 175); /* gray-400 */ color: rgb(156 163 175); /* gray-400 */
} }
</style> </style>

View File

@@ -1,10 +1,10 @@
<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,
@@ -18,39 +18,46 @@
ShieldMinus, ShieldMinus,
ShieldPlus, ShieldPlus,
ShieldUser, ShieldUser,
ToggleLeft, ToggleRight, ToggleLeft,
ToggleRight,
User, User,
UserCheck, UserCheck,
UserCog, UserCog,
Wand2, Wand2,
X X
} from '@lucide/svelte'; } 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,5 +1,5 @@
<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.
@@ -15,29 +15,29 @@
* ?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);
@@ -54,32 +54,35 @@
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 // Output mode: full URL (default) or params-only string
let params_only = $state(false); let params_only = $state(false);
let output = $derived.by(() => { let output = $derived.by(() => {
if (!params_only) return built_url; if (!params_only) return built_url;
const u = new URL(built_url); const u = new URL(built_url);
return u.search || '(no params set)'; return u.search || '(no params set)';
}); });
let copied = $state(false); let copied = $state(false);
function copy_url() { function copy_url() {
navigator.clipboard.writeText(output).then(() => { navigator.clipboard.writeText(output).then(() => {
copied = true; copied = true;
setTimeout(() => copied = false, 2000); setTimeout(() => (copied = false), 2000);
}); });
} }
const theme_options = [ const theme_options = [
{ value: 'cerberus', label: 'Cerberus' }, { value: 'cerberus', label: 'Cerberus' },
{ value: 'concord', label: 'Concord' }, { value: 'concord', label: 'Concord' },
{ value: 'crimson', label: 'Crimson' }, { value: 'crimson', label: 'Crimson' },
@@ -96,25 +99,38 @@
{ value: 'AE_Firefly_Indigo', label: 'Firefly Indigo ✦' }, { value: 'AE_Firefly_Indigo', label: 'Firefly Indigo ✦' },
{ value: 'AE_Firefly_Rainbow', label: 'Firefly Rainbow ✨' }, { value: 'AE_Firefly_Rainbow', label: 'Firefly Rainbow ✨' },
{ value: 'AE_c_IDAA_light', label: 'IDAA light' }, { value: 'AE_c_IDAA_light', label: 'IDAA light' },
{ value: 'AE_c_LCI', label: 'LCI' }, { 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,17 +1,22 @@
<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:
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline: outline:
'border-input bg-background hover:bg-accent hover:text-accent-foreground border', 'border-input bg-background hover:bg-accent hover:text-accent-foreground border',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80', secondary:
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground', ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline' link: 'text-primary underline-offset-4 hover:underline'
}, },
@@ -26,12 +31,12 @@
variant: 'default', variant: 'default',
size: '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;
@@ -40,9 +45,9 @@
</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',
@@ -51,7 +56,7 @@
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>

View File

@@ -1,12 +1,12 @@
<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.SubContentProps = $props(); }: DropdownMenuPrimitive.SubContentProps = $props();
</script> </script>
<DropdownMenuPrimitive.SubContent <DropdownMenuPrimitive.SubContent
@@ -15,5 +15,4 @@
'bg-popover text-popover-foreground z-50 min-w-32 rounded-md border p-1 shadow-lg focus:outline-hidden', 'bg-popover text-popover-foreground z-50 min-w-32 rounded-md border p-1 shadow-lg focus:outline-hidden',
className className
)} )}
{...restProps} {...restProps} />
/>

View File

@@ -1,28 +1,27 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import ChevronRight from 'lucide-svelte/icons/chevron-right'; import ChevronRight from 'lucide-svelte/icons/chevron-right';
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,
children, children,
...restProps ...restProps
}: DropdownMenuPrimitive.SubTriggerProps & { }: DropdownMenuPrimitive.SubTriggerProps & {
inset?: boolean; inset?: boolean;
} = $props(); } = $props();
</script> </script>
<DropdownMenuPrimitive.SubTrigger <DropdownMenuPrimitive.SubTrigger
bind:ref bind:ref
class={cn( class={cn(
'data-[highlighted]:bg-accent data-[state=open]:bg-accent flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', 'data-[highlighted]:bg-accent data-[state=open]:bg-accent flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
inset && 'pl-8', inset && 'pl-8',
className className
)} )}
{...restProps} {...restProps}>
>
{@render children?.()} {@render children?.()}
<ChevronRight class="ml-auto" /> <ChevronRight class="ml-auto" />
</DropdownMenuPrimitive.SubTrigger> </DropdownMenuPrimitive.SubTrigger>

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