New properties screens. New website nav.

This commit is contained in:
Mike Cao 2025-07-17 01:18:31 -07:00
parent a9a9b57f80
commit 01bfd7f52e
17 changed files with 536 additions and 557 deletions

View file

@ -76,13 +76,13 @@
"@dicebear/core": "^9.2.1",
"@fontsource/inter": "^4.5.15",
"@hello-pangea/dnd": "^17.0.0",
"@prisma/adapter-pg": "^6.11.0",
"@prisma/client": "^6.11.0",
"@prisma/adapter-pg": "^6.12.0",
"@prisma/client": "^6.12.0",
"@prisma/extension-read-replicas": "^0.4.1",
"@react-spring/web": "^9.7.3",
"@svgr/cli": "^8.1.0",
"@tanstack/react-query": "^5.80.10",
"@umami/react-zen": "^0.147.0",
"@umami/react-zen": "^0.148.0",
"@umami/redis-client": "^0.27.0",
"bcryptjs": "^2.4.3",
"chalk": "^4.1.1",
@ -111,11 +111,11 @@
"lucide-react": "^0.517.0",
"maxmind": "^4.3.27",
"md5": "^2.3.0",
"next": "15.3.4",
"next": "15.4.1",
"node-fetch": "^3.2.8",
"npm-run-all": "^4.1.5",
"pg": "^8.16.3",
"prisma": "6.11.0",
"prisma": "6.12.0",
"pure-rand": "^6.1.0",
"react": "^19.0.0",
"react-basics": "^0.126.0",

442
pnpm-lock.yaml generated
View file

