Fixed chart rendering when using date nav buttons.

This commit is contained in:
Mike Cao 2025-06-12 23:07:25 -07:00
parent 1649992654
commit 7b5591a3ce
18 changed files with 177 additions and 320 deletions

View file

@ -110,7 +110,7 @@
"lucide-react": "^0.511.0", "lucide-react": "^0.511.0",
"maxmind": "^4.3.24", "maxmind": "^4.3.24",
"md5": "^2.3.0", "md5": "^2.3.0",
"next": "15.3.1", "next": "15.3.3",
"node-fetch": "^3.2.8", "node-fetch": "^3.2.8",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"pg": "^8.16.0", "pg": "^8.16.0",
@ -148,6 +148,7 @@
"@types/react-window": "^1.8.8", "@types/react-window": "^1.8.8",
"@typescript-eslint/eslint-plugin": "^8.32.1", "@typescript-eslint/eslint-plugin": "^8.32.1",
"@typescript-eslint/parser": "^8.32.1", "@typescript-eslint/parser": "^8.32.1",
"babel-plugin-react-compiler": "19.1.0-rc.2",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"cypress": "^13.6.6", "cypress": "^13.6.6",
"esbuild": "^0.25.0", "esbuild": "^0.25.0",

156
pnpm-lock.yaml generated
View file

