Compare commits

..

No commits in common. "97ebdc1babc85e2e44c6db9cbb204c627e171194" and "3aa09572f5ff4ab5b9b8d4d5e2a19bf29eaba2b6" have entirely different histories.

16 changed files with 231 additions and 302 deletions

View file

@ -78,7 +78,7 @@
"@react-spring/web": "^10.0.3",
"@svgr/cli": "^8.1.0",
"@tanstack/react-query": "^5.90.5",
"@umami/react-zen": "^0.206.0",
"@umami/react-zen": "^0.203.0",
"@umami/redis-client": "^0.29.0",
"bcryptjs": "^3.0.2",
"chalk": "^5.6.2",

259
pnpm-lock.yaml generated
View file

@ -45,8 +45,8 @@ importers:
specifier: ^5.90.5
version: 5.90.5(react@19.2.0)
'@umami/react-zen':
specifier: ^0.206.0
version: 0.206.0(@babel/core@7.28.3)(@types/react@19.2.2)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@10.2.0)(use-sync-external-store@1.6.0(react@19.2.0))
specifier: ^0.203.0
version: 0.203.0(@babel/core@7.28.3)(@types/react@19.2.2)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@10.2.0)(use-sync-external-store@1.6.0(react@19.2.0))
'@umami/redis-client':
specifier: ^0.29.0
version: 0.29.0
@ -878,9 +878,6 @@ packages:
'@emnapi/runtime@1.5.0':
resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==}
'@emnapi/runtime@1.7.0':
resolution: {integrity: sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==}
'@emnapi/wasi-threads@1.0.4':
resolution: {integrity: sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==}
@ -1166,8 +1163,8 @@ packages:
cpu: [arm64]
os: [darwin]
'@img/sharp-darwin-arm64@0.34.5':
resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==}
'@img/sharp-darwin-arm64@0.34.4':
resolution: {integrity: sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [darwin]
@ -1178,8 +1175,8 @@ packages:
cpu: [x64]
os: [darwin]
'@img/sharp-darwin-x64@0.34.5':
resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==}
'@img/sharp-darwin-x64@0.34.4':
resolution: {integrity: sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [darwin]
@ -1189,8 +1186,8 @@ packages:
cpu: [arm64]
os: [darwin]
'@img/sharp-libvips-darwin-arm64@1.2.4':
resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==}
'@img/sharp-libvips-darwin-arm64@1.2.3':
resolution: {integrity: sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==}
cpu: [arm64]
os: [darwin]
@ -1199,8 +1196,8 @@ packages:
cpu: [x64]
os: [darwin]
'@img/sharp-libvips-darwin-x64@1.2.4':
resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==}
'@img/sharp-libvips-darwin-x64@1.2.3':
resolution: {integrity: sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==}
cpu: [x64]
os: [darwin]
@ -1209,8 +1206,8 @@ packages:
cpu: [arm64]
os: [linux]
'@img/sharp-libvips-linux-arm64@1.2.4':
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
'@img/sharp-libvips-linux-arm64@1.2.3':
resolution: {integrity: sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==}
cpu: [arm64]
os: [linux]
@ -1219,8 +1216,8 @@ packages:
cpu: [arm]
os: [linux]
'@img/sharp-libvips-linux-arm@1.2.4':
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
'@img/sharp-libvips-linux-arm@1.2.3':
resolution: {integrity: sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==}
cpu: [arm]
os: [linux]
@ -1229,23 +1226,18 @@ packages:
cpu: [ppc64]
os: [linux]
'@img/sharp-libvips-linux-ppc64@1.2.4':
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
'@img/sharp-libvips-linux-ppc64@1.2.3':
resolution: {integrity: sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==}
cpu: [ppc64]
os: [linux]
'@img/sharp-libvips-linux-riscv64@1.2.4':
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
cpu: [riscv64]
os: [linux]
'@img/sharp-libvips-linux-s390x@1.2.0':
resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==}
cpu: [s390x]
os: [linux]
'@img/sharp-libvips-linux-s390x@1.2.4':
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
'@img/sharp-libvips-linux-s390x@1.2.3':
resolution: {integrity: sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==}
cpu: [s390x]
os: [linux]
@ -1254,8 +1246,8 @@ packages:
cpu: [x64]
os: [linux]
'@img/sharp-libvips-linux-x64@1.2.4':
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
'@img/sharp-libvips-linux-x64@1.2.3':
resolution: {integrity: sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==}
cpu: [x64]
os: [linux]
@ -1264,8 +1256,8 @@ packages:
cpu: [arm64]
os: [linux]
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
'@img/sharp-libvips-linuxmusl-arm64@1.2.3':
resolution: {integrity: sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==}
cpu: [arm64]
os: [linux]
@ -1274,8 +1266,8 @@ packages:
cpu: [x64]
os: [linux]
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
'@img/sharp-libvips-linuxmusl-x64@1.2.3':
resolution: {integrity: sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==}
cpu: [x64]
os: [linux]
@ -1285,8 +1277,8 @@ packages:
cpu: [arm64]
os: [linux]
'@img/sharp-linux-arm64@0.34.5':
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
'@img/sharp-linux-arm64@0.34.4':
resolution: {integrity: sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
@ -1297,8 +1289,8 @@ packages:
cpu: [arm]
os: [linux]
'@img/sharp-linux-arm@0.34.5':
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
'@img/sharp-linux-arm@0.34.4':
resolution: {integrity: sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
@ -1309,26 +1301,20 @@ packages:
cpu: [ppc64]
os: [linux]
'@img/sharp-linux-ppc64@0.34.5':
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
'@img/sharp-linux-ppc64@0.34.4':
resolution: {integrity: sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ppc64]
os: [linux]
'@img/sharp-linux-riscv64@0.34.5':
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [riscv64]
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-s390x@0.34.5':
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
'@img/sharp-linux-s390x@0.34.4':
resolution: {integrity: sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
@ -1339,8 +1325,8 @@ packages:
cpu: [x64]
os: [linux]
'@img/sharp-linux-x64@0.34.5':
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
'@img/sharp-linux-x64@0.34.4':
resolution: {integrity: sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
@ -1351,8 +1337,8 @@ packages:
cpu: [arm64]
os: [linux]
'@img/sharp-linuxmusl-arm64@0.34.5':
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
'@img/sharp-linuxmusl-arm64@0.34.4':
resolution: {integrity: sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
@ -1363,8 +1349,8 @@ packages:
cpu: [x64]
os: [linux]
'@img/sharp-linuxmusl-x64@0.34.5':
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
'@img/sharp-linuxmusl-x64@0.34.4':
resolution: {integrity: sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
@ -1374,8 +1360,8 @@ packages:
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [wasm32]
'@img/sharp-wasm32@0.34.5':
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
'@img/sharp-wasm32@0.34.4':
resolution: {integrity: sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [wasm32]
@ -1385,8 +1371,8 @@ packages:
cpu: [arm64]
os: [win32]
'@img/sharp-win32-arm64@0.34.5':
resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==}
'@img/sharp-win32-arm64@0.34.4':
resolution: {integrity: sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [win32]
@ -1397,8 +1383,8 @@ packages:
cpu: [ia32]
os: [win32]
'@img/sharp-win32-ia32@0.34.5':
resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==}
'@img/sharp-win32-ia32@0.34.4':
resolution: {integrity: sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ia32]
os: [win32]
@ -1409,8 +1395,8 @@ packages:
cpu: [x64]
os: [win32]
'@img/sharp-win32-x64@0.34.5':
resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==}
'@img/sharp-win32-x64@0.34.4':
resolution: {integrity: sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [win32]
@ -2935,8 +2921,8 @@ packages:
resolution: {integrity: sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@umami/react-zen@0.206.0':
resolution: {integrity: sha512-9XM3Oj1akdyuwkMT1SldrJOyrMACP9TLJApZ/9ocmPuET4B7vpPxRoxv8OpEVlBaDw5nmlJfIvefsNMBLt1OQg==}
'@umami/react-zen@0.203.0':
resolution: {integrity: sha512-lgGUapA0zDbLu63GINaEPndIsT8ry85vE316AWU/EEH3qYDBNscetcBfZFr+DTD/c5eLKy9OxmMwIpbs7k+/UA==}
'@umami/redis-client@0.29.0':
resolution: {integrity: sha512-Jaqh++jskqDB7ny75pfC02OvKp1JTS4asGDsFrRL3qy8sxL3PAl9+/mybCJe4/6vWrXDJKqpgkSfUDJq2bFjyw==}
@ -3407,8 +3393,8 @@ packages:
caniuse-lite@1.0.30001741:
resolution: {integrity: sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==}
caniuse-lite@1.0.30001754:
resolution: {integrity: sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==}
caniuse-lite@1.0.30001751:
resolution: {integrity: sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==}
caseless@0.12.0:
resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
@ -6554,8 +6540,8 @@ packages:
peerDependencies:
react: '>=16.13.1'
react-hook-form@7.66.0:
resolution: {integrity: sha512-xXBqsWGKrY46ZqaHDo+ZUYiMUgi8suYu5kdrS20EG8KiL7VRQitEbNjm+UcrDYrNi1YLyfpmAeGjCZYXLT9YBw==}
react-hook-form@7.65.0:
resolution: {integrity: sha512-xtOzDz063WcXvGWaHgLNrNzlsdFgtUWcb32E6WFaGTd7kPZG3EeDusjdZfUsPwKCKVXy1ZlntifaHZ4l8pAsmw==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^16.8.0 || ^17 || ^18 || ^19
@ -6860,8 +6846,8 @@ packages:
resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
sharp@0.34.5:
resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
sharp@0.34.4:
resolution: {integrity: sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
shebang-command@1.2.0:
@ -8198,11 +8184,6 @@ snapshots:
tslib: 2.8.1
optional: true
'@emnapi/runtime@1.7.0':
dependencies:
tslib: 2.8.1
optional: true
'@emnapi/wasi-threads@1.0.4':
dependencies:
tslib: 2.8.1
@ -8462,9 +8443,9 @@ snapshots:
'@img/sharp-libvips-darwin-arm64': 1.2.0
optional: true
'@img/sharp-darwin-arm64@0.34.5':
'@img/sharp-darwin-arm64@0.34.4':
optionalDependencies:
'@img/sharp-libvips-darwin-arm64': 1.2.4
'@img/sharp-libvips-darwin-arm64': 1.2.3
optional: true
'@img/sharp-darwin-x64@0.34.3':
@ -8472,66 +8453,63 @@ snapshots:
'@img/sharp-libvips-darwin-x64': 1.2.0
optional: true
'@img/sharp-darwin-x64@0.34.5':
'@img/sharp-darwin-x64@0.34.4':
optionalDependencies:
'@img/sharp-libvips-darwin-x64': 1.2.4
'@img/sharp-libvips-darwin-x64': 1.2.3
optional: true
'@img/sharp-libvips-darwin-arm64@1.2.0':
optional: true
'@img/sharp-libvips-darwin-arm64@1.2.4':
'@img/sharp-libvips-darwin-arm64@1.2.3':
optional: true
'@img/sharp-libvips-darwin-x64@1.2.0':
optional: true
'@img/sharp-libvips-darwin-x64@1.2.4':
'@img/sharp-libvips-darwin-x64@1.2.3':
optional: true
'@img/sharp-libvips-linux-arm64@1.2.0':
optional: true
'@img/sharp-libvips-linux-arm64@1.2.4':
'@img/sharp-libvips-linux-arm64@1.2.3':
optional: true
'@img/sharp-libvips-linux-arm@1.2.0':
optional: true
'@img/sharp-libvips-linux-arm@1.2.4':
'@img/sharp-libvips-linux-arm@1.2.3':
optional: true
'@img/sharp-libvips-linux-ppc64@1.2.0':
optional: true
'@img/sharp-libvips-linux-ppc64@1.2.4':
optional: true
'@img/sharp-libvips-linux-riscv64@1.2.4':
'@img/sharp-libvips-linux-ppc64@1.2.3':
optional: true
'@img/sharp-libvips-linux-s390x@1.2.0':
optional: true
'@img/sharp-libvips-linux-s390x@1.2.4':
'@img/sharp-libvips-linux-s390x@1.2.3':
optional: true
'@img/sharp-libvips-linux-x64@1.2.0':
optional: true
'@img/sharp-libvips-linux-x64@1.2.4':
'@img/sharp-libvips-linux-x64@1.2.3':
optional: true
'@img/sharp-libvips-linuxmusl-arm64@1.2.0':
optional: true
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
'@img/sharp-libvips-linuxmusl-arm64@1.2.3':
optional: true
'@img/sharp-libvips-linuxmusl-x64@1.2.0':
optional: true
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
'@img/sharp-libvips-linuxmusl-x64@1.2.3':
optional: true
'@img/sharp-linux-arm64@0.34.3':
@ -8539,9 +8517,9 @@ snapshots:
'@img/sharp-libvips-linux-arm64': 1.2.0
optional: true
'@img/sharp-linux-arm64@0.34.5':
'@img/sharp-linux-arm64@0.34.4':
optionalDependencies:
'@img/sharp-libvips-linux-arm64': 1.2.4
'@img/sharp-libvips-linux-arm64': 1.2.3
optional: true
'@img/sharp-linux-arm@0.34.3':
@ -8549,9 +8527,9 @@ snapshots:
'@img/sharp-libvips-linux-arm': 1.2.0
optional: true
'@img/sharp-linux-arm@0.34.5':
'@img/sharp-linux-arm@0.34.4':
optionalDependencies:
'@img/sharp-libvips-linux-arm': 1.2.4
'@img/sharp-libvips-linux-arm': 1.2.3
optional: true
'@img/sharp-linux-ppc64@0.34.3':
@ -8559,14 +8537,9 @@ snapshots:
'@img/sharp-libvips-linux-ppc64': 1.2.0
optional: true
'@img/sharp-linux-ppc64@0.34.5':
'@img/sharp-linux-ppc64@0.34.4':
optionalDependencies:
'@img/sharp-libvips-linux-ppc64': 1.2.4
optional: true
'@img/sharp-linux-riscv64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-riscv64': 1.2.4
'@img/sharp-libvips-linux-ppc64': 1.2.3
optional: true
'@img/sharp-linux-s390x@0.34.3':
@ -8574,9 +8547,9 @@ snapshots:
'@img/sharp-libvips-linux-s390x': 1.2.0
optional: true
'@img/sharp-linux-s390x@0.34.5':
'@img/sharp-linux-s390x@0.34.4':
optionalDependencies:
'@img/sharp-libvips-linux-s390x': 1.2.4
'@img/sharp-libvips-linux-s390x': 1.2.3
optional: true
'@img/sharp-linux-x64@0.34.3':
@ -8584,9 +8557,9 @@ snapshots:
'@img/sharp-libvips-linux-x64': 1.2.0
optional: true
'@img/sharp-linux-x64@0.34.5':
'@img/sharp-linux-x64@0.34.4':
optionalDependencies:
'@img/sharp-libvips-linux-x64': 1.2.4
'@img/sharp-libvips-linux-x64': 1.2.3
optional: true
'@img/sharp-linuxmusl-arm64@0.34.3':
@ -8594,9 +8567,9 @@ snapshots:
'@img/sharp-libvips-linuxmusl-arm64': 1.2.0
optional: true
'@img/sharp-linuxmusl-arm64@0.34.5':
'@img/sharp-linuxmusl-arm64@0.34.4':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4
'@img/sharp-libvips-linuxmusl-arm64': 1.2.3
optional: true
'@img/sharp-linuxmusl-x64@0.34.3':
@ -8604,9 +8577,9 @@ snapshots:
'@img/sharp-libvips-linuxmusl-x64': 1.2.0
optional: true
'@img/sharp-linuxmusl-x64@0.34.5':
'@img/sharp-linuxmusl-x64@0.34.4':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-x64': 1.2.4
'@img/sharp-libvips-linuxmusl-x64': 1.2.3
optional: true
'@img/sharp-wasm32@0.34.3':
@ -8614,27 +8587,27 @@ snapshots:
'@emnapi/runtime': 1.5.0
optional: true
'@img/sharp-wasm32@0.34.5':
'@img/sharp-wasm32@0.34.4':
dependencies:
'@emnapi/runtime': 1.7.0
'@emnapi/runtime': 1.5.0
optional: true
'@img/sharp-win32-arm64@0.34.3':
optional: true
'@img/sharp-win32-arm64@0.34.5':
'@img/sharp-win32-arm64@0.34.4':
optional: true
'@img/sharp-win32-ia32@0.34.3':
optional: true
'@img/sharp-win32-ia32@0.34.5':
'@img/sharp-win32-ia32@0.34.4':
optional: true
'@img/sharp-win32-x64@0.34.3':
optional: true
'@img/sharp-win32-x64@0.34.5':
'@img/sharp-win32-x64@0.34.4':
optional: true
'@internationalized/date@3.10.0':
@ -10697,7 +10670,7 @@ snapshots:
'@typescript-eslint/types': 8.46.2
eslint-visitor-keys: 4.2.1
'@umami/react-zen@0.206.0(@babel/core@7.28.3)(@types/react@19.2.2)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@10.2.0)(use-sync-external-store@1.6.0(react@19.2.0))':
'@umami/react-zen@0.203.0(@babel/core@7.28.3)(@types/react@19.2.2)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@10.2.0)(use-sync-external-store@1.6.0(react@19.2.0))':
dependencies:
'@fontsource/jetbrains-mono': 5.2.8
'@internationalized/date': 3.10.0
@ -10711,7 +10684,7 @@ snapshots:
react: 19.2.0
react-aria-components: 1.13.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
react-dom: 19.2.0(react@19.2.0)
react-hook-form: 7.66.0(react@19.2.0)
react-hook-form: 7.65.0(react@19.2.0)
react-icons: 5.5.0(react@19.2.0)
thenby: 1.3.4
zustand: 5.0.8(@types/react@19.2.2)(immer@10.2.0)(react@19.2.0)(use-sync-external-store@1.6.0(react@19.2.0))
@ -11242,7 +11215,7 @@ snapshots:
caniuse-lite@1.0.30001741: {}
caniuse-lite@1.0.30001754: {}
caniuse-lite@1.0.30001751: {}
caseless@0.12.0: {}
@ -13929,7 +13902,7 @@ snapshots:
dependencies:
'@next/env': 15.5.6
'@swc/helpers': 0.5.15
caniuse-lite: 1.0.30001754
caniuse-lite: 1.0.30001751
postcss: 8.4.31
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
@ -13944,7 +13917,7 @@ snapshots:
'@next/swc-win32-arm64-msvc': 15.5.6
'@next/swc-win32-x64-msvc': 15.5.6
babel-plugin-react-compiler: 19.1.0-rc.2
sharp: 0.34.5
sharp: 0.34.4
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
@ -14866,7 +14839,7 @@ snapshots:
'@babel/runtime': 7.28.3
react: 19.2.0
react-hook-form@7.66.0(react@19.2.0):
react-hook-form@7.65.0(react@19.2.0):
dependencies:
react: 19.2.0
@ -15293,36 +15266,34 @@ snapshots:
'@img/sharp-win32-x64': 0.34.3
optional: true
sharp@0.34.5:
sharp@0.34.4:
dependencies:
'@img/colour': 1.0.0
detect-libc: 2.1.2
semver: 7.7.3
optionalDependencies:
'@img/sharp-darwin-arm64': 0.34.5
'@img/sharp-darwin-x64': 0.34.5
'@img/sharp-libvips-darwin-arm64': 1.2.4
'@img/sharp-libvips-darwin-x64': 1.2.4
'@img/sharp-libvips-linux-arm': 1.2.4
'@img/sharp-libvips-linux-arm64': 1.2.4
'@img/sharp-libvips-linux-ppc64': 1.2.4
'@img/sharp-libvips-linux-riscv64': 1.2.4
'@img/sharp-libvips-linux-s390x': 1.2.4
'@img/sharp-libvips-linux-x64': 1.2.4
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4
'@img/sharp-libvips-linuxmusl-x64': 1.2.4
'@img/sharp-linux-arm': 0.34.5
'@img/sharp-linux-arm64': 0.34.5
'@img/sharp-linux-ppc64': 0.34.5
'@img/sharp-linux-riscv64': 0.34.5
'@img/sharp-linux-s390x': 0.34.5
'@img/sharp-linux-x64': 0.34.5
'@img/sharp-linuxmusl-arm64': 0.34.5
'@img/sharp-linuxmusl-x64': 0.34.5
'@img/sharp-wasm32': 0.34.5
'@img/sharp-win32-arm64': 0.34.5
'@img/sharp-win32-ia32': 0.34.5
'@img/sharp-win32-x64': 0.34.5
'@img/sharp-darwin-arm64': 0.34.4
'@img/sharp-darwin-x64': 0.34.4
'@img/sharp-libvips-darwin-arm64': 1.2.3
'@img/sharp-libvips-darwin-x64': 1.2.3
'@img/sharp-libvips-linux-arm': 1.2.3
'@img/sharp-libvips-linux-arm64': 1.2.3
'@img/sharp-libvips-linux-ppc64': 1.2.3
'@img/sharp-libvips-linux-s390x': 1.2.3
'@img/sharp-libvips-linux-x64': 1.2.3
'@img/sharp-libvips-linuxmusl-arm64': 1.2.3
'@img/sharp-libvips-linuxmusl-x64': 1.2.3
'@img/sharp-linux-arm': 0.34.4
'@img/sharp-linux-arm64': 0.34.4
'@img/sharp-linux-ppc64': 0.34.4
'@img/sharp-linux-s390x': 0.34.4
'@img/sharp-linux-x64': 0.34.4
'@img/sharp-linuxmusl-arm64': 0.34.4
'@img/sharp-linuxmusl-x64': 0.34.4
'@img/sharp-wasm32': 0.34.4
'@img/sharp-win32-arm64': 0.34.4
'@img/sharp-win32-ia32': 0.34.4
'@img/sharp-win32-x64': 0.34.4
optional: true
shebang-command@1.2.0:

View file

@ -95,7 +95,7 @@ export function Attribution({
})}
</MetricsBar>
<SectionHeader title={formatMessage(labels.sources)} />
<Grid columns={{ xs: '1fr', md: '1fr 1fr' }} gap>
<Grid columns="1fr 1fr" gap>
<Panel>
<AttributionTable data={data?.['referrer']} title={formatMessage(labels.referrer)} />
</Panel>
@ -104,7 +104,7 @@ export function Attribution({
</Panel>
</Grid>
<SectionHeader title="UTM" />
<Grid columns={{ xs: '1fr', md: '1fr 1fr' }} gap>
<Grid columns="1fr 1fr" gap>
<Panel>
<AttributionTable data={data?.['utm_source']} title={formatMessage(labels.sources)} />
</Panel>

View file

@ -17,7 +17,7 @@ export function AttributionPage({ websiteId }: { websiteId: string }) {
return (
<Column gap="6">
<WebsiteControls websiteId={websiteId} />
<Grid columns={{ xs: '1fr', md: '1fr 1fr 1fr' }} gap>
<Grid columns="1fr 1fr 1fr" gap>
<Column>
<Select
label={formatMessage(labels.model)}

View file

@ -1,4 +1,4 @@
import { Text, DataTable, DataColumn, Column } from '@umami/react-zen';
import { Text, DataTable, DataColumn } from '@umami/react-zen';
import { useMessages, useResultQuery, useFormat, useFields } from '@/components/hooks';
import { LoadingPanel } from '@/components/common/LoadingPanel';
import { formatShortTime } from '@/lib/format';
@ -27,65 +27,43 @@ export function Breakdown({ websiteId, selectedFields = [], startDate, endDate }
return (
<LoadingPanel data={data} isLoading={isLoading} error={error}>
<Column overflow="auto" minHeight="0" height="100%">
<DataTable data={data} style={{ tableLayout: 'fixed' }}>
{selectedFields.map(field => {
return (
<DataColumn
key={field}
id={field}
label={fields.find(f => f.name === field)?.label}
width="minmax(120px, 1fr)"
>
{row => {
const value = formatValue(row[field], field);
return (
<Text truncate title={value}>
{value}
</Text>
);
}}
</DataColumn>
);
})}
<DataColumn
id="visitors"
label={formatMessage(labels.visitors)}
align="end"
width="120px"
>
{row => row?.['visitors']?.toLocaleString()}
</DataColumn>
<DataColumn id="visits" label={formatMessage(labels.visits)} align="end" width="120px">
{row => row?.['visits']?.toLocaleString()}
</DataColumn>
<DataColumn id="views" label={formatMessage(labels.views)} align="end" width="120px">
{row => row?.['views']?.toLocaleString()}
</DataColumn>
<DataColumn
id="bounceRate"
label={formatMessage(labels.bounceRate)}
align="end"
width="120px"
>
{row => {
const n = (Math.min(row?.['visits'], row?.['bounces']) / row?.['visits']) * 100;
return Math.round(+n) + '%';
}}
</DataColumn>
<DataColumn
id="visitDuration"
label={formatMessage(labels.visitDuration)}
align="end"
width="120px"
>
{row => {
const n = row?.['totaltime'] / row?.['visits'];
return `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`;
}}
</DataColumn>
</DataTable>
</Column>
<DataTable data={data}>
{selectedFields.map(field => {
return (
<DataColumn key={field} id={field} label={fields.find(f => f.name === field)?.label}>
{row => {
const value = formatValue(row[field], field);
return (
<Text truncate title={value}>
{value}
</Text>
);
}}
</DataColumn>
);
})}
<DataColumn id="visitors" label={formatMessage(labels.visitors)} align="end">
{row => row?.['visitors']?.toLocaleString()}
</DataColumn>
<DataColumn id="visits" label={formatMessage(labels.visits)} align="end">
{row => row?.['visits']?.toLocaleString()}
</DataColumn>
<DataColumn id="views" label={formatMessage(labels.views)} align="end">
{row => row?.['views']?.toLocaleString()}
</DataColumn>
<DataColumn id="bounceRate" label={formatMessage(labels.bounceRate)} align="end">
{row => {
const n = (Math.min(row?.['visits'], row?.['bounces']) / row?.['visits']) * 100;
return Math.round(+n) + '%';
}}
</DataColumn>
<DataColumn id="visitDuration" label={formatMessage(labels.visitDuration)} align="end">
{row => {
const n = row?.['totaltime'] / row?.['visits'];
return `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`;
}}
</DataColumn>
</DataTable>
</LoadingPanel>
);
}

View file

@ -2,7 +2,7 @@
import { FieldSelectForm } from '@/app/(main)/websites/[websiteId]/(reports)/breakdown/FieldSelectForm';
import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls';
import { Panel } from '@/components/common/Panel';
import { useDateRange, useMessages } from '@/components/hooks';
import { useDateRange, useMessages, useMobile } from '@/components/hooks';
import { ListCheck } from '@/components/icons';
import { DialogButton } from '@/components/input/DialogButton';
import { Column, Row } from '@umami/react-zen';
@ -14,10 +14,12 @@ export function BreakdownPage({ websiteId }: { websiteId: string }) {
dateRange: { startDate, endDate },
} = useDateRange();
const [fields, setFields] = useState(['path']);
const { isMobile } = useMobile();
return (
<Column gap>
<WebsiteControls websiteId={websiteId} />
<Row alignItems="center" justifyContent="flex-start">
<Row alignItems="center" justifyContent={isMobile ? 'flex-end' : 'flex-start'}>
<FieldsButton value={fields} onChange={setFields} />
</Row>
<Panel height="900px" overflow="auto" allowFullscreen>
@ -39,9 +41,8 @@ const FieldsButton = ({ value, onChange }) => {
<DialogButton
icon={<ListCheck />}
label={formatMessage(labels.fields)}
width="400px"
width="800px"
minHeight="300px"
variant="outline"
>
{({ close }) => {
return <FieldSelectForm selectedFields={value} onChange={onChange} onClose={close} />;

View file

@ -51,72 +51,51 @@ export function Retention({ websiteId, days = DAYS, startDate, endDate }: Retent
<LoadingPanel data={data} isLoading={isLoading} error={error}>
{data && (
<Panel allowFullscreen height="900px">
<Column
paddingY="6"
paddingX={{ xs: '3', md: '6' }}
position="absolute"
top="40px"
left="0"
right="0"
bottom="0"
>
<Column gap="1" overflow="auto">
<Grid
columns="120px repeat(10, 100px)"
alignItems="center"
gap="1"
height="50px"
width="max-content"
minWidth="100%"
autoFlow="column"
>
<Column>
<Text weight="bold" align="center">
{formatMessage(labels.cohort)}
<Column gap="1" width="100%" overflow="auto">
<Grid
columns="120px repeat(10, 100px)"
alignItems="center"
gap="1"
height="50px"
autoFlow="column"
>
<Column>
<Text weight="bold" align="center">
{formatMessage(labels.cohort)}
</Text>
</Column>
{days.map(n => (
<Column key={n}>
<Text weight="bold" align="center" wrap="nowrap">
{formatMessage(labels.day)} {n}
</Text>
</Column>
{days.map(n => (
<Column key={n}>
<Text weight="bold" align="center" wrap="nowrap">
{formatMessage(labels.day)} {n}
</Text>
))}
</Grid>
{rows.map(({ date, visitors, records }: any, rowIndex: number) => {
return (
<Grid key={rowIndex} columns="120px repeat(10, 100px)" gap="1" autoFlow="column">
<Column justifyContent="center" gap="1">
<Text weight="bold">{formatDate(date, 'PP', locale)}</Text>
<Row alignItems="center" gap>
<Icon>
<Users />
</Icon>
<Text>{formatLongNumber(visitors)}</Text>
</Row>
</Column>
))}
</Grid>
{rows.map(({ date, visitors, records }: any, rowIndex: number) => {
return (
<Grid
key={rowIndex}
columns="120px repeat(10, 100px)"
gap="1"
autoFlow="column"
width="max-content"
minWidth="100%"
>
<Column justifyContent="center" gap="1">
<Text weight="bold">{formatDate(date, 'PP', locale)}</Text>
<Row alignItems="center" gap>
<Icon>
<Users />
</Icon>
<Text>{formatLongNumber(visitors)}</Text>
</Row>
</Column>
{days.map(day => {
if (totalDays - rowIndex < day) {
return null;
}
const percentage = records.filter(a => a.day === day)[0]?.percentage;
return (
<Cell key={day}>
{percentage ? `${Number(percentage).toFixed(2)}%` : ''}
</Cell>
);
})}
</Grid>
);
})}
</Column>
{days.map(day => {
if (totalDays - rowIndex < day) {
return null;
}
const percentage = records.filter(a => a.day === day)[0]?.percentage;
return (
<Cell key={day}>{percentage ? `${Number(percentage).toFixed(2)}%` : ''}</Cell>
);
})}
</Grid>
);
})}
</Column>
</Panel>
)}

View file

@ -43,7 +43,7 @@ export function UTM({ websiteId, startDate, endDate }: UTMProps) {
return (
<Panel key={param}>
<Grid columns={{ xs: '1fr', md: '1fr 1fr' }} gap="6">
<Grid columns="1fr 1fr">
<Column>
<Heading>
<Text transform="capitalize">{param.replace(/^utm_/, '')}</Text>

View file

@ -34,7 +34,6 @@ export function ExpandedViewModal({
maxWidth: 1320,
width: '100vw',
height: isMobile ? '100dvh' : 'calc(100dvh - 40px)',
overflow: 'hidden',
}}
>
{({ close }) => {

View file

@ -23,10 +23,10 @@ export function WebsiteControls({
return (
<Column gap>
<Grid columns={{ xs: '1fr', md: 'auto 1fr' }} gap>
<Row alignItems="center" justifyContent="flex-start">
<Row alignItems="center" justifyContent="flex-end">
{allowFilter ? <WebsiteFilterButton websiteId={websiteId} /> : <div />}
</Row>
<Row alignItems="center" justifyContent={{ xs: 'flex-start', md: 'flex-end' }}>
<Row alignItems="center" justifyContent="flex-end">
{allowDateFilter && (
<WebsiteDateFilter websiteId={websiteId} allowCompare={allowCompare} />
)}

View file

@ -19,23 +19,17 @@ export function WebsiteExpandedView({
} = useNavigation();
return (
<Column height="100%" overflow="hidden" gap>
<Row id="expanded-mobile-menu-button" display={{ xs: 'flex', md: 'none' }}>
<Column gap>
<Row display={{ xs: 'flex', md: 'none' }}>
<MobileMenuButton>
{({ close }) => {
return (
<Column padding="3">
<WebsiteExpandedMenu excludedIds={excludedIds} onItemClick={close} />
</Column>
);
return <WebsiteExpandedMenu excludedIds={excludedIds} onItemClick={close} />;
}}
</MobileMenuButton>
</Row>
<Grid columns={{ xs: '1fr', md: 'auto 1fr' }} gap="6" overflow="hidden">
<Grid columns={{ xs: '1fr', md: 'auto 1fr' }} gap="6" height="100%" overflow="hidden">
<Column
id="metrics-expanded-menu"
display={{ xs: 'none', md: 'flex' }}
width="240px"
gap="6"
border="right"
paddingRight="3"
@ -43,7 +37,7 @@ export function WebsiteExpandedView({
>
<WebsiteExpandedMenu excludedIds={excludedIds} />
</Column>
<Column id="metrics-expanded-table" overflow="hidden">
<Column overflow="hidden">
<MetricsExpandedTable
title={formatMessage(labels[view])}
type={view}

View file

@ -19,11 +19,15 @@ export function WebsiteHeader({ showActions }: { showActions?: boolean }) {
return (
<PageHeader title={website.name} icon={<Favicon domain={website.domain} />} marginBottom="3">
<Row alignItems="center" gap="6" wrap="wrap">
<Row alignItems="center" gap="6">
<ActiveUsers websiteId={website.id} />
{showActions && (
<Row alignItems="center" gap>
<Row
display={{ xs: 'none', sm: 'none', md: 'none', lg: 'flex', xl: 'flex' }}
alignItems="center"
gap
>
<ShareButton websiteId={website.id} shareId={website.shareId} />
<LinkButton href={renderUrl(`/websites/${website.id}/settings`, false)}>
<Icon>

View file

@ -1,5 +1,5 @@
import { ReactNode } from 'react';
import { Heading, Icon, Row, Text, Column, Grid } from '@umami/react-zen';
import { Heading, Icon, Row, RowProps, Text, Column } from '@umami/react-zen';
export function PageHeader({
title,
@ -8,6 +8,7 @@ export function PageHeader({
icon,
showBorder = true,
children,
...props
}: {
title: string;
description?: string;
@ -17,13 +18,16 @@ export function PageHeader({
allowEdit?: boolean;
className?: string;
children?: ReactNode;
}) {
} & RowProps) {
return (
<Grid
columns={{ xs: '1fr', md: '1fr 1fr' }}
<Row
justifyContent="space-between"
alignItems="center"
paddingY="6"
marginBottom="6"
border={showBorder ? 'bottom' : undefined}
width="100%"
{...props}
>
<Column gap="2">
{label}
@ -42,6 +46,6 @@ export function PageHeader({
)}
</Column>
<Row justifyContent="flex-end">{children}</Row>
</Grid>
</Row>
);
}

View file

@ -28,7 +28,6 @@ export function WebsiteDateFilter({
query: { compare = 'prev', offset = 0 },
} = useNavigation();
const disableForward = isAllTime || isAfter(dateRange.endDate, new Date());
const showCompare = allowCompare && !isAllTime;
const websiteDateRange = useDateRangeQuery(websiteId);
@ -63,7 +62,7 @@ export function WebsiteDateFilter({
}, [dateRange]);
return (
<Row wrap="wrap" gap>
<Row gap>
{showButtons && !isAllTime && !isCustomRange && (
<Row gap="1">
<Button onPress={() => handleIncrement(-1)} variant="outline">
@ -86,7 +85,7 @@ export function WebsiteDateFilter({
renderDate={+offset !== 0}
/>
</Row>
{showCompare && (
{allowCompare && !isAllTime && (
<Row alignItems="center" gap>
<Text weight="bold">VS</Text>
<Row width="200px">

View file

@ -7,7 +7,7 @@ export interface MetricsBarProps extends GridProps {
export function MetricsBar({ children, ...props }: MetricsBarProps) {
return (
<Grid columns="repeat(auto-fit, minmax(160px, 1fr))" gap {...props}>
<Grid columns="repeat(auto-fit, minmax(140px, 1fr))" gap {...props}>
{children}
</Grid>
);

View file

@ -6,7 +6,7 @@ export const IP_ADDRESS_HEADERS = [
'fastly-client-ip', // Fastly
'x-nf-client-connection-ip', // Netlify
'do-connecting-ip', // Digital Ocean
'x-appengine-user-ip', // Google App Engine
'x-appengine-user-ip', // Google App Ending
'x-client-ip',
'x-cluster-client-ip',
'x-forwarded',