@ -27,14 +27,14 @@ importers:
specifier: ^17.0.0
version: 17.0.0(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@prisma/adapter-pg':
specifier: ^6.11.0
version: 6.11.0
specifier: ^6.12.0
version: 6.12.0
'@prisma/client':
specifier: ^6.11.0
version: 6.11.0(prisma@6.11.0(typescript@5.8.3))(typescript@5.8.3)
specifier: ^6.12.0
version: 6.12.0(prisma@6.12.0(typescript@5.8.3))(typescript@5.8.3)
'@prisma/extension-read-replicas':
specifier: ^0.4.1
version: 0.4.1(@prisma/client@6.11.0(prisma@6.11.0(typescript@5.8.3))(typescript@5.8.3))
version: 0.4.1(@prisma/client@6.12.0(prisma@6.12.0(typescript@5.8.3))(typescript@5.8.3))
'@react-spring/web':
specifier: ^9.7.3
version: 9.7.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@ -45,8 +45,8 @@ importers:
specifier: ^5.80.10
version: 5.80.10(react@19.1.0)
'@umami/react-zen':
specifier: ^0.147.0
version: 0.147.0(@babel/core@7.27.1)(@types/react@19.1.8)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@9.0.21)(use-sync-external-store@1.5.0(react@19.1.0))
specifier: ^0.148.0
version: 0.148.0(@babel/core@7.27.1)(@types/react@19.1.8)(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':
specifier: ^0.27.0
version: 0.27.0
@ -132,8 +132,8 @@ importers:
specifier: ^2.3.0
version: 2.3.0
next:
specifier: 15.3.4
version: 15.3.4(@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)
specifier: 15.4.1
version: 15.4.1(@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:
specifier: ^3.2.8
version: 3.3.2
@ -144,8 +144,8 @@ importers:
specifier: ^8.16.3
version: 8.16.3
prisma:
specifier: 6.11.0
version: 6.11.0(typescript@5.8.3)
specifier: 6.12.0
version: 6.12.0(typescript@5.8.3)
pure-rand:
specifier: ^6.1.0
version: 6.1.0
@ -860,6 +860,9 @@ packages:
'@emnapi/runtime@1.4.3':
resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==}
'@emnapi/runtime@1.4.4':
resolution: {integrity: sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==}
'@emnapi/wasi-threads@1.0.2':
resolution: {integrity: sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==}
@ -1141,118 +1144,124 @@ packages:
resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==}
deprecated: Use @eslint/object-schema instead
'@img/sharp-darwin-arm64@0.34.2':
resolution: {integrity: sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==}
'@img/sharp-darwin-arm64@0.34.3':
resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [darwin]
'@img/sharp-darwin-x64@0.34.2':
resolution: {integrity: sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==}
'@img/sharp-darwin-x64@0.34.3':
resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [darwin]
'@img/sharp-libvips-darwin-arm64@1.1.0':
resolution: {integrity: sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==}
'@img/sharp-libvips-darwin-arm64@1.2.0':
resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==}
cpu: [arm64]
os: [darwin]
'@img/sharp-libvips-darwin-x64@1.1.0':
resolution: {integrity: sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==}
'@img/sharp-libvips-darwin-x64@1.2.0':
resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==}
cpu: [x64]
os: [darwin]
'@img/sharp-libvips-linux-arm64@1.1.0':
resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==}
'@img/sharp-libvips-linux-arm64@1.2.0':
resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==}
cpu: [arm64]
os: [linux]
'@img/sharp-libvips-linux-arm@1.1.0':
resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==}
'@img/sharp-libvips-linux-arm@1.2.0':
resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==}
cpu: [arm]
os: [linux]
'@img/sharp-libvips-linux-ppc64@1.1.0':
resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==}
'@img/sharp-libvips-linux-ppc64@1.2.0':
resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==}
cpu: [ppc64]
os: [linux]
'@img/sharp-libvips-linux-s390x@1.1.0':
resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==}
'@img/sharp-libvips-linux-s390x@1.2.0':
resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==}
cpu: [s390x]
os: [linux]
'@img/sharp-libvips-linux-x64@1.1.0':
resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==}
'@img/sharp-libvips-linux-x64@1.2.0':
resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==}
cpu: [x64]
os: [linux]
'@img/sharp-libvips-linuxmusl-arm64@1.1.0':
resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==}
'@img/sharp-libvips-linuxmusl-arm64@1.2.0':
resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==}
cpu: [arm64]
os: [linux]
'@img/sharp-libvips-linuxmusl-x64@1.1.0':
resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==}
'@img/sharp-libvips-linuxmusl-x64@1.2.0':
resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==}
cpu: [x64]
os: [linux]
'@img/sharp-linux-arm64@0.34.2':
resolution: {integrity: sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==}
'@img/sharp-linux-arm64@0.34.3':
resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
'@img/sharp-linux-arm@0.34.2':
resolution: {integrity: sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==}
'@img/sharp-linux-arm@0.34.3':
resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
'@img/sharp-linux-s390x@0.34.2':
resolution: {integrity: sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==}
'@img/sharp-linux-ppc64@0.34.3':
resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ppc64]
os: [linux]
'@img/sharp-linux-s390x@0.34.3':
resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
'@img/sharp-linux-x64@0.34.2':
resolution: {integrity: sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==}
'@img/sharp-linux-x64@0.34.3':
resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
'@img/sharp-linuxmusl-arm64@0.34.2':
resolution: {integrity: sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==}
'@img/sharp-linuxmusl-arm64@0.34.3':
resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
'@img/sharp-linuxmusl-x64@0.34.2':
resolution: {integrity: sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==}
'@img/sharp-linuxmusl-x64@0.34.3':
resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
'@img/sharp-wasm32@0.34.2':
resolution: {integrity: sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==}
'@img/sharp-wasm32@0.34.3':
resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [wasm32]
'@img/sharp-win32-arm64@0.34.2':
resolution: {integrity: sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==}
'@img/sharp-win32-arm64@0.34.3':
resolution: {integrity: sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [win32]
'@img/sharp-win32-ia32@0.34.2':
resolution: {integrity: sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==}
'@img/sharp-win32-ia32@0.34.3':
resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ia32]
os: [win32]
'@img/sharp-win32-x64@0.34.2':
resolution: {integrity: sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==}
'@img/sharp-win32-x64@0.34.3':
resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [win32]
@ -1381,56 +1390,56 @@ packages:
resolution: {integrity: sha512-SCF2UPT+mDrfO3DDeUb7eTRqHycBEx4aJ8vACm17Nyn2b2ueaLlS5u/2V42SSZF/F6LydI7R78fv3xPW7HHdWw==}
engines: {node: '>=18.0.0'}
'@next/env@15.3.4':
resolution: {integrity: sha512-ZkdYzBseS6UjYzz6ylVKPOK+//zLWvD6Ta+vpoye8cW11AjiQjGYVibF0xuvT4L0iJfAPfZLFidaEzAOywyOAQ==}
'@next/env@15.4.1':
resolution: {integrity: sha512-DXQwFGAE2VH+f2TJsKepRXpODPU+scf5fDbKOME8MMyeyswe4XwgRdiiIYmBfkXU+2ssliLYznajTrOQdnLR5A==}
'@next/eslint-plugin-next@14.2.30':
resolution: {integrity: sha512-mvVsMIutMxQ4NGZEMZ1kiBNc+la8Xmlk30bKUmCPQz2eFkmsLv54Mha8QZarMaCtSPkkFA1TMD+FIZk0l/PpzA==}
'@next/swc-darwin-arm64@15.3.4':
resolution: {integrity: sha512-z0qIYTONmPRbwHWvpyrFXJd5F9YWLCsw3Sjrzj2ZvMYy9NPQMPZ1NjOJh4ojr4oQzcGYwgJKfidzehaNa1BpEg==}
'@next/swc-darwin-arm64@15.4.1':
resolution: {integrity: sha512-L+81yMsiHq82VRXS2RVq6OgDwjvA4kDksGU8hfiDHEXP+ncKIUhUsadAVB+MRIp2FErs/5hpXR0u2eluWPAhig==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@next/swc-darwin-x64@15.3.4':
resolution: {integrity: sha512-Z0FYJM8lritw5Wq+vpHYuCIzIlEMjewG2aRkc3Hi2rcbULknYL/xqfpBL23jQnCSrDUGAo/AEv0Z+s2bff9Zkw==}
'@next/swc-darwin-x64@15.4.1':
resolution: {integrity: sha512-jfz1RXu6SzL14lFl05/MNkcN35lTLMJWPbqt7Xaj35+ZWAX342aePIJrN6xBdGeKl6jPXJm0Yqo3Xvh3Gpo3Uw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@next/swc-linux-arm64-gnu@15.3.4':
resolution: {integrity: sha512-l8ZQOCCg7adwmsnFm8m5q9eIPAHdaB2F3cxhufYtVo84pymwKuWfpYTKcUiFcutJdp9xGHC+F1Uq3xnFU1B/7g==}
'@next/swc-linux-arm64-gnu@15.4.1':
resolution: {integrity: sha512-k0tOFn3dsnkaGfs6iQz8Ms6f1CyQe4GacXF979sL8PNQxjYS1swx9VsOyUQYaPoGV8nAZ7OX8cYaeiXGq9ahPQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@next/swc-linux-arm64-musl@15.3.4':
resolution: {integrity: sha512-wFyZ7X470YJQtpKot4xCY3gpdn8lE9nTlldG07/kJYexCUpX1piX+MBfZdvulo+t1yADFVEuzFfVHfklfEx8kw==}
'@next/swc-linux-arm64-musl@15.4.1':
resolution: {integrity: sha512-4ogGQ/3qDzbbK3IwV88ltihHFbQVq6Qr+uEapzXHXBH1KsVBZOB50sn6BWHPcFjwSoMX2Tj9eH/fZvQnSIgc3g==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@next/swc-linux-x64-gnu@15.3.4':
resolution: {integrity: sha512-gEbH9rv9o7I12qPyvZNVTyP/PWKqOp8clvnoYZQiX800KkqsaJZuOXkWgMa7ANCCh/oEN2ZQheh3yH8/kWPSEg==}
'@next/swc-linux-x64-gnu@15.4.1':
resolution: {integrity: sha512-Jj0Rfw3wIgp+eahMz/tOGwlcYYEFjlBPKU7NqoOkTX0LY45i5W0WcDpgiDWSLrN8KFQq/LW7fZq46gxGCiOYlQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@next/swc-linux-x64-musl@15.3.4':
resolution: {integrity: sha512-Cf8sr0ufuC/nu/yQ76AnarbSAXcwG/wj+1xFPNbyNo8ltA6kw5d5YqO8kQuwVIxk13SBdtgXrNyom3ZosHAy4A==}
'@next/swc-linux-x64-musl@15.4.1':
resolution: {integrity: sha512-9WlEZfnw1vFqkWsTMzZDgNL7AUI1aiBHi0S2m8jvycPyCq/fbZjtE/nDkhJRYbSjXbtRHYLDBlmP95kpjEmJbw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@next/swc-win32-arm64-msvc@15.3.4':
resolution: {integrity: sha512-ay5+qADDN3rwRbRpEhTOreOn1OyJIXS60tg9WMYTWCy3fB6rGoyjLVxc4dR9PYjEdR2iDYsaF5h03NA+XuYPQQ==}
'@next/swc-win32-arm64-msvc@15.4.1':
resolution: {integrity: sha512-WodRbZ9g6CQLRZsG3gtrA9w7Qfa9BwDzhFVdlI6sV0OCPq9JrOrJSp9/ioLsezbV8w9RCJ8v55uzJuJ5RgWLZg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@next/swc-win32-x64-msvc@15.3.4':
resolution: {integrity: sha512-4kDt31Bc9DGyYs41FTL1/kNpDeHyha2TC0j5sRRoKCyrhNcfZ/nRQkAUlF27mETwm8QyHqIjHJitfcza2Iykfg==}
'@next/swc-win32-x64-msvc@15.4.1':
resolution: {integrity: sha512-y+wTBxelk2xiNofmDOVU7O5WxTHcvOoL3srOM0kxTzKDjQ57kPU0tpnPJ/BWrRnsOwXEv0+3QSbGR7hY4n9LkQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@ -1455,11 +1464,11 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
'@prisma/adapter-pg@6.11.0':
resolution: {integrity: sha512-FMZwHoF54WVZT/26dQmk8Gbsud/JeHUny+POvaN0Aztr+eomycNOo/dHefU+nCm+fBI/vuwlAaCAAtPkeC+m2w==}
'@prisma/adapter-pg@6.12.0':
resolution: {integrity: sha512-lyNDD0oJntw9D6WilEUCXqaKPZWDLNwePbUZogadhIJ4qICaIoPXQuLGGe7auXdvGcfesCb1y1UKvdYj0iFgQg==}
'@prisma/client@6.11.0':
resolution: {integrity: sha512-K9TkKepOYvCOg3qCuKz7ZHf6rf58BFKi08plKjU4qVv9y7/UxO6tLz7PlWcgODUZKURLPmRHjHERffIx/8az4w==}
'@prisma/client@6.12.0':
resolution: {integrity: sha512-wn98bJ3Cj6edlF4jjpgXwbnQIo/fQLqqQHPk2POrZPxTlhY3+n90SSIF3LMRVa8VzRFC/Gec3YKJRxRu+AIGVA==}
engines: {node: '>=18.18'}
peerDependencies:
prisma: '*'
@ -1470,31 +1479,31 @@ packages:
typescript:
optional: true
'@prisma/config@6.11.0':
resolution: {integrity: sha512-icBfutMpdrwSf2ggo012zhQ4oianijXL/UPbv4PNVK3WUWbB3/F5Ltq8ZfElGrtwKC6XuFFPxU5qDC9x7vh8zQ==}
'@prisma/config@6.12.0':
resolution: {integrity: sha512-HovZWzhWEMedHxmjefQBRZa40P81N7/+74khKFz9e1AFjakcIQdXgMWKgt20HaACzY+d1LRBC+L4tiz71t9fkg==}
'@prisma/debug@6.11.0':
resolution: {integrity: sha512-zo4oEZMWMt0BFWl+4NK9FUpaEOmjGR3y2/r0lkW/DK4BUBRgMj90s8QqK2K+vXG3xn0nAGg2kOSu+Swn60CFLg==}
'@prisma/debug@6.12.0':
resolution: {integrity: sha512-plbz6z72orcqr0eeio7zgUrZj5EudZUpAeWkFTA/DDdXEj28YHDXuiakvR6S7sD6tZi+jiwQEJAPeV6J6m/tEQ==}
'@prisma/driver-adapter-utils@6.11.0':
resolution: {integrity: sha512-fuXpLkhHxY2x7/i1IylscOgmaKdMtvalumSBaTkJ2z3LG0Z/+px6aP3lKBGU6NVX5FrCnrTlK+dxifJaWzPJrA==}
'@prisma/driver-adapter-utils@6.12.0':
resolution: {integrity: sha512-uSUKB17Xs4pZB1UJZL6+PHV9Ab6vCWD20nXoimb5YG4vPqtBVdPKNYa35QbDgdUQbX321daUcgYTsLY/jxOG3w==}
'@prisma/engines-version@6.11.0-18.9c30299f5a0ea26a96790e13f796dc6094db3173':
resolution: {integrity: sha512-M3vbyDICFIA1oJl0cFkM0omD4HsJZjFi0hu0f0UxyPABH8KEcZyUd5BToCrNl4B8lUeQn+L5+gfaQleOKp6Lrg==}
'@prisma/engines-version@6.12.0-15.8047c96bbd92db98a2abc7c9323ce77c02c89dbc':
resolution: {integrity: sha512-70vhecxBJlRr06VfahDzk9ow4k1HIaSfVUT3X0/kZoHCMl9zbabut4gEXAyzJZxaCGi5igAA7SyyfBI//mmkbQ==}
'@prisma/engines@6.11.0':
resolution: {integrity: sha512-uqnYxvPKZPvYZA7F0q4gTR+fVWUJSY5bif7JAKBIOD5SoRRy0qEIaPy4Nna5WDLQaFGshaY/Bh8dLOQMfxhJJw==}
'@prisma/engines@6.12.0':
resolution: {integrity: sha512-4BRZZUaAuB4p0XhTauxelvFs7IllhPmNLvmla0bO1nkECs8n/o1pUvAVbQ/VOrZR5DnF4HED0PrGai+rIOVePA==}
'@prisma/extension-read-replicas@0.4.1':
resolution: {integrity: sha512-mCMDloqUKUwx2o5uedTs1FHX3Nxdt1GdRMoeyp1JggjiwOALmIYWhxfIN08M2BZ0w8SKwvJqicJZMjkQYkkijw==}
peerDependencies:
'@prisma/client': ^6.5.0
'@prisma/fetch-engine@6.11.0':
resolution: {integrity: sha512-ZHHSP7vJFo5hePH+MNovxhqXabIg38ZpCwQfUBON29kwPX3f1pjYnzGpgJLCJy4k7mKGOzTgrXPqH8+nJvq2fw==}
'@prisma/fetch-engine@6.12.0':
resolution: {integrity: sha512-EamoiwrK46rpWaEbLX9aqKDPOd8IyLnZAkiYXFNuq0YsU0Z8K09/rH8S7feOWAVJ3xzeSgcEJtBlVDrajM9Sag==}
'@prisma/get-platform@6.11.0':
resolution: {integrity: sha512-yspBGvOfJQwuoApk5B4aBlHDy6YDXAOe4Ml8U2eZ+M2b7fDd10YDomS3Q4qrYHUUVYF3TJyN86NcnRMOvCMUrA==}
'@prisma/get-platform@6.12.0':
resolution: {integrity: sha512-nRerTGhTlgyvcBlyWgt8OLNIV7QgJS2XYXMJD1hysorMCuLAjuDDuoxmVt7C2nLxbuxbWPp7OuFRHC23HqD9dA==}
'@react-aria/autocomplete@3.0.0-beta.3':
resolution: {integrity: sha512-8haBygHNMqVt4Ge90VOk+iVlLW+zhiOGHYz2IKCE6+Sy1dTE6mzhHjxrtwWYnSez/OQLbxjHlwLch4CDd5JkLA==}
@ -2307,9 +2316,6 @@ packages:
peerDependencies:
'@svgr/core': '*'
'@swc/counter@0.1.3':
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
@ -2547,8 +2553,8 @@ packages:
resolution: {integrity: sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@umami/react-zen@0.147.0':
resolution: {integrity: sha512-29A4IxHX/IC4w2v9cCXg8uU8lZ3VPVUrlJH9mngTKSSaCcMzlkgNjKosrQ2xV+wEsMWHl1zV3JRmLL0sgJ9TxA==}
'@umami/react-zen@0.148.0':
resolution: {integrity: sha512-4bI8wBgqep6bYNsnhQ87lSAYqiKLj/9o1emFfIot0NIxKd2jsmgEwGrymxDjmunXK4OwRs8ukOYvzuH3WbieGA==}
'@umami/redis-client@0.27.0':
resolution: {integrity: sha512-SbHTpxhgeZyTBUSp2zdZM+XUtpsaSL4Tad8QXIEhEtjWhvvfoornyT5kLuyYCVtzSAT4daALeGmOO1z6EE1KcA==}
@ -2942,10 +2948,6 @@ packages:
buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
busboy@1.6.0:
resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
engines: {node: '>=10.16.0'}
cachedir@2.4.0:
resolution: {integrity: sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==}
engines: {node: '>=6'}
@ -2988,9 +2990,6 @@ packages:
caniuse-lite@1.0.30001718:
resolution: {integrity: sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==}
caniuse-lite@1.0.30001726:
resolution: {integrity: sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==}
caniuse-lite@1.0.30001727:
resolution: {integrity: sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==}
@ -5101,13 +5100,13 @@ packages:
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
next@15.3.4:
resolution: {integrity: sha512-mHKd50C+mCjam/gcnwqL1T1vPx/XQNFlXqFIVdgQdVAFY9iIQtY0IfaVflEYzKiqjeA7B0cYYMaCrmAYFjs4rA==}
next@15.4.1:
resolution: {integrity: sha512-eNKB1q8C7o9zXF8+jgJs2CzSLIU3T6bQtX6DcTnCq1sIR1CJ0GlSyRs1BubQi3/JgCnr9Vr+rS5mOMI38FFyQw==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
hasBin: true
peerDependencies:
'@opentelemetry/api': ^1.1.0
'@playwright/test': ^1.41.2
'@playwright/test': ^1.51.1
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
@ -5858,8 +5857,8 @@ packages:
resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
prisma@6.11.0:
resolution: {integrity: sha512-gI69E7fusgk32XALpXzdgR10xUx2aFnHiu/JaUo4O07G4JvFT0xNtD0Iy81p37iBLTYFEhWa9VrHKXaiyZ5fLQ==}
prisma@6.12.0:
resolution: {integrity: sha512-pmV7NEqQej9WjizN6RSNIwf7Y+jeh9mY1JEX2WjGxJi4YZWexClhde1yz/FuvAM+cTwzchcMytu2m4I6wPkIzg==}
engines: {node: '>=18.18'}
hasBin: true
peerDependencies:
@ -6243,8 +6242,8 @@ packages:
resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
engines: {node: '>= 0.4'}
sharp@0.34.2:
resolution: {integrity: sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==}
sharp@0.34.3:
resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
shebang-command@1.2.0:
@ -6371,10 +6370,6 @@ packages:
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
engines: {node: '>=10'}
streamsearch@1.1.0:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'}
string-argv@0.3.2:
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
engines: {node: '>=0.6.19'}
@ -7473,6 +7468,11 @@ snapshots:
tslib: 2.8.1
optional: true
'@emnapi/runtime@1.4.4':
dependencies:
tslib: 2.8.1
optional: true
'@emnapi/wasi-threads@1.0.2':
dependencies:
tslib: 2.8.1
@ -7753,85 +7753,90 @@ snapshots:
'@humanwhocodes/object-schema@2.0.3': {}
'@img/sharp-darwin-arm64@0.34.2':
'@img/sharp-darwin-arm64@0.34.3':
optionalDependencies:
'@img/sharp-libvips-darwin-arm64': 1.1.0
'@img/sharp-libvips-darwin-arm64': 1.2.0
optional: true
'@img/sharp-darwin-x64@0.34.2':
'@img/sharp-darwin-x64@0.34.3':
optionalDependencies:
'@img/sharp-libvips-darwin-x64': 1.1.0
'@img/sharp-libvips-darwin-x64': 1.2.0
optional: true
'@img/sharp-libvips-darwin-arm64@1.1.0':
'@img/sharp-libvips-darwin-arm64@1.2.0':
optional: true
'@img/sharp-libvips-darwin-x64@1.1.0':
'@img/sharp-libvips-darwin-x64@1.2.0':
optional: true
'@img/sharp-libvips-linux-arm64@1.1.0':
'@img/sharp-libvips-linux-arm64@1.2.0':
optional: true
'@img/sharp-libvips-linux-arm@1.1.0':
'@img/sharp-libvips-linux-arm@1.2.0':
optional: true
'@img/sharp-libvips-linux-ppc64@1.1.0':
'@img/sharp-libvips-linux-ppc64@1.2.0':
optional: true
'@img/sharp-libvips-linux-s390x@1.1.0':
'@img/sharp-libvips-linux-s390x@1.2.0':
optional: true
'@img/sharp-libvips-linux-x64@1.1.0':
'@img/sharp-libvips-linux-x64@1.2.0':
optional: true
'@img/sharp-libvips-linuxmusl-arm64@1.1.0':
'@img/sharp-libvips-linuxmusl-arm64@1.2.0':
optional: true
'@img/sharp-libvips-linuxmusl-x64@1.1.0':
'@img/sharp-libvips-linuxmusl-x64@1.2.0':
optional: true
'@img/sharp-linux-arm64@0.34.2':
'@img/sharp-linux-arm64@0.34.3':
optionalDependencies:
'@img/sharp-libvips-linux-arm64': 1.1.0
'@img/sharp-libvips-linux-arm64': 1.2.0
optional: true
'@img/sharp-linux-arm@0.34.2':
'@img/sharp-linux-arm@0.34.3':
optionalDependencies:
'@img/sharp-libvips-linux-arm': 1.1.0
'@img/sharp-libvips-linux-arm': 1.2.0
optional: true
'@img/sharp-linux-s390x@0.34.2':
'@img/sharp-linux-ppc64@0.34.3':
optionalDependencies:
'@img/sharp-libvips-linux-s390x': 1.1.0
'@img/sharp-libvips-linux-ppc64': 1.2.0
optional: true
'@img/sharp-linux-x64@0.34.2':
'@img/sharp-linux-s390x@0.34.3':
optionalDependencies:
'@img/sharp-libvips-linux-x64': 1.1.0
'@img/sharp-libvips-linux-s390x': 1.2.0
optional: true
'@img/sharp-linuxmusl-arm64@0.34.2':
'@img/sharp-linux-x64@0.34.3':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-arm64': 1.1.0
'@img/sharp-libvips-linux-x64': 1.2.0
optional: true
'@img/sharp-linuxmusl-x64@0.34.2':
'@img/sharp-linuxmusl-arm64@0.34.3':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-x64': 1.1.0
'@img/sharp-libvips-linuxmusl-arm64': 1.2.0
optional: true
'@img/sharp-wasm32@0.34.2':
'@img/sharp-linuxmusl-x64@0.34.3':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-x64': 1.2.0
optional: true
'@img/sharp-wasm32@0.34.3':
dependencies:
'@emnapi/runtime': 1.4.3
'@emnapi/runtime': 1.4.4
optional: true
'@img/sharp-win32-arm64@0.34.2':
'@img/sharp-win32-arm64@0.34.3':
optional: true
'@img/sharp-win32-ia32@0.34.2':
'@img/sharp-win32-ia32@0.34.3':
optional: true
'@img/sharp-win32-x64@0.34.2':
'@img/sharp-win32-x64@0.34.3':
optional: true
'@internationalized/date@3.8.2':
@ -8070,34 +8075,34 @@ snapshots:
'@netlify/plugin-nextjs@5.11.3': {}
'@next/env@15.3.4': {}
'@next/env@15.4.1': {}
'@next/eslint-plugin-next@14.2.30':
dependencies:
glob: 10.3.10
'@next/swc-darwin-arm64@15.3.4':
'@next/swc-darwin-arm64@15.4.1':
optional: true
'@next/swc-darwin-x64@15.3.4':
'@next/swc-darwin-x64@15.4.1':
optional: true
'@next/swc-linux-arm64-gnu@15.3.4':
'@next/swc-linux-arm64-gnu@15.4.1':
optional: true
'@next/swc-linux-arm64-musl@15.3.4':
'@next/swc-linux-arm64-musl@15.4.1':
optional: true
'@next/swc-linux-x64-gnu@15.3.4':
'@next/swc-linux-x64-gnu@15.4.1':
optional: true
'@next/swc-linux-x64-musl@15.3.4':
'@next/swc-linux-x64-musl@15.4.1':
optional: true
'@next/swc-win32-arm64-msvc@15.3.4':
'@next/swc-win32-arm64-msvc@15.4.1':
optional: true
'@next/swc-win32-x64-msvc@15.3.4':
'@next/swc-win32-x64-msvc@15.4.1':
optional: true
'@nodelib/fs.scandir@2.1.5':
@ -8117,51 +8122,51 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
'@prisma/adapter-pg@6.11.0':
'@prisma/adapter-pg@6.12.0':
dependencies:
'@prisma/driver-adapter-utils': 6.11.0
'@prisma/driver-adapter-utils': 6.12.0
pg: 8.16.3
postgres-array: 3.0.4
transitivePeerDependencies:
- pg-native
'@prisma/client@6.11.0(prisma@6.11.0(typescript@5.8.3))(typescript@5.8.3)':
'@prisma/client@6.12.0(prisma@6.12.0(typescript@5.8.3))(typescript@5.8.3)':
optionalDependencies:
prisma: 6.11.0(typescript@5.8.3)
prisma: 6.12.0(typescript@5.8.3)
typescript: 5.8.3
'@prisma/config@6.11.0':
'@prisma/config@6.12.0':
dependencies:
jiti: 2.4.2
'@prisma/debug@6.11.0': {}
'@prisma/debug@6.12.0': {}
'@prisma/driver-adapter-utils@6.11.0':
'@prisma/driver-adapter-utils@6.12.0':
dependencies:
'@prisma/debug': 6.11.0
'@prisma/debug': 6.12.0
'@prisma/engines-version@6.11.0-18.9c30299f5a0ea26a96790e13f796dc6094db3173': {}
'@prisma/engines-version@6.12.0-15.8047c96bbd92db98a2abc7c9323ce77c02c89dbc': {}
'@prisma/engines@6.11.0':
'@prisma/engines@6.12.0':
dependencies:
'@prisma/debug': 6.11.0
'@prisma/engines-version': 6.11.0-18.9c30299f5a0ea26a96790e13f796dc6094db3173
'@prisma/fetch-engine': 6.11.0
'@prisma/get-platform': 6.11.0
'@prisma/debug': 6.12.0
'@prisma/engines-version': 6.12.0-15.8047c96bbd92db98a2abc7c9323ce77c02c89dbc
'@prisma/fetch-engine': 6.12.0
'@prisma/get-platform': 6.12.0
'@prisma/extension-read-replicas@0.4.1(@prisma/client@6.11.0(prisma@6.11.0(typescript@5.8.3))(typescript@5.8.3))':
'@prisma/extension-read-replicas@0.4.1(@prisma/client@6.12.0(prisma@6.12.0(typescript@5.8.3))(typescript@5.8.3))':
dependencies:
'@prisma/client': 6.11.0(prisma@6.11.0(typescript@5.8.3))(typescript@5.8.3)
'@prisma/client': 6.12.0(prisma@6.12.0(typescript@5.8.3))(typescript@5.8.3)
'@prisma/fetch-engine@6.11.0':
'@prisma/fetch-engine@6.12.0':
dependencies:
'@prisma/debug': 6.11.0
'@prisma/engines-version': 6.11.0-18.9c30299f5a0ea26a96790e13f796dc6094db3173
'@prisma/get-platform': 6.11.0
'@prisma/debug': 6.12.0
'@prisma/engines-version': 6.12.0-15.8047c96bbd92db98a2abc7c9323ce77c02c89dbc
'@prisma/get-platform': 6.12.0
'@prisma/get-platform@6.11.0':
'@prisma/get-platform@6.12.0':
dependencies:
'@prisma/debug': 6.11.0
'@prisma/debug': 6.12.0
'@react-aria/autocomplete@3.0.0-beta.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
@ -9443,8 +9448,6 @@ snapshots:
transitivePeerDependencies:
- typescript
'@swc/counter@0.1.3': {}
'@swc/helpers@0.5.15':
dependencies:
tslib: 2.8.1
@ -9736,7 +9739,7 @@ snapshots:
'@typescript-eslint/types': 8.34.1
eslint-visitor-keys: 4.2.1
'@umami/react-zen@0.147.0(@babel/core@7.27.1)(@types/react@19.1.8)(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/react-zen@0.148.0(@babel/core@7.27.1)(@types/react@19.1.8)(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:
'@fontsource/jetbrains-mono': 5.2.6
'@internationalized/date': 3.8.2
@ -9746,7 +9749,7 @@ snapshots:
glob: 10.4.5
highlight.js: 11.11.1
lucide-react: 0.511.0(react@19.1.0)
next: 15.3.4(@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)
next: 15.4.1(@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-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)
@ -10200,10 +10203,6 @@ snapshots:
base64-js: 1.5.1
ieee754: 1.2.1
busboy@1.6.0:
dependencies:
streamsearch: 1.1.0
cachedir@2.4.0: {}
call-bind-apply-helpers@1.0.2:
@ -10251,8 +10250,6 @@ snapshots:
caniuse-lite@1.0.30001718: {}
caniuse-lite@1.0.30001726: {}
caniuse-lite@1.0.30001727: {}
caseless@0.12.0: {}
@ -12774,28 +12771,26 @@ snapshots:
natural-compare@1.4.0: {}
next@15.3.4(@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):
next@15.4.1(@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.4
'@swc/counter': 0.1.3
'@next/env': 15.4.1
'@swc/helpers': 0.5.15
busboy: 1.6.0
caniuse-lite: 1.0.30001726
caniuse-lite: 1.0.30001727
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.4
'@next/swc-darwin-x64': 15.3.4
'@next/swc-linux-arm64-gnu': 15.3.4
'@next/swc-linux-arm64-musl': 15.3.4
'@next/swc-linux-x64-gnu': 15.3.4
'@next/swc-linux-x64-musl': 15.3.4
'@next/swc-win32-arm64-msvc': 15.3.4
'@next/swc-win32-x64-msvc': 15.3.4
'@next/swc-darwin-arm64': 15.4.1
'@next/swc-darwin-x64': 15.4.1
'@next/swc-linux-arm64-gnu': 15.4.1
'@next/swc-linux-arm64-musl': 15.4.1
'@next/swc-linux-x64-gnu': 15.4.1
'@next/swc-linux-x64-musl': 15.4.1
'@next/swc-win32-arm64-msvc': 15.4.1
'@next/swc-win32-x64-msvc': 15.4.1
babel-plugin-react-compiler: 19.1.0-rc.2
sharp: 0.34.2
sharp: 0.34.3
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
@ -13520,10 +13515,10 @@ snapshots:
ansi-styles: 5.2.0
react-is: 18.3.1
prisma@6.11.0(typescript@5.8.3):
prisma@6.12.0(typescript@5.8.3):
dependencies:
'@prisma/config': 6.11.0
'@prisma/engines': 6.11.0
'@prisma/config': 6.12.0
'@prisma/engines': 6.12.0
optionalDependencies:
typescript: 5.8.3
@ -14040,33 +14035,34 @@ snapshots:
es-errors: 1.3.0
es-object-atoms: 1.1.1
sharp@0.34.2:
sharp@0.34.3:
dependencies:
color: 4.2.3
detect-libc: 2.0.4
semver: 7.7.2
optionalDependencies:
'@img/sharp-darwin-arm64': 0.34.2
'@img/sharp-darwin-x64': 0.34.2
'@img/sharp-libvips-darwin-arm64': 1.1.0
'@img/sharp-libvips-darwin-x64': 1.1.0
'@img/sharp-libvips-linux-arm': 1.1.0
'@img/sharp-libvips-linux-arm64': 1.1.0
'@img/sharp-libvips-linux-ppc64': 1.1.0
'@img/sharp-libvips-linux-s390x': 1.1.0
'@img/sharp-libvips-linux-x64': 1.1.0
'@img/sharp-libvips-linuxmusl-arm64': 1.1.0
'@img/sharp-libvips-linuxmusl-x64': 1.1.0
'@img/sharp-linux-arm': 0.34.2
'@img/sharp-linux-arm64': 0.34.2
'@img/sharp-linux-s390x': 0.34.2
'@img/sharp-linux-x64': 0.34.2
'@img/sharp-linuxmusl-arm64': 0.34.2
'@img/sharp-linuxmusl-x64': 0.34.2
'@img/sharp-wasm32': 0.34.2
'@img/sharp-win32-arm64': 0.34.2
'@img/sharp-win32-ia32': 0.34.2
'@img/sharp-win32-x64': 0.34.2
'@img/sharp-darwin-arm64': 0.34.3
'@img/sharp-darwin-x64': 0.34.3
'@img/sharp-libvips-darwin-arm64': 1.2.0
'@img/sharp-libvips-darwin-x64': 1.2.0
'@img/sharp-libvips-linux-arm': 1.2.0
'@img/sharp-libvips-linux-arm64': 1.2.0
'@img/sharp-libvips-linux-ppc64': 1.2.0
'@img/sharp-libvips-linux-s390x': 1.2.0
'@img/sharp-libvips-linux-x64': 1.2.0
'@img/sharp-libvips-linuxmusl-arm64': 1.2.0
'@img/sharp-libvips-linuxmusl-x64': 1.2.0
'@img/sharp-linux-arm': 0.34.3
'@img/sharp-linux-arm64': 0.34.3
'@img/sharp-linux-ppc64': 0.34.3
'@img/sharp-linux-s390x': 0.34.3
'@img/sharp-linux-x64': 0.34.3
'@img/sharp-linuxmusl-arm64': 0.34.3
'@img/sharp-linuxmusl-x64': 0.34.3
'@img/sharp-wasm32': 0.34.3
'@img/sharp-win32-arm64': 0.34.3
'@img/sharp-win32-ia32': 0.34.3
'@img/sharp-win32-x64': 0.34.3
optional: true
shebang-command@1.2.0:
@ -14204,8 +14200,6 @@ snapshots:
dependencies:
escape-string-regexp: 2.0.0
streamsearch@1.1.0: {}
string-argv@0.3.2: {}
string-hash@1.1.3: {}

View file

@ -1,4 +1,3 @@
import { ReactNode } from 'react';
import Link from 'next/link';
import {
Sidebar,
@ -18,35 +17,13 @@ import {
Settings,
LockKeyhole,
PanelLeft,
Eye,
Lightning,
User,
Clock,
Target,
Funnel,
Path,
Magnet,
Tag,
Money,
Network,
Arrow,
Sheet,
} from '@/components/icons';
import { useMessages, useNavigation, useGlobalState } from '@/components/hooks';
import { LinkButton } from '@/components/common/LinkButton';
type NavLink = {
id: string;
label: string;
path: string;
icon: ReactNode;
};
export function SideNav(props: any) {
const { formatMessage, labels } = useMessages();
const { websiteId, pathname, renderUrl } = useNavigation();
const { pathname, renderUrl } = useNavigation();
const [isCollapsed, setCollapsed] = useGlobalState('sidenav-collapsed');
const isWebsite = websiteId && !pathname.includes('/settings');
const links = [
{
@ -75,81 +52,6 @@ export function SideNav(props: any) {
},
];
const websiteLinks = [
{
id: 'overview',
label: formatMessage(labels.overview),
icon: <Eye />,
path: '/',
},
{
id: 'events',
label: formatMessage(labels.events),
icon: <Lightning />,
path: '/events',
},
{
id: 'sessions',
label: formatMessage(labels.sessions),
icon: <User />,
path: '/sessions',
},
{
id: 'realtime',
label: formatMessage(labels.realtime),
icon: <Clock />,
path: '/realtime',
},
{
id: 'breakdown',
label: formatMessage(labels.breakdown),
icon: <Sheet />,
path: '/breakdown',
},
{
id: 'goals',
label: formatMessage(labels.goals),
icon: <Target />,
path: '/goals',
},
{
id: 'funnel',
label: formatMessage(labels.funnels),
icon: <Funnel />,
path: '/funnels',
},
{
id: 'journeys',
label: formatMessage(labels.journeys),
icon: <Path />,
path: '/journeys',
},
{
id: 'retention',
label: formatMessage(labels.retention),
icon: <Magnet />,
path: '/retention',
},
{
id: 'utm',
label: formatMessage(labels.utm),
icon: <Tag />,
path: '/utm',
},
{
id: 'revenue',
label: formatMessage(labels.revenue),
icon: <Money />,
path: '/revenue',
},
{
id: 'attribution',
label: formatMessage(labels.attribution),
icon: <Network />,
path: '/attribution',
},
];
const bottomLinks = [
{
id: 'settings',
@ -171,22 +73,22 @@ export function SideNav(props: any) {
<SidebarHeader label="umami" icon={<Logo />} />
</SidebarSection>
<SidebarSection flexGrow={1}>
{!isWebsite && <NavItems items={links} params={false} />}
{isWebsite && (
<>
<Row>
<LinkButton href={renderUrl('/websites', false)} variant="outline">
<Icon rotate={180}>
<Arrow />
</Icon>
</LinkButton>
</Row>
<NavItems items={websiteLinks} prefix={`/websites/${websiteId}`} />
</>
)}
{links.map(({ id, path, label, icon }) => {
return (
<Link key={id} href={renderUrl(path, false)} role="button">
<SidebarItem label={label} icon={icon} isSelected={pathname.endsWith(path)} />
</Link>
);
})}
</SidebarSection>
<SidebarSection>
{!isWebsite && <NavItems items={bottomLinks} params={false} />}
{bottomLinks.map(({ id, path, label, icon }) => {
return (
<Link key={id} href={path} role="button">
<SidebarItem label={label} icon={icon} isSelected={pathname.startsWith(path)} />
</Link>
);
})}
</SidebarSection>
<SidebarSection>
<Row>
@ -200,26 +102,3 @@ export function SideNav(props: any) {
</Sidebar>
);
}
const NavItems = ({
items,
prefix = '',
params,
}: {
items: NavLink[];
prefix?: string;
params?: Record<string, string | number> | false;
}) => {
const { renderUrl, pathname, websiteId } = useNavigation();
return items.map(({ id, path, label, icon }) => {
const isSelected = websiteId
? (path === '/' && pathname.endsWith(websiteId)) || pathname.endsWith(path)
: pathname.startsWith(path);
return (
<Link key={id} href={renderUrl(`${prefix}${path}`, params)} role="button">
<SidebarItem label={label} icon={icon} isSelected={isSelected} />
</Link>
);
});
};

View file

@ -40,7 +40,7 @@ export function AdminLayout({ children }: { children: ReactNode }) {
<PageBody>
<Column gap="6">
<PageHeader title={formatMessage(labels.admin)} />
<Grid columns="160px 1fr" gap>
<Grid columns="200px 1fr" gap>
<Column>
<SideMenu items={items} selectedKey={value} />
</Column>

View file

@ -37,7 +37,7 @@ export function SettingsLayout({ children }: { children: ReactNode }) {
<PageBody>
<Column gap="6">
<PageHeader title={formatMessage(labels.settings)} />
<Grid columns="160px 1fr" gap>
<Grid columns="200px 1fr" gap>
<Column>
<SideMenu items={items} selectedKey={value} />
</Column>

View file

@ -7,6 +7,7 @@ import { Panel } from '@/components/common/Panel';
import { Breakdown } from './Breakdown';
import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls';
import { FieldSelectForm } from '@/app/(main)/websites/[websiteId]/(reports)/breakdown/FieldSelectForm';
import { SectionHeader } from '@/components/common/SectionHeader';
export function BreakdownPage({ websiteId }: { websiteId: string }) {
const {
@ -17,7 +18,9 @@ export function BreakdownPage({ websiteId }: { websiteId: string }) {
return (
<Column gap>
<WebsiteControls websiteId={websiteId} />
<FieldsButton value={fields} onChange={setFields} />
<SectionHeader>
<FieldsButton value={fields} onChange={setFields} />
</SectionHeader>
<Panel height="900px" overflow="auto" allowFullscreen>
<Breakdown
websiteId={websiteId}
@ -36,7 +39,7 @@ const FieldsButton = ({ value, onChange }) => {
return (
<Box>
<DialogTrigger>
<Button>
<Button variant="primary">
<Icon>
<ListCheck />
</Icon>

View file

@ -23,7 +23,7 @@ export function UTM({ websiteId, startDate, endDate }: UTMProps) {
});
return (
<LoadingPanel data={data} isLoading={isLoading} error={error}>
<LoadingPanel data={data} isLoading={isLoading} error={error} minHeight="300px">
{data && (
<Column gap>
{UTM_PARAMS.map(param => {

View file

@ -12,7 +12,7 @@ export function WebsiteHeader() {
const website = useWebsite();
return (
<PageHeader title={website.name} icon={<Favicon domain={website.domain} />} showBorder={false}>
<PageHeader title={website.name} icon={<Favicon domain={website.domain} />}>
<Row alignItems="center" gap="6">
<ActiveUsers websiteId={website.id} />
<Row alignItems="center" gap>

View file

@ -1,17 +1,25 @@
'use client';
import { ReactNode } from 'react';
import { Column } from '@umami/react-zen';
import { Column, Grid } from '@umami/react-zen';
import { WebsiteProvider } from './WebsiteProvider';
import { PageBody } from '@/components/common/PageBody';
import { WebsiteHeader } from './WebsiteHeader';
import { WebsiteNav } from '@/app/(main)/websites/[websiteId]/WebsiteNav';
export function WebsiteLayout({ websiteId, children }: { websiteId: string; children: ReactNode }) {
return (
<WebsiteProvider websiteId={websiteId}>
<PageBody>
<WebsiteHeader />
<Column>{children}</Column>
</PageBody>
<Grid columns="200px 1fr" gap width="100%">
<Column padding>
<WebsiteNav websiteId={websiteId} />
</Column>
<PageBody>
<Column gap>
<WebsiteHeader />
<Column>{children}</Column>
</Column>
</PageBody>
</Grid>
</WebsiteProvider>
);
}

View file

@ -1,10 +1,10 @@
import { Icon, Text, Row, NavMenu, NavMenuItem } from '@umami/react-zen';
import { Icon, Text, Row, NavMenu, NavMenuItem, NavMenuGroup } from '@umami/react-zen';
import {
Eye,
Lightning,
User,
Clock,
Lightbulb,
Sheet,
Target,
Funnel,
Path,
@ -22,95 +22,123 @@ export function WebsiteNav({ websiteId }: { websiteId: string }) {
const links = [
{
id: 'overview',
label: formatMessage(labels.overview),
icon: <Eye />,
path: '',
label: formatMessage(labels.core),
items: [
{
id: 'overview',
label: formatMessage(labels.overview),
icon: <Eye />,
path: '',
},
{
id: 'events',
label: formatMessage(labels.events),
icon: <Lightning />,
path: '/events',
},
{
id: 'sessions',
label: formatMessage(labels.sessions),
icon: <User />,
path: '/sessions',
},
{
id: 'realtime',
label: formatMessage(labels.realtime),
icon: <Clock />,
path: '/realtime',
},
],
},
{
id: 'events',
label: formatMessage(labels.events),
icon: <Lightning />,
path: '/events',
label: formatMessage(labels.behavior),
items: [
{
id: 'goals',
label: formatMessage(labels.goals),
icon: <Target />,
path: '/goals',
},
{
id: 'funnel',
label: formatMessage(labels.funnels),
icon: <Funnel />,
path: '/funnels',
},
{
id: 'journeys',
label: formatMessage(labels.journeys),
icon: <Path />,
path: '/journeys',
},
{
id: 'retention',
label: formatMessage(labels.retention),
icon: <Magnet />,
path: '/retention',
},
],
},
{
id: 'sessions',
label: formatMessage(labels.sessions),
icon: <User />,
path: '/sessions',
label: formatMessage(labels.segments),
items: [
{
id: 'breakdown',
label: formatMessage(labels.breakdown),
icon: <Sheet />,
path: '/breakdown',
},
],
},
{
id: 'realtime',
label: formatMessage(labels.realtime),
icon: <Clock />,
path: '/realtime',
},
{
id: 'insights',
label: formatMessage(labels.insights),
icon: <Lightbulb />,
path: '/insights',
},
{
id: 'goals',
label: formatMessage(labels.goals),
icon: <Target />,
path: '/goals',
},
{
id: 'funnel',
label: formatMessage(labels.funnels),
icon: <Funnel />,
path: '/funnels',
},
{
id: 'journeys',
label: formatMessage(labels.journeys),
icon: <Path />,
path: '/journeys',
},
{
id: 'retention',
label: formatMessage(labels.retention),
icon: <Magnet />,
path: '/retention',
},
{
id: 'utm',
label: formatMessage(labels.utm),
icon: <Tag />,
path: '/utm',
},
{
id: 'revenue',
label: formatMessage(labels.revenue),
icon: <Money />,
path: '/revenue',
},
{
id: 'attribution',
label: formatMessage(labels.attribution),
icon: <Network />,
path: '/attribution',
label: formatMessage(labels.growth),
items: [
{
id: 'utm',
label: formatMessage(labels.utm),
icon: <Tag />,
path: '/utm',
},
{
id: 'revenue',
label: formatMessage(labels.revenue),
icon: <Money />,
path: '/revenue',
},
{
id: 'attribution',
label: formatMessage(labels.attribution),
icon: <Network />,
path: '/attribution',
},
],
},
];
const selected = links.find(({ path }) => path && pathname.endsWith(path))?.id || 'overview';
const selected =
links.flatMap(e => e.items).find(({ path }) => path && pathname.endsWith(path))?.id ||
'overview';
return (
<NavMenu highlightColor="3">
{links.map(({ id, label, icon, path }) => {
const isSelected = selected === id;
<NavMenu highlightColor="3" style={{ position: 'sticky', top: 'var(--spacing-2)' }}>
{links.map(({ label, items }) => {
return (
<Link key={id} href={renderUrl(`/websites/${websiteId}${path}`)}>
<NavMenuItem isSelected={isSelected}>
<Row alignItems="center" gap>
<Icon>{icon}</Icon>
<Text>{label}</Text>
</Row>
</NavMenuItem>
</Link>
<NavMenuGroup title={label} key={label} gap="2">
{items.map(({ id, label, icon, path }) => {
const isSelected = selected === id;
return (
<Link key={id} href={renderUrl(`/websites/${websiteId}${path}`)}>
<NavMenuItem isSelected={isSelected}>
<Row alignItems="center" gap>
<Icon>{icon}</Icon>
<Text>{label}</Text>
</Row>
</NavMenuItem>
</Link>
);
})}
</NavMenuGroup>
);
})}
</NavMenu>

View file

@ -1,14 +1,5 @@
import { useMemo, useState } from 'react';
import {
DataColumn,
DataTable,
Row,
Loading,
Column,
ToggleGroup,
ToggleGroupItem,
Text,
} from '@umami/react-zen';
import { Select, ListItem, Grid } from '@umami/react-zen';
import {
useEventDataPropertiesQuery,
useEventDataValuesQuery,
@ -22,11 +13,69 @@ import { ListTable } from '@/components/metrics/ListTable';
export function EventProperties({ websiteId }: { websiteId: string }) {
const [propertyName, setPropertyName] = useState('');
const [eventName, setEventName] = useState('');
const [propertyView, setPropertyView] = useState('table');
const { formatMessage, labels } = useMessages();
const { data, isLoading, isFetching, error } = useEventDataPropertiesQuery(websiteId);
const { data: values } = useEventDataValuesQuery(websiteId, eventName, propertyName);
const events: string[] = data
? data.reduce((arr: string | any[], e: { eventName: any }) => {
return !arr.includes(e.eventName) ? arr.concat(e.eventName) : arr;
}, [])
: [];
const properties: string[] = eventName
? data?.filter(e => e.eventName === eventName).map(e => e.propertyName)
: [];
return (
<LoadingPanel
isLoading={isLoading}
isFetching={isFetching}
data={data}
error={error}
minHeight="300px"
gap="6"
>
<Grid columns="repeat(auto-fill, minmax(300px, 1fr))" gap>
<Select
label={formatMessage(labels.event)}
value={eventName}
onChange={setEventName}
placeholder=""
>
{events?.map(p => (
<ListItem key={p} id={p}>
{p}
</ListItem>
))}
</Select>
<Select
label={formatMessage(labels.property)}
value={propertyName}
onChange={setPropertyName}
isDisabled={!eventName}
placeholder=""
>
{properties?.map(p => (
<ListItem key={p} id={p}>
{p}
</ListItem>
))}
</Select>
</Grid>
{propertyName && (
<EventValues websiteId={websiteId} eventName={eventName} propertyName={propertyName} />
)}
</LoadingPanel>
);
}
const EventValues = ({ websiteId, eventName, propertyName }) => {
const {
data: values,
isLoading,
isFetching,
error,
} = useEventDataValuesQuery(websiteId, eventName, propertyName);
const propertySum = useMemo(() => {
return values?.reduce((sum, { total }) => sum + total, 0) ?? 0;
@ -55,43 +104,19 @@ export function EventProperties({ websiteId }: { websiteId: string }) {
}));
}, [propertyName, values, propertySum]);
const handleRowClick = row => {
setEventName(row.eventName);
setPropertyName(row.propertyName);
};
return (
<LoadingPanel isLoading={isLoading} isFetching={isFetching} data={data} error={error}>
<Column>
<DataTable data={data}>
<DataColumn id="eventName" label={formatMessage(labels.name)}>
{(row: any) => <Row onClick={() => handleRowClick(row)}>{row.eventName}</Row>}
</DataColumn>
<DataColumn id="propertyName" label={formatMessage(labels.property)}>
{(row: any) => <Row onClick={() => handleRowClick(row)}>{row.propertyName}</Row>}
</DataColumn>
<DataColumn id="total" label={formatMessage(labels.count)} align="end" />
</DataTable>
{propertyName && (
<Column>
<Row gap justifyContent="space-between">
<Text>{`${eventName}: ${propertyName}`}</Text>
<ToggleGroup value={[propertyView]} onChange={value => setPropertyView(value[0])}>
<ToggleGroupItem id="table">{formatMessage(labels.table)}</ToggleGroupItem>
<ToggleGroupItem id="chart">{formatMessage(labels.chart)}</ToggleGroupItem>
</ToggleGroup>
</Row>
{!values ? (
<Loading icon="dots" />
) : propertyView === 'table' ? (
<ListTable data={tableData} />
) : (
<PieChart key={propertyName + eventName} type="doughnut" chartData={chartData} />
)}
</Column>
)}
</Column>
<LoadingPanel
isLoading={isLoading}
isFetching={isFetching}
data={values}
error={error}
minHeight="300px"
gap="6"
>
<Grid columns="1fr 1fr" gap>
{values && <ListTable title={propertyName} data={tableData} />}
<PieChart key={propertyName + eventName} type="doughnut" chartData={chartData} />
</Grid>
</LoadingPanel>
);
}
};

View file

@ -5,7 +5,6 @@ import { useState } from 'react';
import { EventsDataTable } from './EventsDataTable';
import { Panel } from '@/components/common/Panel';
import { EventsChart } from '@/components/metrics/EventsChart';
import { GridRow } from '@/components/common/GridRow';
import { useMessages } from '@/components/hooks';
import { EventProperties } from './EventProperties';
import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls';
@ -22,29 +21,28 @@ export function EventsPage({ websiteId }) {
return (
<Column gap="3">
<WebsiteControls websiteId={websiteId} />
<GridRow layout="two-one">
<Panel gridColumn="span 2">
<EventsChart websiteId={websiteId} focusLabel={label} />
</Panel>
<Panel>
<EventsTable
websiteId={websiteId}
type="event"
title={formatMessage(labels.events)}
metric={formatMessage(labels.actions)}
onLabelClick={handleLabelClick}
/>
</Panel>
</GridRow>
<Panel>
<Tabs selectedKey={tab} onSelectionChange={(value: any) => setTab(value)}>
<TabList>
<Tab id="activity">{formatMessage(labels.activity)}</Tab>
<Tab id="chart">{formatMessage(labels.chart)}</Tab>
<Tab id="properties">{formatMessage(labels.properties)}</Tab>
</TabList>
<TabPanel id="activity">
<EventsDataTable websiteId={websiteId} />
</TabPanel>
<TabPanel id="chart">
<Column gap="6">
<EventsChart websiteId={websiteId} focusLabel={label} />
<EventsTable
websiteId={websiteId}
type="event"
title={formatMessage(labels.events)}
metric={formatMessage(labels.actions)}
onLabelClick={handleLabelClick}
/>
</Column>
</TabPanel>
<TabPanel id="properties">
<EventProperties websiteId={websiteId} />
</TabPanel>

View file

@ -1,51 +1,93 @@
import { Grid, DataColumn, DataTable } from '@umami/react-zen';
import { useMemo, useState } from 'react';
import { Select, ListItem, Grid } from '@umami/react-zen';
import {
useMessages,
useSessionDataPropertiesQuery,
useSessionDataValuesQuery,
useMessages,
} from '@/components/hooks';
import { LoadingPanel } from '@/components/common/LoadingPanel';
import { PieChart } from '@/components/charts/PieChart';
import { useState } from 'react';
import { CHART_COLORS } from '@/lib/constants';
import { ListTable } from '@/components/metrics/ListTable';
export function SessionProperties({ websiteId }: { websiteId: string }) {
const [propertyName, setPropertyName] = useState('');
const { formatMessage, labels } = useMessages();
const { data, isLoading, isFetching, error } = useSessionDataPropertiesQuery(websiteId);
const { data: values } = useSessionDataValuesQuery(websiteId, propertyName);
const chartData =
propertyName && values
? {
labels: values.map(({ value }) => value),
datasets: [
{
data: values.map(({ total }) => total),
backgroundColor: CHART_COLORS,
borderWidth: 0,
},
],
}
: null;
const properties: string[] = data?.map(e => e.propertyName);
return (
<LoadingPanel data={data} isLoading={isLoading} isFetching={isFetching} error={error}>
<Grid>
<DataTable data={data}>
<DataColumn id="propertyName" label={formatMessage(labels.property)}>
{(row: any) => (
<div onClick={() => setPropertyName(row.propertyName)}>{row.propertyName}</div>
)}
</DataColumn>
<DataColumn id="total" label={formatMessage(labels.count)} align="end" />
</DataTable>
{propertyName && (
<div>
<div>{propertyName}</div>
<PieChart key={propertyName} type="doughnut" chartData={chartData} />
</div>
)}
<LoadingPanel
isLoading={isLoading}
isFetching={isFetching}
data={data}
error={error}
minHeight="300px"
gap="6"
>
<Grid columns="repeat(auto-fill, minmax(300px, 1fr))" gap>
<Select
label={formatMessage(labels.event)}
value={propertyName}
onChange={setPropertyName}
placeholder=""
>
{properties?.map(p => (
<ListItem key={p} id={p}>
{p}
</ListItem>
))}
</Select>
</Grid>
{propertyName && <SessionValues websiteId={websiteId} propertyName={propertyName} />}
</LoadingPanel>
);
}
const SessionValues = ({ websiteId, propertyName }) => {
const { data, isLoading, isFetching, error } = useSessionDataValuesQuery(websiteId, propertyName);
const propertySum = useMemo(() => {
return data?.reduce((sum, { total }) => sum + total, 0) ?? 0;
}, [data]);
const chartData = useMemo(() => {
if (!propertyName || !data) return null;
return {
labels: data.map(({ value }) => value),
datasets: [
{
data: data.map(({ total }) => total),
backgroundColor: CHART_COLORS,
borderWidth: 0,
},
],
};
}, [propertyName, data]);
const tableData = useMemo(() => {
if (!propertyName || !data || propertySum === 0) return [];
return data.map(({ value, total }) => ({
x: value,
y: total,
z: 100 * (total / propertySum),
}));
}, [propertyName, data, propertySum]);
return (
<LoadingPanel
isLoading={isLoading}
isFetching={isFetching}
data={data}
error={error}
minHeight="300px"
gap="6"
>
<Grid columns="1fr 1fr" gap>
{data && <ListTable title={propertyName} data={tableData} />}
<PieChart key={propertyName} type="doughnut" chartData={chartData} />
</Grid>
</LoadingPanel>
);
};

View file

@ -1,6 +1,6 @@
'use client';
import { ReactNode } from 'react';
import { AlertBanner, Loading, Column } from '@umami/react-zen';
import { AlertBanner, Loading, Column, ColumnProps } from '@umami/react-zen';
import { useMessages } from '@/components/hooks';
export function PageBody({
@ -14,7 +14,7 @@ export function PageBody({
error?: unknown;
isLoading?: boolean;
children?: ReactNode;
}) {
} & ColumnProps) {
const { formatMessage, messages } = useMessages();
if (error) {

View file

@ -7,7 +7,6 @@ export function useFields() {
{ name: 'path', type: 'string', label: formatMessage(labels.path) },
// { name: 'cohort', type: 'string', label: formatMessage(labels.cohort) },
// { name: 'segment', type: 'string', label: formatMessage(labels.segment) },
{ name: 'url', type: 'string', label: formatMessage(labels.url) },
{ name: 'title', type: 'string', label: formatMessage(labels.pageTitle) },
{ name: 'referrer', type: 'string', label: formatMessage(labels.referrer) },
//{ name: 'query', type: 'string', label: formatMessage(labels.query) },

View file

@ -338,6 +338,9 @@ export const labels = defineMessages({
location: { id: 'label.location', defaultMessage: 'Location' },
chart: { id: 'label.chart', defaultMessage: 'Chart' },
table: { id: 'label.table', defaultMessage: 'Table' },
core: { id: 'label.core', defaultMessage: 'Core' },
behavior: { id: 'label.behavior', defaultMessage: 'Behavior' },
growth: { id: 'label.growth', defaultMessage: 'Growth' },
});
export const messages = defineMessages({

View file

@ -17,7 +17,7 @@ async function relationalQuery(
filters: QueryFilters & { propertyName?: string },
) {
const { rawQuery, parseFilters, getDateSQL } = prisma;
const { filterQuery, cohortQuery, queryParams } = parseFilters(filters);
const { filterQuery, cohortQuery, queryParams } = parseFilters({ ...filters, websiteId });
return rawQuery(
`
@ -49,7 +49,7 @@ async function clickhouseQuery(
filters: QueryFilters & { propertyName?: string },
): Promise<{ propertyName: string; dataType: number; propertyValue: string; total: number }[]> {
const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, cohortQuery, queryParams } = parseFilters(filters);
const { filterQuery, cohortQuery, queryParams } = parseFilters({ ...filters, websiteId });
return rawQuery(
`