@ -46,7 +46,7 @@ importers:
version: 5.77.2(react@19.1.0) version: 5.77.2(react@19.1.0)
'@umami/react-zen': '@umami/react-zen':
specifier: ^0.137.0 specifier: ^0.137.0
version: 0.137.0(@babel/core@7.27.1)(@types/react@19.1.5)(immer@9.0.21)(use-sync-external-store@1.5.0(react@19.1.0)) version: 0.137.0(@babel/core@7.27.1)(@types/react@19.1.5)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@9.0.21)(use-sync-external-store@1.5.0(react@19.1.0))
'@umami/redis-client': '@umami/redis-client':
specifier: ^0.27.0 specifier: ^0.27.0
version: 0.27.0 version: 0.27.0
@ -132,8 +132,8 @@ importers:
specifier: ^2.3.0 specifier: ^2.3.0
version: 2.3.0 version: 2.3.0
next: next:
specifier: 15.3.1 specifier: 15.3.3
version: 15.3.1(@babel/core@7.27.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) version: 15.3.3(@babel/core@7.27.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
node-fetch: node-fetch:
specifier: ^3.2.8 specifier: ^3.2.8
version: 3.3.2 version: 3.3.2
@ -240,6 +240,9 @@ importers:
'@typescript-eslint/parser': '@typescript-eslint/parser':
specifier: ^8.32.1 specifier: ^8.32.1
version: 8.32.1(eslint@8.57.1)(typescript@5.8.3) version: 8.32.1(eslint@8.57.1)(typescript@5.8.3)
babel-plugin-react-compiler:
specifier: 19.1.0-rc.2
version: 19.1.0-rc.2
cross-env: cross-env:
specifier: ^7.0.3 specifier: ^7.0.3
version: 7.0.3 version: 7.0.3
@ -1378,105 +1381,54 @@ packages:
resolution: {integrity: sha512-9Hgd/J5nP2U/Vv0teytq9uUAGppiKV9t5tzpsuMLqeqUGD9STxXwKmyZd2v8Z4THSW9rw4+8w7dH7LVlFoym2A==} resolution: {integrity: sha512-9Hgd/J5nP2U/Vv0teytq9uUAGppiKV9t5tzpsuMLqeqUGD9STxXwKmyZd2v8Z4THSW9rw4+8w7dH7LVlFoym2A==}
engines: {node: '>=18.0.0'} engines: {node: '>=18.0.0'}
'@next/env@15.3.1':
resolution: {integrity: sha512-cwK27QdzrMblHSn9DZRV+DQscHXRuJv6MydlJRpFSqJWZrTYMLzKDeyueJNN9MGd8NNiUKzDQADAf+dMLXX7YQ==}
'@next/env@15.3.3': '@next/env@15.3.3':
resolution: {integrity: sha512-OdiMrzCl2Xi0VTjiQQUK0Xh7bJHnOuET2s+3V+Y40WJBAXrJeGA3f+I8MZJ/YQ3mVGi5XGR1L66oFlgqXhQ4Vw==} resolution: {integrity: sha512-OdiMrzCl2Xi0VTjiQQUK0Xh7bJHnOuET2s+3V+Y40WJBAXrJeGA3f+I8MZJ/YQ3mVGi5XGR1L66oFlgqXhQ4Vw==}
'@next/eslint-plugin-next@14.2.29': '@next/eslint-plugin-next@14.2.29':
resolution: {integrity: sha512-qpxSYiPNJTr9RzqjGi5yom8AIC8Kgdtw4oNIXAB/gDYMDctmfMEv452FRUhT06cWPgcmSsbZiEPYhbFiQtCWTg==} resolution: {integrity: sha512-qpxSYiPNJTr9RzqjGi5yom8AIC8Kgdtw4oNIXAB/gDYMDctmfMEv452FRUhT06cWPgcmSsbZiEPYhbFiQtCWTg==}
'@next/swc-darwin-arm64@15.3.1':
resolution: {integrity: sha512-hjDw4f4/nla+6wysBL07z52Gs55Gttp5Bsk5/8AncQLJoisvTBP0pRIBK/B16/KqQyH+uN4Ww8KkcAqJODYH3w==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@next/swc-darwin-arm64@15.3.3': '@next/swc-darwin-arm64@15.3.3':
resolution: {integrity: sha512-WRJERLuH+O3oYB4yZNVahSVFmtxRNjNF1I1c34tYMoJb0Pve+7/RaLAJJizyYiFhjYNGHRAE1Ri2Fd23zgDqhg==} resolution: {integrity: sha512-WRJERLuH+O3oYB4yZNVahSVFmtxRNjNF1I1c34tYMoJb0Pve+7/RaLAJJizyYiFhjYNGHRAE1Ri2Fd23zgDqhg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@next/swc-darwin-x64@15.3.1':
resolution: {integrity: sha512-q+aw+cJ2ooVYdCEqZVk+T4Ni10jF6Fo5DfpEV51OupMaV5XL6pf3GCzrk6kSSZBsMKZtVC1Zm/xaNBFpA6bJ2g==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@next/swc-darwin-x64@15.3.3': '@next/swc-darwin-x64@15.3.3':
resolution: {integrity: sha512-XHdzH/yBc55lu78k/XwtuFR/ZXUTcflpRXcsu0nKmF45U96jt1tsOZhVrn5YH+paw66zOANpOnFQ9i6/j+UYvw==} resolution: {integrity: sha512-XHdzH/yBc55lu78k/XwtuFR/ZXUTcflpRXcsu0nKmF45U96jt1tsOZhVrn5YH+paw66zOANpOnFQ9i6/j+UYvw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@next/swc-linux-arm64-gnu@15.3.1':
resolution: {integrity: sha512-wBQ+jGUI3N0QZyWmmvRHjXjTWFy8o+zPFLSOyAyGFI94oJi+kK/LIZFJXeykvgXUk1NLDAEFDZw/NVINhdk9FQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@next/swc-linux-arm64-gnu@15.3.3': '@next/swc-linux-arm64-gnu@15.3.3':
resolution: {integrity: sha512-VZ3sYL2LXB8znNGcjhocikEkag/8xiLgnvQts41tq6i+wql63SMS1Q6N8RVXHw5pEUjiof+II3HkDd7GFcgkzw==} resolution: {integrity: sha512-VZ3sYL2LXB8znNGcjhocikEkag/8xiLgnvQts41tq6i+wql63SMS1Q6N8RVXHw5pEUjiof+II3HkDd7GFcgkzw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@next/swc-linux-arm64-musl@15.3.1':
resolution: {integrity: sha512-IIxXEXRti/AulO9lWRHiCpUUR8AR/ZYLPALgiIg/9ENzMzLn3l0NSxVdva7R/VDcuSEBo0eGVCe3evSIHNz0Hg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@next/swc-linux-arm64-musl@15.3.3': '@next/swc-linux-arm64-musl@15.3.3':
resolution: {integrity: sha512-h6Y1fLU4RWAp1HPNJWDYBQ+e3G7sLckyBXhmH9ajn8l/RSMnhbuPBV/fXmy3muMcVwoJdHL+UtzRzs0nXOf9SA==} resolution: {integrity: sha512-h6Y1fLU4RWAp1HPNJWDYBQ+e3G7sLckyBXhmH9ajn8l/RSMnhbuPBV/fXmy3muMcVwoJdHL+UtzRzs0nXOf9SA==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@next/swc-linux-x64-gnu@15.3.1':
resolution: {integrity: sha512-bfI4AMhySJbyXQIKH5rmLJ5/BP7bPwuxauTvVEiJ/ADoddaA9fgyNNCcsbu9SlqfHDoZmfI6g2EjzLwbsVTr5A==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@next/swc-linux-x64-gnu@15.3.3': '@next/swc-linux-x64-gnu@15.3.3':
resolution: {integrity: sha512-jJ8HRiF3N8Zw6hGlytCj5BiHyG/K+fnTKVDEKvUCyiQ/0r5tgwO7OgaRiOjjRoIx2vwLR+Rz8hQoPrnmFbJdfw==} resolution: {integrity: sha512-jJ8HRiF3N8Zw6hGlytCj5BiHyG/K+fnTKVDEKvUCyiQ/0r5tgwO7OgaRiOjjRoIx2vwLR+Rz8hQoPrnmFbJdfw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@next/swc-linux-x64-musl@15.3.1':
resolution: {integrity: sha512-FeAbR7FYMWR+Z+M5iSGytVryKHiAsc0x3Nc3J+FD5NVbD5Mqz7fTSy8CYliXinn7T26nDMbpExRUI/4ekTvoiA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@next/swc-linux-x64-musl@15.3.3': '@next/swc-linux-x64-musl@15.3.3':
resolution: {integrity: sha512-HrUcTr4N+RgiiGn3jjeT6Oo208UT/7BuTr7K0mdKRBtTbT4v9zJqCDKO97DUqqoBK1qyzP1RwvrWTvU6EPh/Cw==} resolution: {integrity: sha512-HrUcTr4N+RgiiGn3jjeT6Oo208UT/7BuTr7K0mdKRBtTbT4v9zJqCDKO97DUqqoBK1qyzP1RwvrWTvU6EPh/Cw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@next/swc-win32-arm64-msvc@15.3.1':
resolution: {integrity: sha512-yP7FueWjphQEPpJQ2oKmshk/ppOt+0/bB8JC8svPUZNy0Pi3KbPx2Llkzv1p8CoQa+D2wknINlJpHf3vtChVBw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@next/swc-win32-arm64-msvc@15.3.3': '@next/swc-win32-arm64-msvc@15.3.3':
resolution: {integrity: sha512-SxorONgi6K7ZUysMtRF3mIeHC5aA3IQLmKFQzU0OuhuUYwpOBc1ypaLJLP5Bf3M9k53KUUUj4vTPwzGvl/NwlQ==} resolution: {integrity: sha512-SxorONgi6K7ZUysMtRF3mIeHC5aA3IQLmKFQzU0OuhuUYwpOBc1ypaLJLP5Bf3M9k53KUUUj4vTPwzGvl/NwlQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@next/swc-win32-x64-msvc@15.3.1':
resolution: {integrity: sha512-3PMvF2zRJAifcRNni9uMk/gulWfWS+qVI/pagd+4yLF5bcXPZPPH2xlYRYOsUjmCJOXSTAC2PjRzbhsRzR2fDQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
'@next/swc-win32-x64-msvc@15.3.3': '@next/swc-win32-x64-msvc@15.3.3':
resolution: {integrity: sha512-4QZG6F8enl9/S2+yIiOiju0iCTFd93d8VC1q9LZS4p/Xuk81W2QDjCFeoogmrWWkAD59z8ZxepBQap2dKS5ruw==} resolution: {integrity: sha512-4QZG6F8enl9/S2+yIiOiju0iCTFd93d8VC1q9LZS4p/Xuk81W2QDjCFeoogmrWWkAD59z8ZxepBQap2dKS5ruw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@ -2907,6 +2859,9 @@ packages:
resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
babel-plugin-react-compiler@19.1.0-rc.2:
resolution: {integrity: sha512-kSNA//p5fMO6ypG8EkEVPIqAjwIXm5tMjfD1XRPL/sRjYSbJ6UsvORfaeolNWnZ9n310aM0xJP7peW26BuCVzA==}
babel-plugin-react-intl@7.9.4: babel-plugin-react-intl@7.9.4:
resolution: {integrity: sha512-cMKrHEXrw43yT4M89Wbgq8A8N8lffSquj1Piwov/HVukR7jwOw8gf9btXNsQhT27ccyqEwy+M286JQYy0jby2g==} resolution: {integrity: sha512-cMKrHEXrw43yT4M89Wbgq8A8N8lffSquj1Piwov/HVukR7jwOw8gf9btXNsQhT27ccyqEwy+M286JQYy0jby2g==}
deprecated: this package has been renamed to babel-plugin-formatjs deprecated: this package has been renamed to babel-plugin-formatjs
@ -3026,8 +2981,8 @@ packages:
caniuse-lite@1.0.30001718: caniuse-lite@1.0.30001718:
resolution: {integrity: sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==} resolution: {integrity: sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==}
caniuse-lite@1.0.30001721: caniuse-lite@1.0.30001722:
resolution: {integrity: sha512-cOuvmUVtKrtEaoKiO0rSc29jcjwMwX5tOHDy4MgVFEWiUXj4uBMJkwI8MDySkgXidpMiHUcviogAvFi4pA2hDQ==} resolution: {integrity: sha512-DCQHBBZtiK6JVkAGw7drvAMK0Q0POD/xZvEmDp6baiMMP6QXXk9HpD6mNYBZWhOPG6LvIDb82ITqtWjhDckHCA==}
caseless@0.12.0: caseless@0.12.0:
resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
@ -5131,27 +5086,6 @@ packages:
natural-compare@1.4.0: natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
next@15.3.1:
resolution: {integrity: sha512-8+dDV0xNLOgHlyBxP1GwHGVaNXsmp+2NhZEYrXr24GWLHtt27YrBPbPuHvzlhi7kZNYjeJNR93IF5zfFu5UL0g==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
hasBin: true
peerDependencies:
'@opentelemetry/api': ^1.1.0
'@playwright/test': ^1.41.2
babel-plugin-react-compiler: '*'
react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
sass: ^1.3.0
peerDependenciesMeta:
'@opentelemetry/api':
optional: true
'@playwright/test':
optional: true
babel-plugin-react-compiler:
optional: true
sass:
optional: true
next@15.3.3: next@15.3.3:
resolution: {integrity: sha512-JqNj29hHNmCLtNvd090SyRbXJiivQ+58XjCcrC50Crb5g5u2zi7Y2YivbsEfzk6AtVI80akdOQbaMZwWB1Hthw==} resolution: {integrity: sha512-JqNj29hHNmCLtNvd090SyRbXJiivQ+58XjCcrC50Crb5g5u2zi7Y2YivbsEfzk6AtVI80akdOQbaMZwWB1Hthw==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
@ -8133,59 +8067,33 @@ snapshots:
'@netlify/plugin-nextjs@5.11.2': {} '@netlify/plugin-nextjs@5.11.2': {}
'@next/env@15.3.1': {}
'@next/env@15.3.3': {} '@next/env@15.3.3': {}
'@next/eslint-plugin-next@14.2.29': '@next/eslint-plugin-next@14.2.29':
dependencies: dependencies:
glob: 10.3.10 glob: 10.3.10
'@next/swc-darwin-arm64@15.3.1':
optional: true
'@next/swc-darwin-arm64@15.3.3': '@next/swc-darwin-arm64@15.3.3':
optional: true optional: true
'@next/swc-darwin-x64@15.3.1':
optional: true
'@next/swc-darwin-x64@15.3.3': '@next/swc-darwin-x64@15.3.3':
optional: true optional: true
'@next/swc-linux-arm64-gnu@15.3.1':
optional: true
'@next/swc-linux-arm64-gnu@15.3.3': '@next/swc-linux-arm64-gnu@15.3.3':
optional: true optional: true
'@next/swc-linux-arm64-musl@15.3.1':
optional: true
'@next/swc-linux-arm64-musl@15.3.3': '@next/swc-linux-arm64-musl@15.3.3':
optional: true optional: true
'@next/swc-linux-x64-gnu@15.3.1':
optional: true
'@next/swc-linux-x64-gnu@15.3.3': '@next/swc-linux-x64-gnu@15.3.3':
optional: true optional: true
'@next/swc-linux-x64-musl@15.3.1':
optional: true
'@next/swc-linux-x64-musl@15.3.3': '@next/swc-linux-x64-musl@15.3.3':
optional: true optional: true
'@next/swc-win32-arm64-msvc@15.3.1':
optional: true
'@next/swc-win32-arm64-msvc@15.3.3': '@next/swc-win32-arm64-msvc@15.3.3':
optional: true optional: true
'@next/swc-win32-x64-msvc@15.3.1':
optional: true
'@next/swc-win32-x64-msvc@15.3.3': '@next/swc-win32-x64-msvc@15.3.3':
optional: true optional: true
@ -9810,7 +9718,7 @@ snapshots:
'@typescript-eslint/types': 8.32.1 '@typescript-eslint/types': 8.32.1
eslint-visitor-keys: 4.2.0 eslint-visitor-keys: 4.2.0
'@umami/react-zen@0.137.0(@babel/core@7.27.1)(@types/react@19.1.5)(immer@9.0.21)(use-sync-external-store@1.5.0(react@19.1.0))': '@umami/react-zen@0.137.0(@babel/core@7.27.1)(@types/react@19.1.5)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@9.0.21)(use-sync-external-store@1.5.0(react@19.1.0))':
dependencies: dependencies:
'@fontsource/jetbrains-mono': 5.2.6 '@fontsource/jetbrains-mono': 5.2.6
'@internationalized/date': 3.8.2 '@internationalized/date': 3.8.2
@ -9820,7 +9728,7 @@ snapshots:
glob: 10.4.5 glob: 10.4.5
highlight.js: 11.11.1 highlight.js: 11.11.1
lucide-react: 0.511.0(react@19.1.0) lucide-react: 0.511.0(react@19.1.0)
next: 15.3.3(@babel/core@7.27.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next: 15.3.3(@babel/core@7.27.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react: 19.1.0 react: 19.1.0
react-aria-components: 1.9.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react-aria-components: 1.9.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react-dom: 19.1.0(react@19.1.0) react-dom: 19.1.0(react@19.1.0)
@ -10172,6 +10080,10 @@ snapshots:
'@types/babel__core': 7.20.5 '@types/babel__core': 7.20.5
'@types/babel__traverse': 7.20.7 '@types/babel__traverse': 7.20.7
babel-plugin-react-compiler@19.1.0-rc.2:
dependencies:
'@babel/types': 7.27.1
babel-plugin-react-intl@7.9.4(ts-jest@29.3.4(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(esbuild@0.25.5)(jest@29.7.0(@types/node@22.15.21)(ts-node@10.9.2(@types/node@22.15.21)(typescript@5.8.3)))(typescript@5.8.3)): babel-plugin-react-intl@7.9.4(ts-jest@29.3.4(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(esbuild@0.25.5)(jest@29.7.0(@types/node@22.15.21)(ts-node@10.9.2(@types/node@22.15.21)(typescript@5.8.3)))(typescript@5.8.3)):
dependencies: dependencies:
'@babel/core': 7.27.1 '@babel/core': 7.27.1
@ -10315,13 +10227,13 @@ snapshots:
caniuse-api@3.0.0: caniuse-api@3.0.0:
dependencies: dependencies:
browserslist: 4.24.5 browserslist: 4.24.5
caniuse-lite: 1.0.30001718 caniuse-lite: 1.0.30001722
lodash.memoize: 4.1.2 lodash.memoize: 4.1.2
lodash.uniq: 4.5.0 lodash.uniq: 4.5.0
caniuse-lite@1.0.30001718: {} caniuse-lite@1.0.30001718: {}
caniuse-lite@1.0.30001721: {} caniuse-lite@1.0.30001722: {}
caseless@0.12.0: {} caseless@0.12.0: {}
@ -12838,38 +12750,13 @@ snapshots:
natural-compare@1.4.0: {} natural-compare@1.4.0: {}
next@15.3.1(@babel/core@7.27.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): next@15.3.3(@babel/core@7.27.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
'@next/env': 15.3.1
'@swc/counter': 0.1.3
'@swc/helpers': 0.5.15
busboy: 1.6.0
caniuse-lite: 1.0.30001718
postcss: 8.4.31
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
styled-jsx: 5.1.6(@babel/core@7.27.1)(react@19.1.0)
optionalDependencies:
'@next/swc-darwin-arm64': 15.3.1
'@next/swc-darwin-x64': 15.3.1
'@next/swc-linux-arm64-gnu': 15.3.1
'@next/swc-linux-arm64-musl': 15.3.1
'@next/swc-linux-x64-gnu': 15.3.1
'@next/swc-linux-x64-musl': 15.3.1
'@next/swc-win32-arm64-msvc': 15.3.1
'@next/swc-win32-x64-msvc': 15.3.1
sharp: 0.34.2
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
next@15.3.3(@babel/core@7.27.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies: dependencies:
'@next/env': 15.3.3 '@next/env': 15.3.3
'@swc/counter': 0.1.3 '@swc/counter': 0.1.3
'@swc/helpers': 0.5.15 '@swc/helpers': 0.5.15
busboy: 1.6.0 busboy: 1.6.0
caniuse-lite: 1.0.30001721 caniuse-lite: 1.0.30001722
postcss: 8.4.31 postcss: 8.4.31
react: 19.1.0 react: 19.1.0
react-dom: 19.1.0(react@19.1.0) react-dom: 19.1.0(react@19.1.0)
@ -12883,6 +12770,7 @@ snapshots:
'@next/swc-linux-x64-musl': 15.3.3 '@next/swc-linux-x64-musl': 15.3.3
'@next/swc-win32-arm64-msvc': 15.3.3 '@next/swc-win32-arm64-msvc': 15.3.3
'@next/swc-win32-x64-msvc': 15.3.3 '@next/swc-win32-x64-msvc': 15.3.3
babel-plugin-react-compiler: 19.1.0-rc.2
sharp: 0.34.2 sharp: 0.34.2
transitivePeerDependencies: transitivePeerDependencies:
- '@babel/core' - '@babel/core'

View file

@ -1,4 +1,5 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { LoadingPanel } from '@/components/common/LoadingPanel';
import { PageviewsChart } from '@/components/metrics/PageviewsChart'; import { PageviewsChart } from '@/components/metrics/PageviewsChart';
import { useWebsitePageviewsQuery } from '@/components/hooks/queries/useWebsitePageviewsQuery'; import { useWebsitePageviewsQuery } from '@/components/hooks/queries/useWebsitePageviewsQuery';
import { useDateRange } from '@/components/hooks'; import { useDateRange } from '@/components/hooks';
@ -12,7 +13,7 @@ export function WebsiteChart({
}) { }) {
const { dateRange, dateCompare } = useDateRange(websiteId); const { dateRange, dateCompare } = useDateRange(websiteId);
const { startDate, endDate, unit, value } = dateRange; const { startDate, endDate, unit, value } = dateRange;
const { data, isLoading } = useWebsitePageviewsQuery( const { data, isLoading, error } = useWebsitePageviewsQuery(
websiteId, websiteId,
compareMode ? dateCompare : undefined, compareMode ? dateCompare : undefined,
); );
@ -46,13 +47,14 @@ export function WebsiteChart({
}, [data, startDate, endDate, unit]); }, [data, startDate, endDate, unit]);
return ( return (
<PageviewsChart <LoadingPanel isLoading={isLoading} error={error}>
data={chartData} <PageviewsChart
minDate={startDate.toISOString()} key={value}
maxDate={endDate.toISOString()} data={chartData}
unit={unit} minDate={value === 'all' ? undefined : startDate}
isLoading={isLoading} maxDate={endDate}
isAllTime={value === 'all'} unit={unit}
/> />
</LoadingPanel>
); );
} }

View file

@ -30,7 +30,7 @@ export function SessionProperties({ websiteId }: { websiteId: string }) {
: null; : null;
return ( return (
<LoadingPanel isLoading={isLoading} isFetched={isFetched} data={data} error={error}> <LoadingPanel isLoading={isLoading} isFetched={isFetched} error={error}>
<div className={styles.container}> <div className={styles.container}>
<DataTable data={data} className={styles.table}> <DataTable data={data} className={styles.table}>
<DataColumn id="propertyName" label={formatMessage(labels.property)}> <DataColumn id="propertyName" label={formatMessage(labels.property)}>
@ -45,7 +45,7 @@ export function SessionProperties({ websiteId }: { websiteId: string }) {
{propertyName && ( {propertyName && (
<div className={styles.chart}> <div className={styles.chart}>
<div className={styles.title}>{propertyName}</div> <div className={styles.title}>{propertyName}</div>
<PieChart key={propertyName} type="doughnut" data={chartData} /> <PieChart key={propertyName} type="doughnut" chartData={chartData} />
</div> </div>
)} )}
</div> </div>

View file

@ -6,7 +6,7 @@ import { getDayOfWeekAsDate } from '@/lib/date';
import { Focusable, Tooltip, TooltipTrigger } from '@umami/react-zen'; import { Focusable, Tooltip, TooltipTrigger } from '@umami/react-zen';
export function SessionsWeekly({ websiteId }: { websiteId: string }) { export function SessionsWeekly({ websiteId }: { websiteId: string }) {
const { data, ...props } = useWebsiteSessionsWeeklyQuery(websiteId); const { data, isLoading, error } = useWebsiteSessionsWeeklyQuery(websiteId);
const { dateLocale } = useLocale(); const { dateLocale } = useLocale();
const { labels, formatMessage } = useMessages(); const { labels, formatMessage } = useMessages();
const { weekStartsOn } = dateLocale.options; const { weekStartsOn } = dateLocale.options;
@ -36,7 +36,7 @@ export function SessionsWeekly({ websiteId }: { websiteId: string }) {
: []; : [];
return ( return (
<LoadingPanel {...(props as any)} data={data}> <LoadingPanel isEmpty={!data?.length} isLoading={isLoading} error={error}>
<Grid columns="repeat(8, 1fr)" gap> <Grid columns="repeat(8, 1fr)" gap>
<Grid rows="repeat(25, 20px)" gap="1"> <Grid rows="repeat(25, 20px)" gap="1">
<Row>&nbsp;</Row> <Row>&nbsp;</Row>

View file

@ -1,21 +0,0 @@
.timeline {
display: flex;
flex-direction: column;
gap: 20px;
}
.row {
display: grid;
grid-template-columns: max-content max-content 1fr;
align-items: center;
gap: 20px;
}
.time {
color: var(--font-color200);
width: 150px;
}
.header {
font-weight: bold;
}

View file

@ -1,9 +1,8 @@
import { isSameDay } from 'date-fns'; import { isSameDay } from 'date-fns';
import { Loading, Icon, StatusLight } from '@umami/react-zen'; import { Icon, StatusLight, Column, Row, Heading, Text, Button } from '@umami/react-zen';
import { Bolt, Eye } from '@/components/icons'; import { LoadingPanel } from '@/components/common/LoadingPanel';
import { Bolt, Eye, FileText } from '@/components/icons';
import { useSessionActivityQuery, useTimezone } from '@/components/hooks'; import { useSessionActivityQuery, useTimezone } from '@/components/hooks';
import styles from './SessionActivity.module.css';
import { Fragment } from 'react';
export function SessionActivity({ export function SessionActivity({
websiteId, websiteId,
@ -17,37 +16,46 @@ export function SessionActivity({
endDate: Date; endDate: Date;
}) { }) {
const { formatTimezoneDate } = useTimezone(); const { formatTimezoneDate } = useTimezone();
const { data, isLoading } = useSessionActivityQuery(websiteId, sessionId, startDate, endDate); const { data, isLoading, error } = useSessionActivityQuery(
websiteId,
if (isLoading) { sessionId,
return <Loading position="page" />; startDate,
} endDate,
);
let lastDay = null; let lastDay = null;
return ( return (
<div className={styles.timeline}> <LoadingPanel isEmpty={!data?.length} isLoading={isLoading} error={error}>
{data.map(({ id, createdAt, urlPath, eventName, visitId }) => { <Column gap>
const showHeader = !lastDay || !isSameDay(new Date(lastDay), new Date(createdAt)); {data?.map(({ eventId, createdAt, urlPath, eventName, visitId, hasData }) => {
lastDay = createdAt; const showHeader = !lastDay || !isSameDay(new Date(lastDay), new Date(createdAt));
lastDay = createdAt;
return ( return (
<Fragment key={id}> <Column key={eventId} gap>
{showHeader && ( {showHeader && <Heading size="2">{formatTimezoneDate(createdAt, 'PPPP')}</Heading>}
<div className={styles.header}>{formatTimezoneDate(createdAt, 'PPPP')}</div> <Row alignItems="center" gap="6" height="40px">
)}
<div className={styles.row}>
<div className={styles.time}>
<StatusLight color={`#${visitId?.substring(0, 6)}`}> <StatusLight color={`#${visitId?.substring(0, 6)}`}>
{formatTimezoneDate(createdAt, 'pp')} {formatTimezoneDate(createdAt, 'pp')}
</StatusLight> </StatusLight>
</div> <Row alignItems="center" gap>
<Icon>{eventName ? <Bolt /> : <Eye />}</Icon> <Icon>{eventName ? <Bolt /> : <Eye />}</Icon>
<div>{eventName || urlPath}</div> <Text>{eventName || urlPath}</Text>
</div> {hasData > 0 && (
</Fragment> <Button variant="quiet">
); <Row alignItems="center" gap>
})} <Icon>
</div> <FileText />
</Icon>
</Row>
</Button>
)}
</Row>
</Row>
</Column>
);
})}
</Column>
</LoadingPanel>
); );
} }

View file

@ -30,10 +30,10 @@ export interface BarChartProps extends ChartProps {
YAxisType?: string; YAxisType?: string;
minDate?: Date; minDate?: Date;
maxDate?: Date; maxDate?: Date;
isAllTime?: boolean;
} }
export function BarChart({ export function BarChart({
chartData,
renderXLabel, renderXLabel,
renderYLabel, renderYLabel,
unit, unit,
@ -43,7 +43,6 @@ export function BarChart({
minDate, minDate,
maxDate, maxDate,
currency, currency,
isAllTime,
...props ...props
}: BarChartProps) { }: BarChartProps) {
const [tooltip, setTooltip] = useState(null); const [tooltip, setTooltip] = useState(null);
@ -51,14 +50,14 @@ export function BarChart({
const { locale } = useLocale(); const { locale } = useLocale();
const { colors } = useMemo(() => getThemeColors(theme), [theme]); const { colors } = useMemo(() => getThemeColors(theme), [theme]);
const options: any = useMemo(() => { const chartOptions: any = useMemo(() => {
return { return {
__id: new Date().getTime(), __id: new Date().getTime(),
scales: { scales: {
x: { x: {
type: XAxisType, type: XAxisType,
stacked: true, stacked: true,
min: isAllTime ? '' : minDate, min: minDate,
max: maxDate, max: maxDate,
time: { time: {
unit, unit,
@ -94,7 +93,7 @@ export function BarChart({
}, },
}, },
}; };
}, [colors, unit, stacked, renderXLabel, renderYLabel]); }, [chartData, colors, unit, stacked, renderXLabel, renderYLabel]);
const handleTooltip = ({ tooltip }: { tooltip: any }) => { const handleTooltip = ({ tooltip }: { tooltip: any }) => {
const { opacity, labelColors, dataPoints } = tooltip; const { opacity, labelColors, dataPoints } = tooltip;
@ -121,9 +120,9 @@ export function BarChart({
<Chart <Chart
{...props} {...props}
type="bar" type="bar"
chartOptions={options} chartData={chartData}
chartOptions={chartOptions}
onTooltip={handleTooltip} onTooltip={handleTooltip}
height="400px"
/> />
{tooltip && <ChartTooltip {...tooltip} />} {tooltip && <ChartTooltip {...tooltip} />}
</> </>

View file

@ -1,29 +1,25 @@
import { useState, useRef, useEffect, useMemo } from 'react'; import { useState, useRef, useEffect, useMemo } from 'react';
import { Loading, Box, Column, BoxProps } from '@umami/react-zen'; import { Box, Column, BoxProps } from '@umami/react-zen';
import ChartJS, { LegendItem, ChartOptions } from 'chart.js/auto'; import ChartJS, { LegendItem, ChartOptions, ChartData, UpdateMode } from 'chart.js/auto';
import { Legend } from '@/components/metrics/Legend'; import { Legend } from '@/components/metrics/Legend';
import { DEFAULT_ANIMATION_DURATION } from '@/lib/constants'; import { DEFAULT_ANIMATION_DURATION } from '@/lib/constants';
ChartJS.defaults.font.family = 'Inter';
export interface ChartProps extends BoxProps { export interface ChartProps extends BoxProps {
type?: 'bar' | 'bubble' | 'doughnut' | 'pie' | 'line' | 'polarArea' | 'radar' | 'scatter'; type?: 'bar' | 'bubble' | 'doughnut' | 'pie' | 'line' | 'polarArea' | 'radar' | 'scatter';
data?: object; chartData?: ChartData & { focusLabel?: string };
isLoading?: boolean;
animationDuration?: number;
updateMode?: string;
onCreate?: (chart: any) => void;
onUpdate?: (chart: any) => void;
onTooltip?: (model: any) => void;
chartOptions?: ChartOptions; chartOptions?: ChartOptions;
updateMode?: UpdateMode;
animationDuration?: number;
onTooltip?: (model: any) => void;
} }
export function Chart({ export function Chart({
type, type,
data, chartData,
isLoading = false,
animationDuration = DEFAULT_ANIMATION_DURATION, animationDuration = DEFAULT_ANIMATION_DURATION,
updateMode, updateMode,
onCreate,
onUpdate,
onTooltip, onTooltip,
chartOptions, chartOptions,
...props ...props
@ -59,53 +55,6 @@ export function Chart({
}; };
}, [chartOptions]); }, [chartOptions]);
const createChart = (data: any) => {
ChartJS.defaults.font.family = 'Inter';
chart.current = new ChartJS(canvas.current, {
type,
data,
options,
});
onCreate?.(chart.current);
setLegendItems(chart.current.legend.legendItems);
};
const updateChart = (data: any) => {
if (data.datasets) {
if (data.datasets.length === chart.current.data.datasets.length) {
chart.current.data.datasets.forEach((dataset: { data: any }, index: string | number) => {
if (data?.datasets[index]) {
dataset.data = data?.datasets[index]?.data;
if (chart.current.legend.legendItems[index]) {
chart.current.legend.legendItems[index].text = data.datasets[index]?.label;
}
}
});
} else {
chart.current.data.datasets = data.datasets;
}
}
if (data.focusLabel !== null) {
chart.current.data.datasets.forEach(ds => {
ds.hidden = data.focusLabel ? ds.label !== data.focusLabel : false;
});
}
chart.current.options = options;
// Allow config changes before update
onUpdate?.(chart.current);
chart.current.update(updateMode);
setLegendItems(chart.current.legend.legendItems);
};
const handleLegendClick = (item: LegendItem) => { const handleLegendClick = (item: LegendItem) => {
if (type === 'bar') { if (type === 'bar') {
const { datasetIndex } = item; const { datasetIndex } = item;
@ -127,20 +76,47 @@ export function Chart({
setLegendItems(chart.current.legend.legendItems); setLegendItems(chart.current.legend.legendItems);
}; };
// Create chart
useEffect(() => { useEffect(() => {
if (data) { if (canvas.current) {
if (!chart.current) { chart.current = new ChartJS(canvas.current, {
createChart(data); type,
} else { data: chartData,
updateChart(data); options,
} });
setLegendItems(chart.current.legend.legendItems);
} }
}, [data, options]);
return () => {
chart.current?.destroy();
};
}, []);
// Update chart
useEffect(() => {
if (chart.current && chartData) {
// Replace labels and datasets *in-place*
chart.current.data.labels = chartData.labels;
chart.current.data.datasets = chartData.datasets;
if (chartData.focusLabel !== null) {
chart.current.data.datasets.forEach((ds: { hidden: boolean; label: any }) => {
ds.hidden = chartData.focusLabel ? ds.label !== chartData.focusLabel : false;
});
}
chart.current.options = options;
chart.current.update(updateMode);
setLegendItems(chart.current.legend.legendItems);
}
}, [chartData, options, updateMode]);
return ( return (
<Column gap="6"> <Column gap="6">
<Box {...props}> <Box {...props}>
{isLoading && <Loading position="page" icon="dots" />}
<canvas ref={canvas} /> <canvas ref={canvas} />
</Box> </Box>
<Legend items={legendItems} onClick={handleLegendClick} /> <Legend items={legendItems} onClick={handleLegendClick} />

View file

@ -1,9 +1,9 @@
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import Link from 'next/link'; import Link from 'next/link';
import { Button } from '@umami/react-zen'; import { Button, ButtonProps } from '@umami/react-zen';
import { useLocale } from '@/components/hooks'; import { useLocale } from '@/components/hooks';
export interface LinkButtonProps { export interface LinkButtonProps extends ButtonProps {
href: string; href: string;
target?: string; target?: string;
scroll?: boolean; scroll?: boolean;

View file

@ -6,12 +6,15 @@ export {
ChevronRight as Chevron, ChevronRight as Chevron,
Clock, Clock,
Copy, Copy,
Database,
Download, Download,
Edit, Edit,
Ellipsis, Ellipsis,
Eye, Eye,
ExternalLink, ExternalLink,
File, File,
FileJson,
FileText,
Globe, Globe,
Grid2X2, Grid2X2,
KeyRound, KeyRound,
@ -25,6 +28,7 @@ export {
Minimize, Minimize,
Moon, Moon,
MoreHorizontal as More, MoreHorizontal as More,
Paperclip,
PanelLeft, PanelLeft,
Plus, Plus,
RefreshCw as Refresh, RefreshCw as Refresh,

View file

@ -88,6 +88,7 @@ export function ReportEditButton({
title={formatMessage(labels.delete)} title={formatMessage(labels.delete)}
onConfirm={handleDelete} onConfirm={handleDelete}
onCancel={handleClose} onCancel={handleClose}
isDanger
> >
<Row gap="1"> <Row gap="1">
{formatMessage(messages.confirmDelete, { target: <b key={name}>{name}</b> })} {formatMessage(messages.confirmDelete, { target: <b key={name}>{name}</b> })}

View file

@ -47,7 +47,7 @@ export function WebsiteDateFilter({
}; };
const handleIncrement = (increment: number) => { const handleIncrement = (increment: number) => {
router.push(renderUrl({ increment })); router.push(renderUrl({ offset: offset + increment }));
saveDateRange(getOffsetDateRange(dateRange, increment)); saveDateRange(getOffsetDateRange(dateRange, increment));
}; };

View file

@ -1,26 +1,26 @@
import { useMemo, useState, useEffect } from 'react'; import { useMemo, useState, useEffect } from 'react';
import { colord } from 'colord'; import { colord } from 'colord';
import { BarChart } from '@/components/charts/BarChart'; import { BarChart, BarChartProps } from '@/components/charts/BarChart';
import { useDateRange, useLocale, useWebsiteEventsSeriesQuery } from '@/components/hooks'; import { useDateRange, useLocale, useWebsiteEventsSeriesQuery } from '@/components/hooks';
import { renderDateLabels } from '@/lib/charts'; import { renderDateLabels } from '@/lib/charts';
import { CHART_COLORS } from '@/lib/constants'; import { CHART_COLORS } from '@/lib/constants';
import { LoadingPanel } from '@/components/common/LoadingPanel';
export interface EventsChartProps { export interface EventsChartProps extends BarChartProps {
websiteId: string; websiteId: string;
className?: string;
focusLabel?: string; focusLabel?: string;
} }
export function EventsChart({ websiteId, className, focusLabel }: EventsChartProps) { export function EventsChart({ websiteId, focusLabel }: EventsChartProps) {
const { const {
dateRange: { startDate, endDate, unit, value }, dateRange: { startDate, endDate, unit, value },
} = useDateRange(websiteId); } = useDateRange(websiteId);
const { locale } = useLocale(); const { locale } = useLocale();
const { data, isLoading } = useWebsiteEventsSeriesQuery(websiteId); const { data, isLoading, error } = useWebsiteEventsSeriesQuery(websiteId);
const [label, setLabel] = useState<string>(focusLabel); const [label, setLabel] = useState<string>(focusLabel);
const chartData = useMemo(() => { const chartData: any = useMemo(() => {
if (!data) return []; if (!data) return;
const map = (data as any[]).reduce((obj, { x, t, y }) => { const map = (data as any[]).reduce((obj, { x, t, y }) => {
if (!obj[x]) { if (!obj[x]) {
@ -55,16 +55,18 @@ export function EventsChart({ websiteId, className, focusLabel }: EventsChartPro
}, [focusLabel]); }, [focusLabel]);
return ( return (
<BarChart <LoadingPanel isLoading={isLoading} error={error}>
minDate={startDate} {chartData && (
maxDate={endDate} <BarChart
className={className} chartData={chartData}
data={chartData} minDate={value === 'all' ? undefined : startDate}
unit={unit} maxDate={endDate}
stacked={true} unit={unit}
renderXLabel={renderDateLabels(unit, locale)} stacked={true}
isLoading={isLoading} renderXLabel={renderDateLabels(unit, locale)}
isAllTime={value === 'all'} height="400px"
/> />
)}
</LoadingPanel>
); );
} }

View file

@ -96,13 +96,20 @@ const AnimatedRow = ({
}) => { }) => {
const props = useSpring({ const props = useSpring({
width: percent, width: percent,
y: value, y: !isNaN(value) ? value : 0,
from: { width: 0, y: 0 }, from: { width: 0, y: 0 },
config: animate ? config.default : { duration: 0 }, config: animate ? config.default : { duration: 0 },
}); });
return ( return (
<Grid columns="1fr 50px 50px" paddingLeft="2" alignItems="center" hoverBackgroundColor="2" gap> <Grid
columns="1fr 50px 50px"
paddingLeft="2"
alignItems="center"
hoverBackgroundColor="2"
borderRadius
gap
>
<Row alignItems="center"> <Row alignItems="center">
<Text>{label}</Text> <Text>{label}</Text>
</Row> </Row>

View file

@ -15,26 +15,16 @@ export interface PageviewsChartProps extends BarChartProps {
}; };
}; };
unit: string; unit: string;
isLoading?: boolean;
isAllTime?: boolean;
} }
export function PageviewsChart({ export function PageviewsChart({ data, unit, ...props }: PageviewsChartProps) {
data,
unit,
isLoading,
isAllTime,
...props
}: PageviewsChartProps) {
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const { theme } = useTheme(); const { theme } = useTheme();
const { locale } = useLocale(); const { locale } = useLocale();
const { colors } = useMemo(() => getThemeColors(theme), [theme]); const { colors } = useMemo(() => getThemeColors(theme), [theme]);
const chartData = useMemo(() => { const chartData: any = useMemo(() => {
if (!data) { if (!data) return;
return {};
}
return { return {
__id: new Date().getTime(), __id: new Date().getTime(),
@ -84,11 +74,10 @@ export function PageviewsChart({
return ( return (
<BarChart <BarChart
{...props} {...props}
data={chartData} chartData={chartData}
unit={unit} unit={unit}
isLoading={isLoading}
isAllTime={isAllTime}
renderXLabel={renderXLabel} renderXLabel={renderXLabel}
height="400px"
/> />
); );
} }

View file

@ -38,8 +38,8 @@ export function RealtimeChart({ data, unit, ...props }: RealtimeChartProps) {
return ( return (
<PageviewsChart <PageviewsChart
{...props} {...props}
minDate={startDate.toISOString()} minDate={startDate}
maxDate={endDate.toISOString()} maxDate={endDate}
unit={unit} unit={unit}
data={chartData} data={chartData}
animationDuration={animationDuration} animationDuration={animationDuration}

View file

@ -46,12 +46,13 @@ async function clickhouseQuery(
event_id as eventId, event_id as eventId,
event_type as eventType, event_type as eventType,
event_name as eventName, event_name as eventName,
visit_id as visitId visit_id as visitId,
from website_event event_id IN (SELECT event_id FROM event_data) AS hasData
where website_id = {websiteId:UUID} from website_event e
and session_id = {sessionId:UUID} where e.website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64} and e.session_id = {sessionId:UUID}
order by created_at desc and e.created_at between {startDate:DateTime64} and {endDate:DateTime64}
order by e.created_at desc
limit 500 limit 500
`, `,
{ websiteId, sessionId, startDate, endDate }, { websiteId, sessionId, startDate, endDate },