From 1d908a9d8127b7571a5ab8fc8378e7d99e7931d2 Mon Sep 17 00:00:00 2001 From: ErikaKK <491649804@qq.com> Date: Fri, 5 Sep 2025 13:27:15 +0800 Subject: [PATCH 01/12] update clerk dependency --- package.json | 4 +- pnpm-lock.yaml | 637 +++++++++++++++--- src/middleware.ts | 7 +- .../api/routers/admin/users/update-email.ts | 8 +- src/server/api/routers/users/create.ts | 3 +- src/server/api/routers/users/update-email.ts | 68 ++ src/server/api/routers/users/update.ts | 34 +- 7 files changed, 637 insertions(+), 124 deletions(-) create mode 100644 src/server/api/routers/users/update-email.ts diff --git a/package.json b/package.json index f977b89f3..18ccda0e3 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,9 @@ "prepare": "node .husky/install.mjs || true" }, "dependencies": { - "@clerk/nextjs": "5.2.10", + "@clerk/clerk-js": "^5.91.1", + "@clerk/clerk-react": "^5.46.0", + "@clerk/nextjs": "6.31.8", "@hookform/resolvers": "3.3.4", "@material-symbols/font-300": "0.17.1", "@radix-ui/react-accordion": "1.1.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 68f024e6b..4500ac097 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,9 +16,15 @@ importers: .: dependencies: + '@clerk/clerk-js': + specifier: ^5.91.1 + version: 5.91.2(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.3)(use-sync-external-store@1.5.0(react@18.3.1))(zod@3.22.4) + '@clerk/clerk-react': + specifier: ^5.46.0 + version: 5.46.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@clerk/nextjs': - specifier: 5.2.10 - version: 5.2.10(next@14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: 6.31.8 + version: 6.31.8(next@14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@hookform/resolvers': specifier: 3.3.4 version: 3.3.4(react-hook-form@7.51.2(react@18.3.1)) @@ -297,6 +303,9 @@ importers: packages: + '@adraffy/ens-normalize@1.11.0': + resolution: {integrity: sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==} + '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -415,6 +424,9 @@ packages: resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} engines: {node: '>=6.9.0'} + '@base-org/account@2.0.1': + resolution: {integrity: sha512-tySVNx+vd6XEynZL0uvB10uKiwnAfThr8AbKTwILVG86mPbLAhEOInQIk+uDnvpTvfdUhC1Bi5T/46JvFoLZQQ==} + '@builder.io/react-hydration-overlay@0.3.0': resolution: {integrity: sha512-o8CPDcGoWS5NkND8xQGvHmz/mixslQzNu0yxWD+xVM8AJpr2gW3pg6Q2LeSt5wLLswJg0e3hT8E0kyqDSVNrjg==} peerDependencies: @@ -426,41 +438,54 @@ packages: '@spotlightjs/spotlight': optional: true - '@clerk/backend@1.5.0': - resolution: {integrity: sha512-uJUcA4oIDfTkjhjcjnlnarjuLXp5rVvtXB1/HtFKLXfvm8SveH5D3w97IeiUDNMvggxBo1Q/faBDBjEJxTV9Cw==} + '@clerk/backend@2.12.1': + resolution: {integrity: sha512-itpMTMrPaitY8wU6gvmG5GMLecAmyvWnNZB3FHntIhyS/na+bHcdYRyNlCksvh7s39f3wXuS2lwoUCanXEs7yg==} engines: {node: '>=18.17.0'} - '@clerk/clerk-react@5.3.0': - resolution: {integrity: sha512-sKJrIHwkfeemSolI4pU+jwNFYFG+HfwZ+Anx1JvnvlLDELUrsQMFCVOKCE7TOEs+Y/9loEK7xFdanzvxL+uasA==} + '@clerk/clerk-js@5.91.2': + resolution: {integrity: sha512-6oqaLGo9dLXBginLoQwLtKsZwaayvHIbGgl5CQr6wPMSxrUai7DxAGwZCfE7CvqsRzJrn6cvUGSH00lnU2fMGA==} engines: {node: '>=18.17.0'} peerDependencies: - react: '>=18 || >=19.0.0-beta' - react-dom: '>=18 || >=19.0.0-beta' + react: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-0 - '@clerk/nextjs@5.2.10': - resolution: {integrity: sha512-P2oxgrE7HfR5SLGAEGcuzstiWggyuRgqdytWo9NdKD/el8LYeuxey6FcIzNNzndWyTik7ERXSKG4fVrIrHnv9g==} + '@clerk/clerk-react@5.46.1': + resolution: {integrity: sha512-vKtIU3SHfIfsPFcLlw+I+El3VxN/io2aekGzAP7cKoClRPB4bE8GKsLvLIA326ff7yTDnvyrdxfEFY4ieyq5zg==} engines: {node: '>=18.17.0'} peerDependencies: - next: ^13.5.4 || ^14.0.3 || >=15.0.0-rc - react: '>=18 || >=19.0.0-beta' - react-dom: '>=18 || >=19.0.0-beta' + react: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-0 - '@clerk/shared@2.4.2': - resolution: {integrity: sha512-8AZg7Qus7nGBwhbdJINV1NR0vAbOYtv/m8C9VdASHW/DzamNJvyf9d61E1DLZGTTjQw/Fja9zolq9CcWT5d6NA==} + '@clerk/localizations@3.24.2': + resolution: {integrity: sha512-ElOLHQJALrWbnRpdausIsdGMmRzHYjl0WxAfkaP+u+NSk/4rIlsNsdvMCSqktC5pZUIqvRGGKKLhNFrvO/gx+g==} + engines: {node: '>=18.17.0'} + + '@clerk/nextjs@6.31.8': + resolution: {integrity: sha512-gSGJo8AfucTGmb6ejkVQxRprlhE/dHHHYWau/a4yVkotna9VZFbdX9v1I3KSxdqEQDIFp5jgDY8LHEsKC4yeYQ==} engines: {node: '>=18.17.0'} peerDependencies: - react: '>=18 || >=19.0.0-beta' - react-dom: '>=18 || >=19.0.0-beta' + next: ^13.5.7 || ^14.2.25 || ^15.2.3 + react: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + + '@clerk/shared@3.24.1': + resolution: {integrity: sha512-9ZLSeQOejWKH+MdftUH4iBjvx1ilIvZPZqJ2YQDO1RkY3lT3DVj64zIHHMZpjQN7dw2MOsalD0sHIPlQhshT5A==} + engines: {node: '>=18.17.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-0 peerDependenciesMeta: react: optional: true react-dom: optional: true - '@clerk/types@4.10.0': - resolution: {integrity: sha512-Yy4m+aCrvVHT8BVNGDi40hcSUTwMf5o+cBHhAQQTofzCikc+DRI6yL7aYT6nf2YJ5gfm7WdeUwPM8Zz08TDzNA==} + '@clerk/types@4.84.1': + resolution: {integrity: sha512-0lLz3u8u0Ot5ZUObU+8JJLOeiHHnruShJMeLAHNryp1d5zANPQquOyagamxbkoV1K2lAf8ld3liobs3EBzll6Q==} engines: {node: '>=18.17.0'} + '@coinbase/wallet-sdk@4.3.0': + resolution: {integrity: sha512-T3+SNmiCw4HzDm4we9wCHCxlP0pqCiwKe4sOwPH3YAK2KSKjxPRydKu6UQJrdONFVLG7ujXvbd/6ZqmvJb8rkw==} '@clerk/types@4.84.1': resolution: {integrity: sha512-0lLz3u8u0Ot5ZUObU+8JJLOeiHHnruShJMeLAHNryp1d5zANPQquOyagamxbkoV1K2lAf8ld3liobs3EBzll6Q==} engines: {node: '>=18.17.0'} @@ -480,6 +505,9 @@ packages: '@emotion/babel-plugin@11.13.5': resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + '@emotion/cache@11.11.0': + resolution: {integrity: sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==} + '@emotion/cache@11.14.0': resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} @@ -489,9 +517,21 @@ packages: '@emotion/hash@0.9.2': resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + '@emotion/memoize@0.8.1': + resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==} + '@emotion/memoize@0.9.0': resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + '@emotion/react@11.11.1': + resolution: {integrity: sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + '@emotion/react@11.14.0': resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} peerDependencies: @@ -518,6 +558,9 @@ packages: '@emotion/utils@1.4.2': resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + '@emotion/weak-memoize@0.3.1': + resolution: {integrity: sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==} + '@emotion/weak-memoize@0.4.0': resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} @@ -715,9 +758,18 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' + '@floating-ui/react@0.27.12': + resolution: {integrity: sha512-kKlWNrpIQxF1B/a2MZvE0/uyKby4960yjO91W7nVyNKmmfNi62xU9HCjL1M1eWzx/LFj/VPSwJVbwQk9Pq/68A==} + peerDependencies: + react: '>=17.0.0' + react-dom: '>=17.0.0' + '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + '@formkit/auto-animate@0.8.4': + resolution: {integrity: sha512-DHHC01EJ1p70Q0z/ZFRBIY8NDnmfKccQoyoM84Tgb6omLMat6jivCdf272Y8k3nf4Lzdin/Y4R9q8uFtU0GbnA==} + '@hookform/resolvers@3.3.4': resolution: {integrity: sha512-o5cgpGOuJYrd+iMKvkttOclgwRW86EsWJZZRC23prf0uU2i48Htq4PuT73AVb9ionFyZrwYEITuOFGF+BydEtQ==} peerDependencies: @@ -849,6 +901,26 @@ packages: cpu: [x64] os: [win32] + '@noble/ciphers@1.3.0': + resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.1': + resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.7': + resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.4.0': + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -2039,6 +2111,15 @@ packages: '@rushstack/eslint-patch@1.12.0': resolution: {integrity: sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==} + '@scure/base@1.2.6': + resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} + + '@scure/bip32@1.7.0': + resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==} + + '@scure/bip39@1.6.0': + resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} + '@selderee/plugin-htmlparser2@0.11.0': resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} @@ -2164,9 +2245,19 @@ packages: resolution: {integrity: sha512-m+pauPLJUHO4qzBNKlHRMe+qDAhQdbEd7g/v/SW8BWZARwJC0liHB4AnVAJuM/7JMFEPiT2ZO3SRt1VQsCeoYg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + '@stablelib/base64@1.0.1': + resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} + + '@stripe/stripe-js@5.6.0': + resolution: {integrity: sha512-w8CEY73X/7tw2KKlL3iOk679V9bWseE4GzNz3zlaYxcTjmcmWOathRb0emgo/QQ3eoNzmq68+2Y2gxluAv3xGw==} + engines: {node: '>=12.16'} + '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + '@swc/helpers@0.5.17': + resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} + '@swc/helpers@0.5.5': resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} @@ -2824,10 +2915,27 @@ packages: '@xtuc/long@4.2.2': resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + '@zxcvbn-ts/core@3.0.4': + resolution: {integrity: sha512-aQeiT0F09FuJaAqNrxynlAwZ2mW/1MdXakKWNmGM1Qp/VaY6CnB/GfnMS2T8gB2231Esp1/maCWd8vTG4OuShw==} + + '@zxcvbn-ts/language-common@3.0.4': + resolution: {integrity: sha512-viSNNnRYtc7ULXzxrQIVUNwHAPSXRtoIwy/Tq4XQQdIknBzw4vz36lQLF6mvhMlTIlpjoN/Z1GFu/fwiAlUSsw==} + abbrev@2.0.0: resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + abitype@1.1.0: + resolution: {integrity: sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + acorn-import-attributes@1.9.5: resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} peerDependencies: @@ -2872,6 +2980,9 @@ packages: ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + alien-signals@2.0.6: + resolution: {integrity: sha512-P3TxJSe31bUHBiblg59oU1PpaWPtmxF9GhJ/cB7OkgJ0qN/ifFSKUI25/v8ZhsT+lIG6ac8DpTOplXxORX6F3Q==} + ansi-escapes@7.0.0: resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} engines: {node: '>=18'} @@ -3013,6 +3124,9 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + browser-tabs-lock@1.3.0: + resolution: {integrity: sha512-g6nHaobTiT0eMZ7jh16YpD2kcjAp+PInbiVq3M1x6KKaEIVhT4v9oURNIpZLOZ3LQbQ3XYfNhMAb/9hzNLIWrw==} + browserslist@4.25.4: resolution: {integrity: sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -3091,6 +3205,10 @@ packages: client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + clsx@1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + clsx@2.0.0: resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} engines: {node: '>=6'} @@ -3161,6 +3279,12 @@ packages: resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} engines: {node: '>=12.13'} + copy-to-clipboard@3.3.3: + resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} + + core-js@3.41.0: + resolution: {integrity: sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==} + core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -3187,9 +3311,6 @@ packages: engines: {node: '>=4'} hasBin: true - csstype@3.1.1: - resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} - csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -3729,9 +3850,16 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-sha256@1.3.0: + resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} + fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} @@ -3986,6 +4114,9 @@ packages: engines: {node: '>=18'} hasBin: true + idb-keyval@6.2.1: + resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -4014,6 +4145,12 @@ packages: ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + input-otp@1.4.2: + resolution: {integrity: sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} @@ -4172,6 +4309,11 @@ packages: resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} engines: {node: '>=16'} + isows@1.0.7: + resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==} + peerDependencies: + ws: '*' + iterator.prototype@1.1.5: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} @@ -4318,9 +4460,6 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true - lower-case@2.0.2: - resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} - lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -4334,10 +4473,6 @@ packages: resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} engines: {node: '>=12'} - map-obj@4.3.0: - resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} - engines: {node: '>=8'} - mapbox-gl@3.2.0: resolution: {integrity: sha512-v8S7x+wTr35kJ9nqzgn/VPiSFZxBkyQhwCk9bdyiFHVwCukNGG3LXt03FoaHHTsOuB9JWenWE96k0Uw+HGMZ8w==} @@ -4476,9 +4611,6 @@ packages: sass: optional: true - no-case@3.0.4: - resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} - node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -4563,6 +4695,22 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} + ox@0.6.9: + resolution: {integrity: sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.9.3: + resolution: {integrity: sha512-KzyJP+fPV4uhuuqrTZyok4DC7vFzi7HLUFiUNEmpbyh59htKWkOC98IONC1zgXJPbHAhQgqs6B0Z6StCGhmQvg==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -4608,9 +4756,6 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-to-regexp@6.2.2: - resolution: {integrity: sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==} - path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -4748,6 +4893,12 @@ packages: potpack@2.1.0: resolution: {integrity: sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==} + preact@10.24.2: + resolution: {integrity: sha512-1cSoF0aCC8uaARATfrlz4VCBqE8LwZwRfLgkxJOQwAlQt6ayTmi0D9OF7nXid1POI5SZidFuG9CnlXbDfLqY/Q==} + + preact@10.27.1: + resolution: {integrity: sha512-V79raXEWch/rbqoNc7nT9E4ep7lu+mI3+sBmfRD4i1M73R3WLYcCtdI0ibxGVf4eQL8ZIz2nFacqEC+rmnOORQ==} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -4841,6 +4992,11 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + qrcode.react@4.2.0: + resolution: {integrity: sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -5144,13 +5300,6 @@ packages: resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} engines: {node: '>=18'} - snake-case@3.0.4: - resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} - - snakecase-keys@5.4.4: - resolution: {integrity: sha512-YTywJG93yxwHLgrYLZjlC75moVEX04LZM4FHfihjHe1FCXm+QaLOFfSf535aXOAd0ArVQMWUAe8ZPm4VtWyXaA==} - engines: {node: '>=12'} - snarkdown@2.0.0: resolution: {integrity: sha512-MgL/7k/AZdXCTJiNgrO7chgDqaB9FGM/1Tvlcenenb7div6obaDATzs16JhFyHHBGodHT3B7RzRc5qk8pFhg3A==} @@ -5184,6 +5333,9 @@ packages: resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==} engines: {node: '>=6'} + standardwebhooks@1.0.0: + resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} + std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} @@ -5302,6 +5454,9 @@ packages: peerDependencies: react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + tabbable@6.2.0: + resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + tailwind-merge@2.2.2: resolution: {integrity: sha512-tWANXsnmJzgw6mQ07nE3aCDkCK4QdT3ThPMCzawoYA2Pws7vSTCvz3Vrjg61jVUGfFZPJzxEP+NimbcW+EdaDw==} @@ -5364,6 +5519,9 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toggle-selection@1.0.6: + resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -5379,9 +5537,6 @@ packages: tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} - tslib@2.4.1: - resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -5400,10 +5555,6 @@ packages: resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} engines: {node: '>=8'} - type-fest@2.19.0: - resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} - engines: {node: '>=12.20'} - typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -5494,6 +5645,14 @@ packages: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true + viem@2.37.3: + resolution: {integrity: sha512-hwoZqkFSy13GCFzIftgfIH8hNENvdlcHIvtLt73w91tL6rKmZjQisXWTahi1Vn5of8/JQ1FBKfwUus3YkDXwbw==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + vt-pbf@3.1.3: resolution: {integrity: sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==} @@ -5569,6 +5728,18 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -5596,8 +5767,28 @@ packages: zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + zustand@5.0.3: + resolution: {integrity: sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + snapshots: + '@adraffy/ens-normalize@1.11.0': {} + '@alloc/quick-lru@5.2.0': {} '@ampproject/remapping@2.3.0': @@ -5778,6 +5969,26 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@base-org/account@2.0.1(@types/react@18.3.3)(react@18.3.1)(typescript@5.4.3)(use-sync-external-store@1.5.0(react@18.3.1))(zod@3.22.4)': + dependencies: + '@noble/hashes': 1.4.0 + clsx: 1.2.1 + eventemitter3: 5.0.1 + idb-keyval: 6.2.1 + ox: 0.6.9(typescript@5.4.3)(zod@3.22.4) + preact: 10.24.2 + viem: 2.37.3(typescript@5.4.3)(zod@3.22.4) + zustand: 5.0.3(@types/react@18.3.3)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1)) + transitivePeerDependencies: + - '@types/react' + - bufferutil + - immer + - react + - typescript + - use-sync-external-store + - utf-8-validate + - zod + '@builder.io/react-hydration-overlay@0.3.0(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.101.3(esbuild@0.25.9))': dependencies: beautify: 0.0.8 @@ -5790,42 +6001,86 @@ snapshots: - supports-color optional: true - '@clerk/backend@1.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@clerk/backend@2.12.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@clerk/shared': 2.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@clerk/types': 4.10.0 + '@clerk/shared': 3.24.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@clerk/types': 4.84.1 cookie: 1.0.2 - snakecase-keys: 5.4.4 - tslib: 2.4.1 + standardwebhooks: 1.0.0 + tslib: 2.8.1 transitivePeerDependencies: - react - react-dom - '@clerk/clerk-react@5.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@clerk/clerk-js@5.91.2(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.3)(use-sync-external-store@1.5.0(react@18.3.1))(zod@3.22.4)': dependencies: - '@clerk/shared': 2.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@clerk/types': 4.10.0 + '@base-org/account': 2.0.1(@types/react@18.3.3)(react@18.3.1)(typescript@5.4.3)(use-sync-external-store@1.5.0(react@18.3.1))(zod@3.22.4) + '@clerk/localizations': 3.24.2 + '@clerk/shared': 3.24.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@clerk/types': 4.84.1 + '@coinbase/wallet-sdk': 4.3.0 + '@emotion/cache': 11.11.0 + '@emotion/react': 11.11.1(@types/react@18.3.3)(react@18.3.1) + '@floating-ui/react': 0.27.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@floating-ui/react-dom': 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@formkit/auto-animate': 0.8.4 + '@stripe/stripe-js': 5.6.0 + '@swc/helpers': 0.5.17 + '@zxcvbn-ts/core': 3.0.4 + '@zxcvbn-ts/language-common': 3.0.4 + alien-signals: 2.0.6 + browser-tabs-lock: 1.3.0 + copy-to-clipboard: 3.3.3 + core-js: 3.41.0 + crypto-js: 4.2.0 + dequal: 2.0.3 + input-otp: 1.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + qrcode.react: 4.2.0(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + regenerator-runtime: 0.14.1 + swr: 2.3.4(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - bufferutil + - immer + - supports-color + - typescript + - use-sync-external-store + - utf-8-validate + - zod + + '@clerk/clerk-react@5.46.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': tslib: 2.4.1 '@clerk/nextjs@5.2.10(next@14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@clerk/backend': 1.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@clerk/clerk-react': 5.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@clerk/shared': 2.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@clerk/types': 4.10.0 - crypto-js: 4.2.0 + '@clerk/shared': 3.24.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@clerk/types': 4.84.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tslib: 2.8.1 + + '@clerk/localizations@3.24.2': + dependencies: + '@clerk/types': 4.84.1 + + '@clerk/nextjs@6.31.8(next@14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@clerk/backend': 2.12.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@clerk/clerk-react': 5.46.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@clerk/shared': 3.24.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@clerk/types': 4.84.1 next: 14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - path-to-regexp: 6.2.2 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) server-only: 0.0.1 - tslib: 2.4.1 + tslib: 2.8.1 - '@clerk/shared@2.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@clerk/shared@3.24.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@clerk/types': 4.10.0 + '@clerk/types': 4.84.1 + dequal: 2.0.3 glob-to-regexp: 0.4.1 js-cookie: 3.0.5 std-env: 3.9.0 @@ -5834,13 +6089,16 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@clerk/types@4.10.0': + '@clerk/types@4.84.1': dependencies: - csstype: 3.1.1 + csstype: 3.1.3 '@clerk/types@4.84.1': dependencies: - csstype: 3.1.3 + '@noble/hashes': 1.8.0 + clsx: 1.2.1 + eventemitter3: 5.0.1 + preact: 10.27.1 '@drizzle-team/brocli@0.10.2': {} @@ -5875,7 +6133,14 @@ snapshots: stylis: 4.2.0 transitivePeerDependencies: - supports-color - optional: true + + '@emotion/cache@11.11.0': + dependencies: + '@emotion/memoize': 0.8.1 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.3.1 + stylis: 4.2.0 '@emotion/cache@11.14.0': dependencies: @@ -5897,11 +6162,27 @@ snapshots: - supports-color optional: true - '@emotion/hash@0.9.2': - optional: true + '@emotion/hash@0.9.2': {} - '@emotion/memoize@0.9.0': - optional: true + '@emotion/memoize@0.8.1': {} + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.11.1(@types/react@18.3.3)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.3 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.11.0 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.3.1 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + transitivePeerDependencies: + - supports-color '@emotion/react@11.14.0(@types/react@18.3.3)(react@18.3.1)': dependencies: @@ -5927,21 +6208,18 @@ snapshots: '@emotion/unitless': 0.10.0 '@emotion/utils': 1.4.2 csstype: 3.1.3 - optional: true - '@emotion/sheet@1.4.0': - optional: true + '@emotion/sheet@1.4.0': {} - '@emotion/unitless@0.10.0': - optional: true + '@emotion/unitless@0.10.0': {} '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@18.3.1)': dependencies: react: 18.3.1 - optional: true - '@emotion/utils@1.4.2': - optional: true + '@emotion/utils@1.4.2': {} + + '@emotion/weak-memoize@0.3.1': {} '@emotion/weak-memoize@0.4.0': optional: true @@ -6072,8 +6350,18 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + '@floating-ui/react@0.27.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/react-dom': 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@floating-ui/utils': 0.2.10 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tabbable: 6.2.0 + '@floating-ui/utils@0.2.10': {} + '@formkit/auto-animate@0.8.4': {} + '@hookform/resolvers@3.3.4(react-hook-form@7.51.2(react@18.3.1))': dependencies: react-hook-form: 7.51.2(react@18.3.1) @@ -6181,6 +6469,20 @@ snapshots: '@next/swc-win32-x64-msvc@14.2.26': optional: true + '@noble/ciphers@1.3.0': {} + + '@noble/curves@1.9.1': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/curves@1.9.7': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/hashes@1.4.0': {} + + '@noble/hashes@1.8.0': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -7435,6 +7737,19 @@ snapshots: '@rushstack/eslint-patch@1.12.0': {} + '@scure/base@1.2.6': {} + + '@scure/bip32@1.7.0': + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + + '@scure/bip39@1.6.0': + dependencies: + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@selderee/plugin-htmlparser2@0.11.0': dependencies: domhandler: 5.0.3 @@ -7630,8 +7945,16 @@ snapshots: dependencies: '@square/web-payments-sdk-types': 1.78.1 + '@stablelib/base64@1.0.1': {} + + '@stripe/stripe-js@5.6.0': {} + '@swc/counter@0.1.3': {} + '@swc/helpers@0.5.17': + dependencies: + tslib: 2.8.1 + '@swc/helpers@0.5.5': dependencies: '@swc/counter': 0.1.3 @@ -7795,8 +8118,7 @@ snapshots: dependencies: undici-types: 5.26.5 - '@types/parse-json@4.0.2': - optional: true + '@types/parse-json@4.0.2': {} '@types/pg-pool@2.0.6': dependencies: @@ -8492,9 +8814,20 @@ snapshots: '@xtuc/long@4.2.2': {} + '@zxcvbn-ts/core@3.0.4': + dependencies: + fastest-levenshtein: 1.0.16 + + '@zxcvbn-ts/language-common@3.0.4': {} + abbrev@2.0.0: optional: true + abitype@1.1.0(typescript@5.4.3)(zod@3.22.4): + optionalDependencies: + typescript: 5.4.3 + zod: 3.22.4 + acorn-import-attributes@1.9.5(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -8538,6 +8871,8 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + alien-signals@2.0.6: {} + ansi-escapes@7.0.0: dependencies: environment: 1.1.0 @@ -8678,7 +9013,6 @@ snapshots: '@babel/runtime': 7.28.3 cosmiconfig: 7.1.0 resolve: 1.22.10 - optional: true balanced-match@0.4.2: {} @@ -8706,9 +9040,14 @@ snapshots: dependencies: fill-range: 7.1.1 + browser-tabs-lock@1.3.0: browserslist@4.25.4: dependencies: - caniuse-lite: 1.0.30001741 + lodash: 4.17.21 + + browserslist@4.25.4: + dependencies: + caniuse-lite: 1.0.30001739 electron-to-chromium: 1.5.214 node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.25.4) @@ -8792,6 +9131,8 @@ snapshots: client-only@0.0.1: {} + clsx@1.2.1: {} + clsx@2.0.0: {} clsx@2.1.0: {} @@ -8849,8 +9190,7 @@ snapshots: proto-list: 1.2.4 optional: true - convert-source-map@1.9.0: - optional: true + convert-source-map@1.9.0: {} convert-source-map@2.0.0: {} @@ -8860,6 +9200,12 @@ snapshots: dependencies: is-what: 4.1.16 + copy-to-clipboard@3.3.3: + dependencies: + toggle-selection: 1.0.6 + + core-js@3.41.0: {} + core-util-is@1.0.3: optional: true @@ -8870,7 +9216,6 @@ snapshots: parse-json: 5.2.0 path-type: 4.0.0 yaml: 1.10.2 - optional: true cross-spawn@7.0.6: dependencies: @@ -8887,8 +9232,6 @@ snapshots: cssesc@3.0.0: {} - csstype@3.1.1: {} - csstype@3.1.3: {} d3-array@3.2.1: @@ -9127,7 +9470,6 @@ snapshots: error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 - optional: true es-abstract@1.24.0: dependencies: @@ -9479,8 +9821,7 @@ snapshots: esutils@2.0.3: {} - eventemitter3@5.0.1: - optional: true + eventemitter3@5.0.1: {} events@3.3.0: {} @@ -9513,8 +9854,12 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-sha256@1.3.0: {} + fast-uri@3.1.0: {} + fastest-levenshtein@1.0.16: {} + fastq@1.19.1: dependencies: reusify: 1.1.0 @@ -9535,8 +9880,7 @@ snapshots: dependencies: to-regex-range: 5.0.1 - find-root@1.1.0: - optional: true + find-root@1.1.0: {} find-up@5.0.0: dependencies: @@ -9790,6 +10134,8 @@ snapshots: husky@9.0.11: optional: true + idb-keyval@6.2.1: {} + ieee754@1.2.1: {} ignore@5.3.2: {} @@ -9818,6 +10164,11 @@ snapshots: ini@1.3.8: optional: true + input-otp@1.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 @@ -9832,8 +10183,7 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 - is-arrayish@0.2.1: - optional: true + is-arrayish@0.2.1: {} is-async-function@2.1.1: dependencies: @@ -9974,6 +10324,10 @@ snapshots: isexe@3.1.1: {} + isows@1.0.7(ws@8.18.3): + dependencies: + ws: 8.18.3 + iterator.prototype@1.1.5: dependencies: define-data-property: 1.1.4 @@ -10395,6 +10749,35 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + ox@0.6.9(typescript@5.4.3)(zod@3.22.4): + dependencies: + '@adraffy/ens-normalize': 1.11.0 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.4.3)(zod@3.22.4) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.4.3 + transitivePeerDependencies: + - zod + + ox@0.9.3(typescript@5.4.3)(zod@3.22.4): + dependencies: + '@adraffy/ens-normalize': 1.11.0 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.4.3)(zod@3.22.4) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.4.3 + transitivePeerDependencies: + - zod + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -10415,7 +10798,6 @@ snapshots: error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - optional: true parseley@0.12.1: dependencies: @@ -10438,8 +10820,6 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 - path-to-regexp@6.2.2: {} - path-type@4.0.0: {} pbf@3.3.0: @@ -10554,6 +10934,10 @@ snapshots: potpack@2.1.0: {} + preact@10.24.2: {} + + preact@10.27.1: {} + prelude-ls@1.2.1: {} prettier-plugin-tailwindcss@0.5.13(@trivago/prettier-plugin-sort-imports@5.2.2(prettier@3.2.5))(prettier@3.2.5): @@ -10588,6 +10972,10 @@ snapshots: punycode@2.3.1: {} + qrcode.react@4.2.0(react@18.3.1): + dependencies: + react: 18.3.1 + queue-microtask@1.2.3: {} quickselect@2.0.0: {} @@ -10980,8 +11368,7 @@ snapshots: buffer-from: 1.1.2 source-map: 0.6.1 - source-map@0.5.7: - optional: true + source-map@0.5.7: {} source-map@0.6.1: {} @@ -11004,6 +11391,11 @@ snapshots: dependencies: type-fest: 0.7.1 + standardwebhooks@1.0.0: + dependencies: + '@stablelib/base64': 1.0.1 + fast-sha256: 1.3.0 + std-env@3.9.0: {} stop-iteration-iterator@1.1.0: @@ -11112,8 +11504,7 @@ snapshots: optionalDependencies: '@babel/core': 7.28.3 - stylis@4.2.0: - optional: true + stylis@4.2.0: {} sucrase@3.35.0: dependencies: @@ -11149,6 +11540,8 @@ snapshots: react: 18.3.1 use-sync-external-store: 1.5.0(react@18.3.1) + tabbable@6.2.0: {} + tailwind-merge@2.2.2: dependencies: '@babel/runtime': 7.28.3 @@ -11227,6 +11620,8 @@ snapshots: dependencies: is-number: 7.0.0 + toggle-selection@1.0.6: {} + tr46@0.0.3: {} ts-api-utils@1.4.3(typescript@5.4.3): @@ -11242,8 +11637,6 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 - tslib@2.4.1: {} - tslib@2.8.1: {} tweakpane@4.0.5: {} @@ -11256,8 +11649,6 @@ snapshots: type-fest@0.7.1: {} - type-fest@2.19.0: {} - typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -11375,6 +11766,23 @@ snapshots: uuid@9.0.1: {} + viem@2.37.3(typescript@5.4.3)(zod@3.22.4): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.4.3)(zod@3.22.4) + isows: 1.0.7(ws@8.18.3) + ox: 0.9.3(typescript@5.4.3)(zod@3.22.4) + ws: 8.18.3 + optionalDependencies: + typescript: 5.4.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + vt-pbf@3.1.3: dependencies: '@mapbox/point-geometry': 0.1.0 @@ -11501,12 +11909,13 @@ snapshots: wrappy@1.0.2: {} + ws@8.18.3: {} + xtend@4.0.2: {} yallist@3.1.1: {} - yaml@1.10.2: - optional: true + yaml@1.10.2: {} yaml@2.3.4: optional: true @@ -11516,3 +11925,9 @@ snapshots: yocto-queue@0.1.0: {} zod@3.22.4: {} + + zustand@5.0.3(@types/react@18.3.3)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1)): + optionalDependencies: + '@types/react': 18.3.3 + react: 18.3.1 + use-sync-external-store: 1.5.0(react@18.3.1) diff --git a/src/middleware.ts b/src/middleware.ts index a3b66b9ad..30b14e19e 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -11,7 +11,8 @@ const isProtectedPage = createRouteMatcher(["/dashboard(.*)", "/profile/settings const isAuthPage = createRouteMatcher(["/join(.*)"]) export default clerkMiddleware(async (auth, req) => { - const clerkId = auth().userId + const session = await auth() + const clerkId = session.userId if (isAdminPage(req) && clerkId) { const user = await db.query.User.findFirst({ @@ -20,14 +21,14 @@ export default clerkMiddleware(async (auth, req) => { if (!adminRoles.includes(user?.role ?? "")) { // non-existent clerk role so we go to 404 page cleanly - auth().protect({ + auth.protect({ role: "lmfaooo", }) } } if (isProtectedPage(req)) { - auth().protect() + auth.protect() } if (isAuthPage(req) && clerkId) { diff --git a/src/server/api/routers/admin/users/update-email.ts b/src/server/api/routers/admin/users/update-email.ts index 403c9e027..3acf3cefa 100644 --- a/src/server/api/routers/admin/users/update-email.ts +++ b/src/server/api/routers/admin/users/update-email.ts @@ -10,6 +10,7 @@ import { User } from "~/server/db/schema" export const updateEmail = adminProcedure .input(z.object({ userId: z.string(), oldEmail: z.string().email(), newEmail: z.string().email() })) .mutation(async ({ ctx, input }) => { + const clerk = await clerkClient() const user_data = await ctx.db.query.User.findFirst({ where: eq(User.email, input.oldEmail), }) @@ -29,7 +30,7 @@ export const updateEmail = adminProcedure throw new TRPCError({ code: "NOT_FOUND", message: `User with id: ${input.userId} does not exist` }) } - const clerkUser = await clerkClient().users.getUser(user.clerk_id) + const clerkUser = await clerk.users.getUser(user.clerk_id) if (!clerkUser.primaryEmailAddressId) throw new TRPCError({ code: "NOT_FOUND", @@ -51,11 +52,12 @@ export const updateEmail = adminProcedure }) const updateClerkUserEmail = async (userId: string, oldEmailAddressId: string, newEmailAddress: string) => { - await clerkClient().emailAddresses.createEmailAddress({ + const clerk = await clerkClient() + await clerk.emailAddresses.createEmailAddress({ userId, emailAddress: newEmailAddress, verified: true, primary: true, }) - await clerkClient().emailAddresses.deleteEmailAddress(oldEmailAddressId) + await clerk.emailAddresses.deleteEmailAddress(oldEmailAddressId) } diff --git a/src/server/api/routers/users/create.ts b/src/server/api/routers/users/create.ts index 7ac2b5d33..f4592cd15 100644 --- a/src/server/api/routers/users/create.ts +++ b/src/server/api/routers/users/create.ts @@ -70,7 +70,8 @@ export const create = publicRatedProcedure(Ratelimit.fixedWindow(4, "30s")) }) } - const clerkUser = await clerkClient().users.getUser(input.clerk_id) + const clerk = await clerkClient() + const clerkUser = await clerk.users.getUser(input.clerk_id) if (!clerkUser) { throw new TRPCError({ diff --git a/src/server/api/routers/users/update-email.ts b/src/server/api/routers/users/update-email.ts new file mode 100644 index 000000000..b31e8783c --- /dev/null +++ b/src/server/api/routers/users/update-email.ts @@ -0,0 +1,68 @@ +import { clerkClient } from "@clerk/nextjs/server" +import { TRPCError } from "@trpc/server" +import { Ratelimit } from "@upstash/ratelimit" +import { eq } from "drizzle-orm" +import { z } from "zod" + +import { protectedRatedProcedure } from "~/server/api/trpc" +import { User } from "~/server/db/schema" + +export const updateEmail = protectedRatedProcedure(Ratelimit.fixedWindow(4, "30s")) + .input(z.object({ userId: z.string(), oldEmail: z.string().email(), newEmail: z.string().email() })) + .mutation(async ({ ctx, input }) => { + const currentUser = ctx.user + const clerk = await clerkClient() + if (currentUser?.id !== input.userId) { + throw new TRPCError({ code: "FORBIDDEN", message: "You can only update your own email" }) + } + + const user_data = await ctx.db.query.User.findFirst({ + where: eq(User.email, input.oldEmail), + }) + if (!user_data) { + throw new TRPCError({ code: "NOT_FOUND", message: `User with email: ${input.oldEmail} does not exist` }) + } + const user_email_data = await ctx.db.query.User.findFirst({ + where: eq(User.email, input.newEmail), + }) + if (user_email_data) { + throw new TRPCError({ code: "FORBIDDEN", message: `User with email: ${input.newEmail} already exist` }) + } + const user = await ctx.db.query.User.findFirst({ + where: eq(User.id, input.userId), + }) + if (!user) { + throw new TRPCError({ code: "NOT_FOUND", message: `User with id: ${input.userId} does not exist` }) + } + + const clerkUser = await clerk.users.getUser(user.clerk_id) + if (!clerkUser.primaryEmailAddressId) + throw new TRPCError({ + code: "NOT_FOUND", + message: `Clerk user with id: ${input.userId} does not have a primary email address???`, + }) + + // Clerk will always return the lowercased email from its API + if (clerkUser.primaryEmailAddress?.emailAddress !== input.oldEmail.toLowerCase()) + throw new TRPCError({ code: "BAD_REQUEST", message: "Old email does not match" }) + try { + await updateClerkUserEmail(clerkUser.id, clerkUser.primaryEmailAddressId, input.newEmail) + await ctx.db.update(User).set({ email: input.newEmail }).where(eq(User.id, input.userId)) + } catch (err) { + console.error(err) + throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Failed to update email" }) + } + + return user + }) + +const updateClerkUserEmail = async (userId: string, oldEmailAddressId: string, newEmailAddress: string) => { + const clerk = await clerkClient() + await clerk.emailAddresses.createEmailAddress({ + userId, + emailAddress: newEmailAddress, + verified: true, + primary: true, + }) + await clerk.emailAddresses.deleteEmailAddress(oldEmailAddressId) +} diff --git a/src/server/api/routers/users/update.ts b/src/server/api/routers/users/update.ts index b35bfc60d..9060504f6 100644 --- a/src/server/api/routers/users/update.ts +++ b/src/server/api/routers/users/update.ts @@ -38,17 +38,38 @@ export const update = protectedRatedProcedure(Ratelimit.fixedWindow(4, "30s")) .optional(), student_number: z.string().nullish(), uni: z.string().optional().nullish(), + github: z.string().optional().nullish(), + discord: z.string().optional().nullish(), + subscribe: z.boolean(), }), ) .mutation(async ({ ctx, input }) => { const currentUser = ctx.user + if (!currentUser) throw new Error("Not authenticated") // TODO: update clerk email // TODO: Wrap in a transaction - await clerkClient().users.updateUser(currentUser.clerk_id, { - // emailAddress: input.email, - firstName: input.preferred_name, - lastName: input.name, - }) + try { + const clerk = await clerkClient() + await clerk.users.updateUser(currentUser.clerk_id, { + firstName: input.name, + unsafeMetadata: { + preferred_name: input.preferred_name, + pronouns: input.pronouns, + student_number: input.student_number, + university: input.uni, + github: input.github, + discord: input.discord, + subscribe: input.subscribe, + }, + }) + } catch (err: unknown) { + // Narrow the error type + if (err instanceof Error) { + throw new Error(`Failed to update user metadata: ${err?.message}`) + } else { + throw new Error("Failed to update user metadata: unknown error") + } + } const [user] = await ctx.db .update(User) @@ -59,6 +80,9 @@ export const update = protectedRatedProcedure(Ratelimit.fixedWindow(4, "30s")) pronouns: input.pronouns?.trim(), student_number: input.student_number?.trim() ?? null, university: input.uni?.trim() ?? null, + github: input.github?.trim(), + discord: input.discord?.trim(), + subscribe: input.subscribe, }) .where(eq(User.id, currentUser.id)) .returning() From 45a61806036baf5f4a9c694f6d7d720800b56bbb Mon Sep 17 00:00:00 2001 From: ErikaKK <491649804@qq.com> Date: Fri, 5 Sep 2025 13:27:35 +0800 Subject: [PATCH 02/12] rename file --- src/app/profile/[id]/page.tsx | 4 ++-- src/app/profile/[id]/{profile/page.tsx => profile.tsx} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename src/app/profile/[id]/{profile/page.tsx => profile.tsx} (100%) diff --git a/src/app/profile/[id]/page.tsx b/src/app/profile/[id]/page.tsx index 9612759d5..6c90f0f55 100644 --- a/src/app/profile/[id]/page.tsx +++ b/src/app/profile/[id]/page.tsx @@ -1,12 +1,12 @@ import NotFound from "~/app/not-found" import { api } from "~/trpc/server" -import ProfilePage from "./profile/page" +import ProfilePage from "./profile" const Profile = async ({ params: { id } }: { params: { id: string } }) => { const currentUser = await api.users.getCurrent.query() - if (currentUser) { + if (currentUser.id === id) { return } diff --git a/src/app/profile/[id]/profile/page.tsx b/src/app/profile/[id]/profile.tsx similarity index 100% rename from src/app/profile/[id]/profile/page.tsx rename to src/app/profile/[id]/profile.tsx From b019d2a5b90113bb5c55c99df9c4fbd169b64ced Mon Sep 17 00:00:00 2001 From: ErikaKK <491649804@qq.com> Date: Fri, 5 Sep 2025 14:46:21 +0800 Subject: [PATCH 03/12] update profile and setting page --- .../_components/clients/EditProfile/page.tsx | 7 - src/app/profile/[id]/profile.tsx | 141 ++++++------ src/app/profile/settings/@email/form.tsx | 203 ++++++++++++++++++ src/app/profile/settings/@email/page.tsx | 27 +++ src/app/profile/settings/@personal/form.tsx | 163 ++++++++++++-- src/app/profile/settings/@personal/page.tsx | 5 +- src/app/profile/settings/@socials/form.tsx | 146 ------------- src/app/profile/settings/@socials/page.tsx | 28 --- src/app/profile/settings/layout.tsx | 8 +- src/server/api/routers/users/index.ts | 7 +- src/server/api/routers/users/update-email.ts | 25 +-- .../api/routers/users/update-socials.ts | 28 --- 12 files changed, 467 insertions(+), 321 deletions(-) create mode 100644 src/app/profile/settings/@email/form.tsx create mode 100644 src/app/profile/settings/@email/page.tsx delete mode 100644 src/app/profile/settings/@socials/form.tsx delete mode 100644 src/app/profile/settings/@socials/page.tsx delete mode 100644 src/server/api/routers/users/update-socials.ts diff --git a/src/app/_components/clients/EditProfile/page.tsx b/src/app/_components/clients/EditProfile/page.tsx index c5a4aaa30..82b43634e 100644 --- a/src/app/_components/clients/EditProfile/page.tsx +++ b/src/app/_components/clients/EditProfile/page.tsx @@ -118,7 +118,6 @@ const EditProfile = ({ setIsEditing, id, refetch }: EditProfileProps) => { const [showCircleProgress, setShowCircleProgress] = useState(false) const { data: user } = api.users.get.useQuery(id) const { mutateAsync: updateUser } = api.users.update.useMutation() - const { mutateAsync: updateSocials } = api.users.updateSocials.useMutation() const userDefaultValues = user && { name: user.name, @@ -148,12 +147,6 @@ const EditProfile = ({ setIsEditing, id, refetch }: EditProfileProps) => { student_number: data.isUWA ? data.student_number : null, uni: data.isUWA ? "UWA" : data.uni, }), - - updateSocials({ - ...data, - github: data.github ?? null, - discord: data.discord ?? null, - }), ]) await refetch() diff --git a/src/app/profile/[id]/profile.tsx b/src/app/profile/[id]/profile.tsx index 280acbdd2..93b982e8b 100644 --- a/src/app/profile/[id]/profile.tsx +++ b/src/app/profile/[id]/profile.tsx @@ -1,12 +1,12 @@ "use client" +import Link from "next/link" import { useState } from "react" import { siDiscord, siGithub } from "simple-icons" import { Badge } from "~/components/ui/badge" import { Button } from "~/components/ui/button" -import EditProfile from "~/app/_components/clients/EditProfile/page" import ProfilePageSkeleton from "~/app/_components/clients/ProfilePageSkeleton/page" import TitleText from "~/app/_components/title-text" import { UNIVERSITIES } from "~/lib/constants" @@ -38,82 +38,77 @@ const ProfilePage = ({ id, currentUser }: ProfilePageProps) => {
- {!isEditing ? ( - <> -
- {user.role && {user.role}} -

{user.preferred_name}

-
-

{user.name}

{" "} - {user.pronouns && ( - <> -

·

-

{user.pronouns}

- - )} -
-
-
- {user.github && ( - - - - - {user.github} - - )} - {user.discord && ( - - - - - {user.discord} - - )} -
-
- {user.student_number && ( -
- badge -

{user.student_number}

-
- )} - {user.university ? ( -
- school -

{universityLabel}

-
- ) : ( -
- school -

The University of Western Australia

-
+ <> +
+ {user.role && {user.role}} +

{user.preferred_name}

+
+

{user.name}

{" "} + {user.pronouns && ( + <> +

·

+

{user.pronouns}

+ )}
- {currentUser?.id === user.id && ( -
- +
+
+ {user.github && ( + + + + + {user.github} + + )} + {user.discord && ( + + + + + {user.discord} + + )} +
+
+ {user.student_number && ( +
+ badge +

{user.student_number}

+
+ )} + {user.university ? ( +
+ school +

{universityLabel}

+
+ ) : ( +
+ school +

The University of Western Australia

)} - - ) : ( - - )} +
+ {currentUser?.id === user.id && ( +
+ + + +
+ )} +

Projects

diff --git a/src/app/profile/settings/@email/form.tsx b/src/app/profile/settings/@email/form.tsx new file mode 100644 index 000000000..743478d47 --- /dev/null +++ b/src/app/profile/settings/@email/form.tsx @@ -0,0 +1,203 @@ +"use client" + +import { useReverification, useUser } from "@clerk/nextjs" +import { EmailAddressResource } from "@clerk/types" +import { zodResolver } from "@hookform/resolvers/zod" +import Link from "next/link" +import { useEffect, useState } from "react" +import { FormProvider, useForm } from "react-hook-form" +import { z } from "zod" + +import { Button } from "~/components/ui/button" +import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "~/components/ui/form" +import { Input } from "~/components/ui/input" +import { toast } from "~/components/ui/use-toast" + +import { api } from "~/trpc/react" + +const formSchema = z.object({ + email: z + .string() + .email({ + message: "Invalid email address", + }) + .min(2, { + message: "Email is required", + }), + new_email: z + .string() + .email({ + message: "Invalid email address", + }) + .min(2, { + message: "New email is required", + }), +}) + +type FormSchema = z.infer + +const defaultValues: FormSchema = { + email: "", + new_email: "", +} + +const EmailForm = (props: { user_id: string; email?: Partial }) => { + const utils = api.useUtils() + const { isLoaded, isSignedIn, user } = useUser() + const [countdown, setCountdown] = useState(0) + const [code, setCode] = useState("") + const [step, setStep] = useState<"submitForm" | "enterCode" | "verifying" | "updated">("submitForm") + const [emailObj, setEmailObj] = useState() + const createEmailAddress = useReverification((email: string) => user?.createEmailAddress({ email })) + useEffect(() => { + if (countdown > 0) { + const timer = setInterval(() => { + setCountdown((prev) => prev - 1) + }, 1000) + + return () => clearInterval(timer) + } + }, [countdown]) + const updateEmail = api.users.updateEmail.useMutation({ + onSuccess: async () => { + await utils.users.getCurrent.refetch() + }, + }) + + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: props.email ? { ...props.email } : defaultValues, + }) + if (!isLoaded) { + return null + } + + if (!isSignedIn) { + return

You must be logged in to access this page

+ } + + const sendOtp = async (values: FormSchema) => { + try { + const res = await createEmailAddress(values.new_email.trim()) + await user.reload() + + const emailAddress = user.emailAddresses.find((a) => a.id === res?.id) + setEmailObj(emailAddress) + + emailAddress?.prepareVerification({ strategy: "email_code" }) + setCountdown(60) + setStep("enterCode") + window.scrollTo({ + top: 0, + behavior: "smooth", + }) + } catch (error) { + console.error(JSON.stringify(error, null, 2)) + toast({ + variant: "destructive", + title: "Verification failed", + description: `Error sending OTP. ${(error as { message?: string })?.message ?? ""} `, + }) + } + } + const onSubmit = async (data: FormSchema) => { + setStep("verifying") + try { + const emailVerifyAttempt = await emailObj?.attemptVerification({ code }) + + if (emailVerifyAttempt?.verification.status === "verified") { + updateEmail.mutate({ + userId: props.user_id, + oldEmail: (data.email ?? "").trim(), + newEmail: (data.new_email ?? "").trim(), + }) + toast({ + title: "Email updated", + description: "Your email has been updated successfully.", + }) + setStep("updated") + } + } catch (error) { + console.log("Update error", error) + toast({ + variant: "destructive", + title: "Failed to update email", + description: `An error occurred while trying to update email. ${(error as { message?: string })?.message ?? ""}`, + }) + setStep("enterCode") + } + } + + return ( + + {step === "submitForm" ? ( +
+
+ ( + + +

Email address

+

*

+
+ + + + +
+ )} + /> + ( + + +

New email address

+

*

+
+ + + + +
+ )} + /> +
+ +
+ ) : ( +
+ Enter one-time code from your email + setCode(e.target.value)} + required + /> + + +
+ )} +
+ ) +} + +export default EmailForm diff --git a/src/app/profile/settings/@email/page.tsx b/src/app/profile/settings/@email/page.tsx new file mode 100644 index 000000000..76898754c --- /dev/null +++ b/src/app/profile/settings/@email/page.tsx @@ -0,0 +1,27 @@ +import { Separator } from "~/components/ui/separator" + +import { api } from "~/trpc/server" + +import EmailForm from "./form" + +export default async function Socials() { + const data = await api.users.getCurrent.query() + const user_id = data.id + const email = { + email: data?.email ?? undefined, + } + + return ( +
+
+

Change your email

+

+ You may change your email address associated with your account here. An email will be sent to your new email + address. +

+
+ + +
+ ) +} diff --git a/src/app/profile/settings/@personal/form.tsx b/src/app/profile/settings/@personal/form.tsx index 8b65e4902..6b067ef70 100644 --- a/src/app/profile/settings/@personal/form.tsx +++ b/src/app/profile/settings/@personal/form.tsx @@ -1,14 +1,18 @@ "use client" import { zodResolver } from "@hookform/resolvers/zod" +import Link from "next/link" import { FormProvider, useForm } from "react-hook-form" +import { siDiscord } from "simple-icons" import { z } from "zod" +import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" import { Button } from "~/components/ui/button" import { Checkbox } from "~/components/ui/checkbox" import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "~/components/ui/form" import { Input } from "~/components/ui/input" import { RadioGroup, RadioGroupItem } from "~/components/ui/radio-group" +import { Separator } from "~/components/ui/separator" import { toast } from "~/components/ui/use-toast" import { PRONOUNS, UNIVERSITIES } from "~/lib/constants" @@ -37,6 +41,9 @@ const formSchema = z isUWA: z.boolean(), student_number: z.string().optional(), uni: z.string().optional(), + github: z.string().optional(), + discord: z.string().optional(), + subscribe: z.boolean(), }) .refine(({ isUWA, student_number }) => !Boolean(isUWA) || student_number, { message: "Student number is required", @@ -70,16 +77,44 @@ const PersonalForm = (props: { defaultValues?: Partial }) => { const form = useForm({ resolver: zodResolver(formSchema), - defaultValues: { ...props.defaultValues, uni: props.defaultValues?.uni ?? UNIVERSITIES[0].value }, + defaultValues: { ...props.defaultValues }, }) - const { getValues } = form + const { getValues, setError } = form - const onSubmit = (data: FormSchema) => { - updateUser.mutate({ - ...data, - student_number: !data.isUWA ? null : data.student_number, - uni: data.isUWA ? "UWA" : data.uni, - }) + const onSubmit = async (data: FormSchema) => { + if (data.github !== "") { + const { status: githubStatus } = await fetch(`https://api.github.com/users/${data.github}`) + + if (githubStatus !== 200) { + toast({ + variant: "destructive", + title: "Github username not found", + description: "The Github username not found. Please try again.", + }) + setError("github", { + type: "custom", + message: "Github username not found", + }) + return + } + } + try { + updateUser.mutate({ + ...data, + student_number: !data.isUWA ? null : data.student_number, + uni: data.isUWA ? "UWA" : data.uni, + }) + toast({ + title: "Update successful", + description: "Your personal details have been updated successfully.", + }) + } catch (err) { + toast({ + variant: "destructive", + title: "Failed to update user details", + description: "An error occurred while trying to update your personal details. Please try again later.", + }) + } } return ( @@ -90,13 +125,14 @@ const PersonalForm = (props: { defaultValues?: Partial }) => { name="email" render={({ field }) => ( - Email address + +

Email address

+

*

+
- - Email address cannot be updated right now. Please contact us if it needs to be changed. - + Email address can be updated in the `Email` tab.
)} @@ -106,7 +142,10 @@ const PersonalForm = (props: { defaultValues?: Partial }) => { name="name" render={({ field }) => ( - Full name + +

Full name

+

*

+
@@ -122,7 +161,10 @@ const PersonalForm = (props: { defaultValues?: Partial }) => { name="preferred_name" render={({ field }) => ( - Preferred name + +

Preferred name

+

*

+
@@ -136,7 +178,10 @@ const PersonalForm = (props: { defaultValues?: Partial }) => { name="pronouns" render={({ field }) => ( - Pronouns + +

Pronouns

+

*

+
}) => { name="student_number" render={({ field }) => ( - UWA student number + +

UWA student number

+

*

+
@@ -202,7 +250,10 @@ const PersonalForm = (props: { defaultValues?: Partial }) => { name="uni" render={({ field }) => ( - University + +

University

+

*

+
}) => {
)} /> +
+

Socials

+

+ These fields are optional but are required if you plan on applying for projects during the winter and + summer breaks. +

+
+ + + + {siDiscord.title} + + + Join our Discord! + + You can join our Discord server at{" "} + + + + ( + + Github username + + + + + Sign up at{" "} + + + + + )} + /> + ( + + Discord username + + + + + Sign up at{" "} + + + + + )} + /> + ( + + + + + I wish to receive emails about future CFC events + + + )} + />
- - - ( - - Github username - - - - - Sign up at{" "} - - - - - )} - /> - ( - - Discord username - - - - - Sign up at{" "} - - - - - )} - /> -
- - - - ) -} - -export default SocialsForm diff --git a/src/app/profile/settings/@socials/page.tsx b/src/app/profile/settings/@socials/page.tsx deleted file mode 100644 index a3cc8c9fa..000000000 --- a/src/app/profile/settings/@socials/page.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Separator } from "~/components/ui/separator" - -import { api } from "~/trpc/server" - -import SocialsForm from "./form" - -export default async function Socials() { - const data = await api.users.getCurrent.query() - - const defaultValues = { - github: data?.github ?? undefined, - discord: data?.discord ?? undefined, - } - - return ( -
-
-

Your socials

-

- These fields are optional but are required if you plan on applying for projects during the winter and summer - breaks. -

-
- - -
- ) -} diff --git a/src/app/profile/settings/layout.tsx b/src/app/profile/settings/layout.tsx index 74d4df561..85c1866ea 100644 --- a/src/app/profile/settings/layout.tsx +++ b/src/app/profile/settings/layout.tsx @@ -7,7 +7,7 @@ import { type PropsWithChildren } from "~/lib/types" interface UserSettingsLayoutProps extends PropsWithChildren { personal: React.ReactNode - socials: React.ReactNode + email: React.ReactNode appearance: React.ReactNode membership: React.ReactNode } @@ -16,9 +16,9 @@ const Layout = async ({ children, ...props }: UserSettingsLayoutProps) => { const sidebarItems = [ { text: "Personal", icon: "face", component: props.personal }, { - text: "Socials", - icon: "tag", - component: props.socials, + text: "Email", + icon: "mail", + component: props.email, }, { text: "Appearance", icon: "palette", component: props.appearance }, // { text: "Notifications", icon: "notifications", component: }, diff --git a/src/server/api/routers/users/index.ts b/src/server/api/routers/users/index.ts index 27d864f8f..c52fb16ff 100644 --- a/src/server/api/routers/users/index.ts +++ b/src/server/api/routers/users/index.ts @@ -1,18 +1,15 @@ -import { Client, Environment } from "square" - -import { env } from "~/env" import { createTRPCRouter } from "~/server/api/trpc" import { create } from "./create" import { get } from "./get" import { getCurrent } from "./get-current" import { update } from "./update" -import { updateSocials } from "./update-socials" +import { updateEmail } from "./update-email" export const usersRouter = createTRPCRouter({ create, getCurrent, get, update, - updateSocials, + updateEmail, }) diff --git a/src/server/api/routers/users/update-email.ts b/src/server/api/routers/users/update-email.ts index b31e8783c..372a0cf98 100644 --- a/src/server/api/routers/users/update-email.ts +++ b/src/server/api/routers/users/update-email.ts @@ -42,11 +42,23 @@ export const updateEmail = protectedRatedProcedure(Ratelimit.fixedWindow(4, "30s message: `Clerk user with id: ${input.userId} does not have a primary email address???`, }) + const updateClerkUserEmail = async (oldEmailAddressId: string, newEmailAddress: string) => { + const clerk = await clerkClient() + // TODO: cannot update + const nonPrimaryEmail = clerkUser.emailAddresses.find((e) => e.emailAddress == input.newEmail.toLowerCase()) + if (nonPrimaryEmail) { + await clerk.users.updateUser(user.clerk_id, { + primaryEmailAddressID: nonPrimaryEmail.id, + }) + // TODO: delete social account(for sso) + await clerk.emailAddresses.deleteEmailAddress(oldEmailAddressId) + } + } // Clerk will always return the lowercased email from its API if (clerkUser.primaryEmailAddress?.emailAddress !== input.oldEmail.toLowerCase()) throw new TRPCError({ code: "BAD_REQUEST", message: "Old email does not match" }) try { - await updateClerkUserEmail(clerkUser.id, clerkUser.primaryEmailAddressId, input.newEmail) + await updateClerkUserEmail(clerkUser.primaryEmailAddressId, input.newEmail) await ctx.db.update(User).set({ email: input.newEmail }).where(eq(User.id, input.userId)) } catch (err) { console.error(err) @@ -55,14 +67,3 @@ export const updateEmail = protectedRatedProcedure(Ratelimit.fixedWindow(4, "30s return user }) - -const updateClerkUserEmail = async (userId: string, oldEmailAddressId: string, newEmailAddress: string) => { - const clerk = await clerkClient() - await clerk.emailAddresses.createEmailAddress({ - userId, - emailAddress: newEmailAddress, - verified: true, - primary: true, - }) - await clerk.emailAddresses.deleteEmailAddress(oldEmailAddressId) -} diff --git a/src/server/api/routers/users/update-socials.ts b/src/server/api/routers/users/update-socials.ts deleted file mode 100644 index 63ec15eb0..000000000 --- a/src/server/api/routers/users/update-socials.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Ratelimit } from "@upstash/ratelimit" -import { eq } from "drizzle-orm" -import { z } from "zod" - -import { protectedRatedProcedure } from "~/server/api/trpc" -import { User } from "~/server/db/schema" - -export const updateSocials = protectedRatedProcedure(Ratelimit.fixedWindow(4, "30s")) - .input( - z.object({ - github: z.string().optional().nullish(), - discord: z.string().optional().nullish(), - }), - ) - .mutation(async ({ ctx, input }) => { - const currentUser = ctx.user - - const [user] = await ctx.db - .update(User) - .set({ - github: input.github?.trim(), - discord: input.discord?.trim(), - }) - .where(eq(User.id, currentUser.id)) - .returning() - - return user - }) From 813162bb5b7f0d0a2d5d4e33fbe0505752d56c84 Mon Sep 17 00:00:00 2001 From: ErikaKK <491649804@qq.com> Date: Sat, 13 Sep 2025 00:51:25 +0800 Subject: [PATCH 04/12] update profile and api --- pnpm-lock.yaml | 313 +++++++++---------- src/app/profile/[id]/profile.tsx | 38 ++- src/app/profile/settings/@email/form.tsx | 9 +- src/middleware.ts | 2 +- src/server/api/routers/users/update-email.ts | 21 +- 5 files changed, 195 insertions(+), 188 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4500ac097..f0c21372d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,7 +24,7 @@ importers: version: 5.46.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@clerk/nextjs': specifier: 6.31.8 - version: 6.31.8(next@14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 6.31.8(next@14.2.26(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@hookform/resolvers': specifier: 3.3.4 version: 3.3.4(react-hook-form@7.51.2(react@18.3.1)) @@ -96,7 +96,7 @@ importers: version: 0.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@sentry/nextjs': specifier: 9.13.0 - version: 9.13.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.101.3(esbuild@0.25.9)) + version: 9.13.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@14.2.26(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.101.3(esbuild@0.25.9)) '@square/web-sdk': specifier: 2.0.1 version: 2.0.1 @@ -117,7 +117,7 @@ importers: version: 10.45.2(@trpc/server@10.45.2) '@trpc/next': specifier: 10.45.2 - version: 10.45.2(@tanstack/react-query@4.36.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/react-query@10.45.2(@tanstack/react-query@4.36.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@trpc/server@10.45.2)(next@14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 10.45.2(@tanstack/react-query@4.36.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/react-query@10.45.2(@tanstack/react-query@4.36.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@trpc/server@10.45.2)(next@14.2.26(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@trpc/react-query': specifier: 10.45.2 version: 10.45.2(@tanstack/react-query@4.36.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -132,7 +132,7 @@ importers: version: 1.29.0 '@vercel/analytics': specifier: 1.2.2 - version: 1.2.2(next@14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 1.2.2(next@14.2.26(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) '@visx/visx': specifier: 3.10.2 version: 3.10.2(@react-spring/web@9.7.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -168,7 +168,7 @@ importers: version: 3.2.0 next: specifier: 14.2.26 - version: 14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.26(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-themes: specifier: 0.3.0 version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -310,10 +310,6 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - '@apimatic/authentication-adapters@0.5.12': resolution: {integrity: sha512-H0dVMsuiPRRS6qPSz42T8ktaOUBlvJgKSj5L+DKr4DwBEZaE6rZx4i5kuEdAUZ5oIuteupbGEx5hMJlPi6Y0XA==} engines: {node: '>=14.15.0 || >=16.0.0'} @@ -357,12 +353,12 @@ packages: resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.0': - resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} + '@babel/compat-data@7.28.4': + resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.3': - resolution: {integrity: sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==} + '@babel/core@7.28.4': + resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} engines: {node: '>=6.9.0'} '@babel/generator@7.28.3': @@ -399,29 +395,29 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.3': - resolution: {integrity: sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==} + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.3': - resolution: {integrity: sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==} + '@babel/parser@7.28.4': + resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/runtime@7.28.3': - resolution: {integrity: sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==} + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} '@babel/template@7.27.2': resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.3': - resolution: {integrity: sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==} + '@babel/traverse@7.28.4': + resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.2': - resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} + '@babel/types@7.28.4': + resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} engines: {node: '>=6.9.0'} '@base-org/account@2.0.1': @@ -486,9 +482,6 @@ packages: '@coinbase/wallet-sdk@4.3.0': resolution: {integrity: sha512-T3+SNmiCw4HzDm4we9wCHCxlP0pqCiwKe4sOwPH3YAK2KSKjxPRydKu6UQJrdONFVLG7ujXvbd/6ZqmvJb8rkw==} - '@clerk/types@4.84.1': - resolution: {integrity: sha512-0lLz3u8u0Ot5ZUObU+8JJLOeiHHnruShJMeLAHNryp1d5zANPQquOyagamxbkoV1K2lAf8ld3liobs3EBzll6Q==} - engines: {node: '>=18.17.0'} '@drizzle-team/brocli@0.10.2': resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} @@ -795,6 +788,9 @@ packages: '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -3494,9 +3490,6 @@ packages: domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} - dot-case@3.0.4: - resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} - dotenv@16.6.1: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} @@ -4620,8 +4613,8 @@ packages: encoding: optional: true - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + node-releases@2.0.20: + resolution: {integrity: sha512-7gK6zSXEH6neM212JgfYFXe+GmZQM+fia5SsusuBIUgnPheLFBmIPhtFoAQRj8/7wASYQnbDlHPVwY0BefoFgA==} nopt@7.2.1: resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} @@ -5121,6 +5114,9 @@ packages: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + regexp.prototype.flags@1.5.4: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} @@ -5449,8 +5445,8 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - swr@2.3.6: - resolution: {integrity: sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw==} + swr@2.3.4: + resolution: {integrity: sha512-bYd2lrhc+VarcpkgWclcUi92wYCpOgMws9Sd1hG1ntAu0NEy+14CbotuFjshBU2kt9rYj9TSmDcybpxpeTU1fg==} peerDependencies: react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -5508,8 +5504,8 @@ packages: tiny-warning@1.0.3: resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} - tinyglobby@0.2.14: - resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} tinyqueue@2.0.3: @@ -5791,11 +5787,6 @@ snapshots: '@alloc/quick-lru@5.2.0': {} - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 - '@apimatic/authentication-adapters@0.5.12': dependencies: '@apimatic/core-interfaces': 0.2.12 @@ -5873,20 +5864,20 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.28.0': {} + '@babel/compat-data@7.28.4': {} - '@babel/core@7.28.3': + '@babel/core@7.28.4': dependencies: - '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.27.1 '@babel/generator': 7.28.3 '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.3) - '@babel/helpers': 7.28.3 - '@babel/parser': 7.28.3 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.4 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.3 - '@babel/types': 7.28.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.1 gensync: 1.0.0-beta.2 @@ -5897,15 +5888,15 @@ snapshots: '@babel/generator@7.28.3': dependencies: - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.30 jsesc: 3.1.0 '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.28.0 + '@babel/compat-data': 7.28.4 '@babel/helper-validator-option': 7.27.1 browserslist: 4.25.4 lru-cache: 5.1.1 @@ -5915,17 +5906,17 @@ snapshots: '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.28.3 - '@babel/types': 7.28.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.3)': + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.3 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color @@ -5935,36 +5926,36 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.28.3': + '@babel/helpers@7.28.4': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 - '@babel/parser@7.28.3': + '@babel/parser@7.28.4': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 - '@babel/runtime@7.28.3': {} + '@babel/runtime@7.28.4': {} '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 - '@babel/traverse@7.28.3': + '@babel/traverse@7.28.4': dependencies: '@babel/code-frame': 7.27.1 '@babel/generator': 7.28.3 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.3 + '@babel/parser': 7.28.4 '@babel/template': 7.27.2 - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 debug: 4.4.1 transitivePeerDependencies: - supports-color - '@babel/types@7.28.2': + '@babel/types@7.28.4': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 @@ -6051,9 +6042,6 @@ snapshots: - zod '@clerk/clerk-react@5.46.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - tslib: 2.4.1 - - '@clerk/nextjs@5.2.10(next@14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@clerk/shared': 3.24.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@clerk/types': 4.84.1 @@ -6065,13 +6053,13 @@ snapshots: dependencies: '@clerk/types': 4.84.1 - '@clerk/nextjs@6.31.8(next@14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@clerk/nextjs@6.31.8(next@14.2.26(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@clerk/backend': 2.12.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@clerk/clerk-react': 5.46.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@clerk/shared': 3.24.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@clerk/types': 4.84.1 - next: 14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.26(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) server-only: 0.0.1 @@ -6084,7 +6072,7 @@ snapshots: glob-to-regexp: 0.4.1 js-cookie: 3.0.5 std-env: 3.9.0 - swr: 2.3.6(react@18.3.1) + swr: 2.3.4(react@18.3.1) optionalDependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -6093,7 +6081,7 @@ snapshots: dependencies: csstype: 3.1.3 - '@clerk/types@4.84.1': + '@coinbase/wallet-sdk@4.3.0': dependencies: '@noble/hashes': 1.8.0 clsx: 1.2.1 @@ -6121,7 +6109,7 @@ snapshots: '@emotion/babel-plugin@11.13.5': dependencies: '@babel/helper-module-imports': 7.27.1 - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@emotion/hash': 0.9.2 '@emotion/memoize': 0.9.0 '@emotion/serialize': 1.3.3 @@ -6170,7 +6158,7 @@ snapshots: '@emotion/react@11.11.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@emotion/babel-plugin': 11.13.5 '@emotion/cache': 11.11.0 '@emotion/serialize': 1.3.3 @@ -6186,7 +6174,7 @@ snapshots: '@emotion/react@11.14.0(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@emotion/babel-plugin': 11.13.5 '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 @@ -6392,6 +6380,11 @@ snapshots: '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/source-map@0.3.11': @@ -6765,17 +6758,17 @@ snapshots: '@radix-ui/number@1.0.1': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive@1.0.1': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive@1.1.3': {} '@radix-ui/react-accordion@1.1.2(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-collapsible': 1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -6793,7 +6786,7 @@ snapshots: '@radix-ui/react-alert-dialog@1.0.5(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -6808,7 +6801,7 @@ snapshots: '@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -6818,7 +6811,7 @@ snapshots: '@radix-ui/react-avatar@1.0.4(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -6831,7 +6824,7 @@ snapshots: '@radix-ui/react-checkbox@1.0.4(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -6848,7 +6841,7 @@ snapshots: '@radix-ui/react-collapsible@1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -6865,7 +6858,7 @@ snapshots: '@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -6878,7 +6871,7 @@ snapshots: '@radix-ui/react-compose-refs@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 react: 18.3.1 optionalDependencies: '@types/react': 18.3.3 @@ -6891,7 +6884,7 @@ snapshots: '@radix-ui/react-context@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 react: 18.3.1 optionalDependencies: '@types/react': 18.3.3 @@ -6904,7 +6897,7 @@ snapshots: '@radix-ui/react-dialog@1.0.5(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -6949,14 +6942,14 @@ snapshots: '@radix-ui/react-direction@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 react: 18.3.1 optionalDependencies: '@types/react': 18.3.3 '@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -6983,7 +6976,7 @@ snapshots: '@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -6999,7 +6992,7 @@ snapshots: '@radix-ui/react-focus-guards@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 react: 18.3.1 optionalDependencies: '@types/react': 18.3.3 @@ -7012,7 +7005,7 @@ snapshots: '@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -7035,7 +7028,7 @@ snapshots: '@radix-ui/react-hover-card@1.0.7(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -7053,7 +7046,7 @@ snapshots: '@radix-ui/react-id@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.3)(react@18.3.1) react: 18.3.1 optionalDependencies: @@ -7068,7 +7061,7 @@ snapshots: '@radix-ui/react-label@2.0.2(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -7078,7 +7071,7 @@ snapshots: '@radix-ui/react-menu@2.0.6(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -7105,7 +7098,7 @@ snapshots: '@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -7129,7 +7122,7 @@ snapshots: '@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@floating-ui/react-dom': 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -7148,7 +7141,7 @@ snapshots: '@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -7168,7 +7161,7 @@ snapshots: '@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.3)(react@18.3.1) react: 18.3.1 @@ -7189,7 +7182,7 @@ snapshots: '@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/react-slot': 1.0.2(@types/react@18.3.3)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -7208,7 +7201,7 @@ snapshots: '@radix-ui/react-radio-group@1.1.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -7227,7 +7220,7 @@ snapshots: '@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -7245,7 +7238,7 @@ snapshots: '@radix-ui/react-scroll-area@1.0.5(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/number': 1.0.1 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -7263,7 +7256,7 @@ snapshots: '@radix-ui/react-select@2.0.0(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/number': 1.0.1 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -7293,7 +7286,7 @@ snapshots: '@radix-ui/react-separator@1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -7303,7 +7296,7 @@ snapshots: '@radix-ui/react-slot@1.0.2(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) react: 18.3.1 optionalDependencies: @@ -7318,7 +7311,7 @@ snapshots: '@radix-ui/react-tabs@1.0.4(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-direction': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -7335,7 +7328,7 @@ snapshots: '@radix-ui/react-toast@1.1.5(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -7356,7 +7349,7 @@ snapshots: '@radix-ui/react-toggle-group@1.0.4(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-direction': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -7372,7 +7365,7 @@ snapshots: '@radix-ui/react-toggle@1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -7384,7 +7377,7 @@ snapshots: '@radix-ui/react-tooltip@1.0.7(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -7405,7 +7398,7 @@ snapshots: '@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 react: 18.3.1 optionalDependencies: '@types/react': 18.3.3 @@ -7418,7 +7411,7 @@ snapshots: '@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) react: 18.3.1 optionalDependencies: @@ -7441,7 +7434,7 @@ snapshots: '@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) react: 18.3.1 optionalDependencies: @@ -7456,7 +7449,7 @@ snapshots: '@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 react: 18.3.1 optionalDependencies: '@types/react': 18.3.3 @@ -7469,14 +7462,14 @@ snapshots: '@radix-ui/react-use-previous@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 react: 18.3.1 optionalDependencies: '@types/react': 18.3.3 '@radix-ui/react-use-rect@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/rect': 1.0.1 react: 18.3.1 optionalDependencies: @@ -7484,7 +7477,7 @@ snapshots: '@radix-ui/react-use-size@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.3)(react@18.3.1) react: 18.3.1 optionalDependencies: @@ -7492,7 +7485,7 @@ snapshots: '@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.23)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -7502,7 +7495,7 @@ snapshots: '@radix-ui/rect@1.0.1': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@react-email/body@0.1.0(react@18.3.1)': dependencies: @@ -7785,7 +7778,7 @@ snapshots: '@sentry/bundler-plugin-core@3.3.1': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@sentry/babel-plugin-component-annotate': 3.3.1 '@sentry/cli': 2.42.2 dotenv: 16.6.1 @@ -7839,7 +7832,7 @@ snapshots: '@sentry/core@9.13.0': {} - '@sentry/nextjs@9.13.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.101.3(esbuild@0.25.9))': + '@sentry/nextjs@9.13.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@14.2.26(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.101.3(esbuild@0.25.9))': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.37.0 @@ -7852,7 +7845,7 @@ snapshots: '@sentry/vercel-edge': 9.13.0 '@sentry/webpack-plugin': 3.3.1(webpack@5.101.3(esbuild@0.25.9)) chalk: 3.0.0 - next: 14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.26(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) resolve: 1.22.8 rollup: 4.35.0 stacktrace-parser: 0.1.11 @@ -7998,9 +7991,9 @@ snapshots: '@trivago/prettier-plugin-sort-imports@5.2.2(prettier@3.2.5)': dependencies: '@babel/generator': 7.28.3 - '@babel/parser': 7.28.3 - '@babel/traverse': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 javascript-natural-sort: 0.7.1 lodash: 4.17.21 prettier: 3.2.5 @@ -8011,13 +8004,13 @@ snapshots: dependencies: '@trpc/server': 10.45.2 - '@trpc/next@10.45.2(@tanstack/react-query@4.36.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/react-query@10.45.2(@tanstack/react-query@4.36.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@trpc/server@10.45.2)(next@14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@trpc/next@10.45.2(@tanstack/react-query@4.36.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/react-query@10.45.2(@tanstack/react-query@4.36.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@trpc/server@10.45.2)(next@14.2.26(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@tanstack/react-query': 4.36.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@trpc/client': 10.45.2(@trpc/server@10.45.2) '@trpc/react-query': 10.45.2(@tanstack/react-query@4.36.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@trpc/server': 10.45.2 - next: 14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.26(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -8355,11 +8348,11 @@ snapshots: '@use-gesture/core': 10.3.1 react: 18.3.1 - '@vercel/analytics@1.2.2(next@14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@vercel/analytics@1.2.2(next@14.2.26(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': dependencies: server-only: 0.0.1 optionalDependencies: - next: 14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.26(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 '@visx/annotation@3.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': @@ -9010,7 +9003,7 @@ snapshots: babel-plugin-macros@3.1.0: dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 cosmiconfig: 7.1.0 resolve: 1.22.10 @@ -9041,15 +9034,14 @@ snapshots: fill-range: 7.1.1 browser-tabs-lock@1.3.0: - browserslist@4.25.4: dependencies: lodash: 4.17.21 browserslist@4.25.4: dependencies: - caniuse-lite: 1.0.30001739 + caniuse-lite: 1.0.30001741 electron-to-chromium: 1.5.214 - node-releases: 2.0.19 + node-releases: 2.0.20 update-browserslist-db: 1.1.3(browserslist@4.25.4) buffer-from@1.1.2: {} @@ -9398,11 +9390,6 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 - dot-case@3.0.4: - dependencies: - no-case: 3.0.4 - tslib: 2.4.1 - dotenv@16.6.1: {} drizzle-kit@0.30.6: @@ -9649,7 +9636,7 @@ snapshots: get-tsconfig: 4.10.1 is-bun-module: 2.0.0 stable-hash: 0.0.5 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.4.0(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0) @@ -10484,10 +10471,6 @@ snapshots: dependencies: js-tokens: 4.0.0 - lower-case@2.0.2: - dependencies: - tslib: 2.4.1 - lru-cache@10.4.3: {} lru-cache@5.1.1: @@ -10502,8 +10485,6 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - map-obj@4.3.0: {} - mapbox-gl@3.2.0: dependencies: '@mapbox/geojson-rewind': 0.5.2 @@ -10625,7 +10606,7 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - next@14.2.26(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next@14.2.26(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@next/env': 14.2.26 '@swc/helpers': 0.5.5 @@ -10635,7 +10616,7 @@ snapshots: postcss: 8.4.31 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.1(@babel/core@7.28.3)(react@18.3.1) + styled-jsx: 5.1.1(@babel/core@7.28.4)(react@18.3.1) optionalDependencies: '@next/swc-darwin-arm64': 14.2.26 '@next/swc-darwin-x64': 14.2.26 @@ -10651,16 +10632,11 @@ snapshots: - '@babel/core' - babel-plugin-macros - no-case@3.0.4: - dependencies: - lower-case: 2.0.2 - tslib: 2.4.1 - node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 - node-releases@2.0.19: {} + node-releases@2.0.20: {} nopt@7.2.1: dependencies: @@ -11119,6 +11095,8 @@ snapshots: get-proto: 1.0.1 which-builtin-type: 1.2.1 + regenerator-runtime@0.14.1: {} + regexp.prototype.flags@1.5.4: dependencies: call-bind: 1.0.8 @@ -11348,17 +11326,6 @@ snapshots: is-fullwidth-code-point: 5.1.0 optional: true - snake-case@3.0.4: - dependencies: - dot-case: 3.0.4 - tslib: 2.4.1 - - snakecase-keys@5.4.4: - dependencies: - map-obj: 4.3.0 - snake-case: 3.0.4 - type-fest: 2.19.0 - snarkdown@2.0.0: {} source-map-js@1.2.1: {} @@ -11497,12 +11464,12 @@ snapshots: strip-json-comments@3.1.1: {} - styled-jsx@5.1.1(@babel/core@7.28.3)(react@18.3.1): + styled-jsx@5.1.1(@babel/core@7.28.4)(react@18.3.1): dependencies: client-only: 0.0.1 react: 18.3.1 optionalDependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 stylis@4.2.0: {} @@ -11534,7 +11501,7 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - swr@2.3.6(react@18.3.1): + swr@2.3.4(react@18.3.1): dependencies: dequal: 2.0.3 react: 18.3.1 @@ -11544,7 +11511,7 @@ snapshots: tailwind-merge@2.2.2: dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 tailwindcss-animate@1.0.7(tailwindcss@3.4.3): dependencies: @@ -11609,7 +11576,7 @@ snapshots: tiny-warning@1.0.3: {} - tinyglobby@0.2.14: + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 diff --git a/src/app/profile/[id]/profile.tsx b/src/app/profile/[id]/profile.tsx index 93b982e8b..98d27d7b0 100644 --- a/src/app/profile/[id]/profile.tsx +++ b/src/app/profile/[id]/profile.tsx @@ -1,7 +1,6 @@ "use client" import Link from "next/link" -import { useState } from "react" import { siDiscord, siGithub } from "simple-icons" import { Badge } from "~/components/ui/badge" @@ -9,6 +8,7 @@ import { Button } from "~/components/ui/button" import ProfilePageSkeleton from "~/app/_components/clients/ProfilePageSkeleton/page" import TitleText from "~/app/_components/title-text" +import { DashboardCard } from "~/app/dashboard/(root)/card" import { UNIVERSITIES } from "~/lib/constants" import { api } from "~/trpc/react" import { type RouterOutputs } from "~/trpc/shared" @@ -19,9 +19,10 @@ interface ProfilePageProps { } const ProfilePage = ({ id, currentUser }: ProfilePageProps) => { - const [isEditing, setIsEditing] = useState(false) const { data: user, refetch } = api.users.get.useQuery(id) - + const { isLoading: p1Loading, data: pastProjects } = api.projects.getProjectByUser.useQuery({ + user: user?.email ?? "", + }) let universityLabel: string | undefined if (user?.student_number?.length) { universityLabel = "University of Western Australia" @@ -36,7 +37,7 @@ const ProfilePage = ({ id, currentUser }: ProfilePageProps) => {
./profile
-
+
<>
@@ -102,17 +103,38 @@ const ProfilePage = ({ id, currentUser }: ProfilePageProps) => { )}
{currentUser?.id === user.id && ( -
+
- +
)}
-
+

Projects

-

Coming soon...

+
+
+ {p1Loading ? ( +

Loading...

+ ) : !pastProjects || pastProjects.length == 0 ? ( +

No past participated projects

+ ) : ( +
+ {pastProjects.map((project, index) => ( +
+ +
+ ))} +
+ )} +
+ + + +
+
+
diff --git a/src/app/profile/settings/@email/form.tsx b/src/app/profile/settings/@email/form.tsx index 743478d47..33c9568b5 100644 --- a/src/app/profile/settings/@email/form.tsx +++ b/src/app/profile/settings/@email/form.tsx @@ -106,7 +106,7 @@ const EmailForm = (props: { user_id: string; email?: Partial }) => { const emailVerifyAttempt = await emailObj?.attemptVerification({ code }) if (emailVerifyAttempt?.verification.status === "verified") { - updateEmail.mutate({ + await updateEmail.mutateAsync({ userId: props.user_id, oldEmail: (data.email ?? "").trim(), newEmail: (data.new_email ?? "").trim(), @@ -116,6 +116,13 @@ const EmailForm = (props: { user_id: string; email?: Partial }) => { description: "Your email has been updated successfully.", }) setStep("updated") + } else { + toast({ + variant: "destructive", + title: "Verification failed", + description: "The code you entered is incorrect. Please try again.", + }) + setStep("enterCode") } } catch (error) { console.log("Update error", error) diff --git a/src/middleware.ts b/src/middleware.ts index 30b14e19e..b5f7fa30e 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,4 +1,4 @@ -import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server" +import { auth, clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server" import { eq } from "drizzle-orm" import { db } from "./server/db" diff --git a/src/server/api/routers/users/update-email.ts b/src/server/api/routers/users/update-email.ts index 372a0cf98..4d4154fe6 100644 --- a/src/server/api/routers/users/update-email.ts +++ b/src/server/api/routers/users/update-email.ts @@ -44,13 +44,22 @@ export const updateEmail = protectedRatedProcedure(Ratelimit.fixedWindow(4, "30s const updateClerkUserEmail = async (oldEmailAddressId: string, newEmailAddress: string) => { const clerk = await clerkClient() - // TODO: cannot update const nonPrimaryEmail = clerkUser.emailAddresses.find((e) => e.emailAddress == input.newEmail.toLowerCase()) + const externalAccount = clerkUser.externalAccounts.find( + (account) => account.emailAddress === input.oldEmail.toLowerCase(), + ) + // if (externalAccount) { + // // TODO: correct this!! + // const mess = await clerk.users.deleteUserExternalAccount({ + // userId: user.clerk_id, + // externalAccountId: externalAccount.id, + // }) + // if (!mess.deleted) throw new TRPCError({ code: "BAD_REQUEST", message: "Delete external account failed" }) + // } if (nonPrimaryEmail) { await clerk.users.updateUser(user.clerk_id, { primaryEmailAddressID: nonPrimaryEmail.id, }) - // TODO: delete social account(for sso) await clerk.emailAddresses.deleteEmailAddress(oldEmailAddressId) } } @@ -59,11 +68,13 @@ export const updateEmail = protectedRatedProcedure(Ratelimit.fixedWindow(4, "30s throw new TRPCError({ code: "BAD_REQUEST", message: "Old email does not match" }) try { await updateClerkUserEmail(clerkUser.primaryEmailAddressId, input.newEmail) - await ctx.db.update(User).set({ email: input.newEmail }).where(eq(User.id, input.userId)) } catch (err) { - console.error(err) - throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Failed to update email" }) + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: err instanceof Error ? err.message : "Failed to update email", + }) } + await ctx.db.update(User).set({ email: input.newEmail }).where(eq(User.id, input.userId)) return user }) From 6bae49ff4680a4c390cf7c5093974af1849f90f6 Mon Sep 17 00:00:00 2001 From: ErikaKK <491649804@qq.com> Date: Wed, 17 Sep 2025 23:30:33 +0800 Subject: [PATCH 05/12] edit profile and settings views --- src/app/dashboard/(root)/page.tsx | 46 +---- src/app/profile/[id]/profile.tsx | 189 +++++++++--------- src/app/profile/settings/@email/form.tsx | 18 +- src/app/profile/settings/@email/page.tsx | 4 +- .../api/routers/admin/users/create-manual.ts | 5 +- 5 files changed, 120 insertions(+), 142 deletions(-) diff --git a/src/app/dashboard/(root)/page.tsx b/src/app/dashboard/(root)/page.tsx index a83c8897a..ae44dbf55 100644 --- a/src/app/dashboard/(root)/page.tsx +++ b/src/app/dashboard/(root)/page.tsx @@ -10,44 +10,14 @@ export default async function Dashboard() { return ( <> -
-
-

Hey, {user?.preferred_name}

-

- Here you can see our upcoming projects and the projects you have participated in. You can apply for new - projects here if we link the application form. -

-
- -
-
-
- {user?.role === null && ( -
-
-

Membership

-
-

- You're not a member with us yet. Become a paying member of Coders for Causes for just $5 a year - (ends on 31st Dec {new Date().getFullYear()}). There are many benefits to becoming a member which - include: -

-
    -
  • discounts to paid events such as industry nights
  • -
  • the ability to vote and run for committee positions
  • -
  • the ability to join our projects run during the winter and summer breaks.
  • -
-
-
- {getIsMembershipOpen() ? ( - - ) : ( -

- Memberships are temporarily closed for the new year. Please check back later. -

- )} -
- )} +
+

Hey, {user?.preferred_name}

+

+ Here you can see our upcoming projects and the projects you have participated in. You can apply for new + projects here if we link the application form. +

+
+
diff --git a/src/app/profile/[id]/profile.tsx b/src/app/profile/[id]/profile.tsx index 98d27d7b0..c97475abf 100644 --- a/src/app/profile/[id]/profile.tsx +++ b/src/app/profile/[id]/profile.tsx @@ -3,13 +3,14 @@ import Link from "next/link" import { siDiscord, siGithub } from "simple-icons" +import PaymentFormWrapper from "~/components/payment/online/wrapper" import { Badge } from "~/components/ui/badge" import { Button } from "~/components/ui/button" import ProfilePageSkeleton from "~/app/_components/clients/ProfilePageSkeleton/page" import TitleText from "~/app/_components/title-text" -import { DashboardCard } from "~/app/dashboard/(root)/card" import { UNIVERSITIES } from "~/lib/constants" +import { getIsMembershipOpen } from "~/lib/utils" import { api } from "~/trpc/react" import { type RouterOutputs } from "~/trpc/shared" @@ -20,9 +21,6 @@ interface ProfilePageProps { const ProfilePage = ({ id, currentUser }: ProfilePageProps) => { const { data: user, refetch } = api.users.get.useQuery(id) - const { isLoading: p1Loading, data: pastProjects } = api.projects.getProjectByUser.useQuery({ - user: user?.email ?? "", - }) let universityLabel: string | undefined if (user?.student_number?.length) { universityLabel = "University of Western Australia" @@ -37,103 +35,106 @@ const ProfilePage = ({ id, currentUser }: ProfilePageProps) => {
./profile
-
-
- <> -
- {user.role && {user.role}} -

{user.preferred_name}

-
-

{user.name}

{" "} - {user.pronouns && ( - <> -

·

-

{user.pronouns}

- - )} -
-
-
- {user.github && ( - - - - - {user.github} - - )} - {user.discord && ( - - - - - {user.discord} - - )} -
-
- {user.student_number && ( -
- badge -

{user.student_number}

-
- )} - {user.university ? ( -
- school -

{universityLabel}

-
- ) : ( -
- school -

The University of Western Australia

-
+
+
+
+ {user.role && {user.role}} +

{user.preferred_name}

+
+

{user.name}

{" "} + {user.pronouns && ( + <> +

·

+

{user.pronouns}

+ )}
- {currentUser?.id === user.id && ( -
- - - +
+
+ {user.github && ( + + + + + {user.github} + + )} + {user.discord && ( + + + + + {user.discord} + + )} +
+
+ {user.student_number && ( +
+ badge +

{user.student_number}

+
+ )} + {user.university ? ( +
+ school +

{universityLabel}

+
+ ) : ( +
+ school +

The University of Western Australia

)} - +
+ {currentUser?.id === user.id && ( +
+ + + +
+ )}
-
-

Projects

-
-
- {p1Loading ? ( -

Loading...

- ) : !pastProjects || pastProjects.length == 0 ? ( -

No past participated projects

- ) : ( -
- {pastProjects.map((project, index) => ( -
- -
- ))} +
+
+ {user?.role === null && ( +
+
+

Membership

+
+

+ You're not a member with us yet. Become a paying member of Coders for Causes for just $5 + a year (ends on 31st Dec {new Date().getFullYear()}). There are many benefits to becoming a + member which include: +

+
    +
  • discounts to paid events such as industry nights
  • +
  • the ability to vote and run for committee positions
  • +
  • the ability to join our projects run during the winter and summer breaks.
  • +
+
- )} -
- - - + {getIsMembershipOpen() ? ( + + ) : ( +

+ Memberships are temporarily closed for the new year. Please check back later. +

+ )}
-
+ )}
diff --git a/src/app/profile/settings/@email/form.tsx b/src/app/profile/settings/@email/form.tsx index 33c9568b5..27686164e 100644 --- a/src/app/profile/settings/@email/form.tsx +++ b/src/app/profile/settings/@email/form.tsx @@ -3,13 +3,12 @@ import { useReverification, useUser } from "@clerk/nextjs" import { EmailAddressResource } from "@clerk/types" import { zodResolver } from "@hookform/resolvers/zod" -import Link from "next/link" import { useEffect, useState } from "react" import { FormProvider, useForm } from "react-hook-form" import { z } from "zod" import { Button } from "~/components/ui/button" -import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "~/components/ui/form" +import { FormControl, FormField, FormItem, FormLabel, FormMessage } from "~/components/ui/form" import { Input } from "~/components/ui/input" import { toast } from "~/components/ui/use-toast" @@ -59,8 +58,12 @@ const EmailForm = (props: { user_id: string; email?: Partial }) => { } }, [countdown]) const updateEmail = api.users.updateEmail.useMutation({ - onSuccess: async () => { - await utils.users.getCurrent.refetch() + onError: (error) => { + toast({ + variant: "destructive", + title: "Failed to update email", + description: error.message, + }) }, }) @@ -111,11 +114,12 @@ const EmailForm = (props: { user_id: string; email?: Partial }) => { oldEmail: (data.email ?? "").trim(), newEmail: (data.new_email ?? "").trim(), }) + setStep("updated") toast({ title: "Email updated", description: "Your email has been updated successfully.", }) - setStep("updated") + window.location.reload() } else { toast({ variant: "destructive", @@ -150,7 +154,7 @@ const EmailForm = (props: { user_id: string; email?: Partial }) => {

*

- + @@ -179,7 +183,7 @@ const EmailForm = (props: { user_id: string; email?: Partial }) => { ) : (
- Enter one-time code from your email + Enter one-time code from your new email
- +
+ +
) } diff --git a/src/server/api/routers/admin/users/create-manual.ts b/src/server/api/routers/admin/users/create-manual.ts index 87a71c937..93d9a3cbc 100644 --- a/src/server/api/routers/admin/users/create-manual.ts +++ b/src/server/api/routers/admin/users/create-manual.ts @@ -46,9 +46,10 @@ export const createManual = adminProcedure ) .mutation(async ({ ctx, input }) => { let clerkRes: ClerkUser | undefined + const clerk = await clerkClient() // TODO: wrap in a transaction try { - clerkRes = await clerkClient().users.createUser({ + clerkRes = await clerk.users.createUser({ emailAddress: [input.email], firstName: input.name, // we treat clerk.firstName as the user's full name unsafeMetadata: { @@ -63,7 +64,7 @@ export const createManual = adminProcedure }) } catch (err) { // user might exist already - clerkRes = (await clerkClient().users.getUserList({ emailAddress: [input.email] })).data[0] + clerkRes = (await clerk.users.getUserList({ emailAddress: [input.email] })).data[0] } if (!clerkRes) From 00bd10628ac55210a8f5550d9d84dac381ae4c98 Mon Sep 17 00:00:00 2001 From: ErikaKK <491649804@qq.com> Date: Wed, 17 Sep 2025 23:33:37 +0800 Subject: [PATCH 06/12] delete import --- src/app/dashboard/(root)/page.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/app/dashboard/(root)/page.tsx b/src/app/dashboard/(root)/page.tsx index ae44dbf55..358404c21 100644 --- a/src/app/dashboard/(root)/page.tsx +++ b/src/app/dashboard/(root)/page.tsx @@ -1,6 +1,3 @@ -import PaymentFormWrapper from "~/components/payment/online/wrapper" - -import { getIsMembershipOpen } from "~/lib/utils" import { api } from "~/trpc/server" import DashboardContent from "./content" From 05f305a58ae54543c9254bb2d7a1c3e47c807f2c Mon Sep 17 00:00:00 2001 From: ErikaKK <491649804@qq.com> Date: Wed, 17 Sep 2025 23:47:28 +0800 Subject: [PATCH 07/12] fix error --- src/app/join/page.tsx | 12 ++++++++---- src/app/profile/settings/@email/form.tsx | 2 +- src/middleware.ts | 4 ++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/app/join/page.tsx b/src/app/join/page.tsx index 7fdd94216..361ecb2ec 100644 --- a/src/app/join/page.tsx +++ b/src/app/join/page.tsx @@ -65,10 +65,14 @@ export default function Join() { try { const attempt = await signIn.create({ identifier: email }) - const emailFactor = attempt.supportedFirstFactors.find((factor) => factor.strategy === "email_code") - + const emailFactor = attempt.supportedFirstFactors?.find((factor) => factor.strategy === "email_code") if (!emailFactor || emailFactor.strategy !== "email_code") { - throw new Error("Email code factor not supported") + toast({ + variant: "destructive", + title: "Error", + description: "Email code factor not supported", + }) + return } const si = await attempt.prepareFirstFactor({ @@ -79,7 +83,7 @@ export default function Join() { setStep("email") window.scrollTo({ top: 0, - behavior: "smooth", // smooth scrolling + behavior: "smooth", }) } catch (error) { const { errors = [] } = error as ClerkError diff --git a/src/app/profile/settings/@email/form.tsx b/src/app/profile/settings/@email/form.tsx index 27686164e..cf3220268 100644 --- a/src/app/profile/settings/@email/form.tsx +++ b/src/app/profile/settings/@email/form.tsx @@ -87,7 +87,7 @@ const EmailForm = (props: { user_id: string; email?: Partial }) => { const emailAddress = user.emailAddresses.find((a) => a.id === res?.id) setEmailObj(emailAddress) - emailAddress?.prepareVerification({ strategy: "email_code" }) + await emailAddress?.prepareVerification({ strategy: "email_code" }) setCountdown(60) setStep("enterCode") window.scrollTo({ diff --git a/src/middleware.ts b/src/middleware.ts index b5f7fa30e..d5b7acdfb 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -21,14 +21,14 @@ export default clerkMiddleware(async (auth, req) => { if (!adminRoles.includes(user?.role ?? "")) { // non-existent clerk role so we go to 404 page cleanly - auth.protect({ + await auth.protect({ role: "lmfaooo", }) } } if (isProtectedPage(req)) { - auth.protect() + await auth.protect() } if (isAuthPage(req) && clerkId) { From 237f90fd4150f412c189f861d237671dfee20462 Mon Sep 17 00:00:00 2001 From: ErikaKK <491649804@qq.com> Date: Thu, 18 Sep 2025 01:23:53 +0800 Subject: [PATCH 08/12] improve logic --- src/app/profile/[id]/page.tsx | 2 +- src/app/profile/[id]/profile.tsx | 79 +++++++++++++------ src/app/profile/settings/@email/form.tsx | 42 +++++++--- src/app/profile/settings/@personal/form.tsx | 2 +- .../api/routers/projects/get-projects.ts | 14 +++- src/server/api/routers/users/update-email.ts | 56 +++++++------ 6 files changed, 132 insertions(+), 63 deletions(-) diff --git a/src/app/profile/[id]/page.tsx b/src/app/profile/[id]/page.tsx index 6c90f0f55..cd6a6d5c6 100644 --- a/src/app/profile/[id]/page.tsx +++ b/src/app/profile/[id]/page.tsx @@ -6,7 +6,7 @@ import ProfilePage from "./profile" const Profile = async ({ params: { id } }: { params: { id: string } }) => { const currentUser = await api.users.getCurrent.query() - if (currentUser.id === id) { + if (currentUser) { return } diff --git a/src/app/profile/[id]/profile.tsx b/src/app/profile/[id]/profile.tsx index c97475abf..36cf407a6 100644 --- a/src/app/profile/[id]/profile.tsx +++ b/src/app/profile/[id]/profile.tsx @@ -9,6 +9,7 @@ import { Button } from "~/components/ui/button" import ProfilePageSkeleton from "~/app/_components/clients/ProfilePageSkeleton/page" import TitleText from "~/app/_components/title-text" +import { DashboardCard } from "~/app/dashboard/(root)/card" import { UNIVERSITIES } from "~/lib/constants" import { getIsMembershipOpen } from "~/lib/utils" import { api } from "~/trpc/react" @@ -21,6 +22,10 @@ interface ProfilePageProps { const ProfilePage = ({ id, currentUser }: ProfilePageProps) => { const { data: user, refetch } = api.users.get.useQuery(id) + const { isLoading: p1Loading, data: pastProjects } = api.projects.getProjectByUser.useQuery({ + user: user?.email ?? "", + isPublic: true, + }) let universityLabel: string | undefined if (user?.student_number?.length) { universityLabel = "University of Western Australia" @@ -36,7 +41,7 @@ const ProfilePage = ({ id, currentUser }: ProfilePageProps) => { ./profile
-
+
{user.role && {user.role}}

{user.preferred_name}

@@ -108,32 +113,58 @@ const ProfilePage = ({ id, currentUser }: ProfilePageProps) => { )}
-
- {user?.role === null && ( -
-
-

Membership

-
-

- You're not a member with us yet. Become a paying member of Coders for Causes for just $5 - a year (ends on 31st Dec {new Date().getFullYear()}). There are many benefits to becoming a - member which include: +

+ {currentUser?.id === user.id ? ( + user?.role === null && ( +
+
+

Membership

+
+

+ You're not a member with us yet. Become a paying member of Coders for Causes for just + $5 a year (ends on 31st Dec {new Date().getFullYear()}). There are many benefits to becoming + a member which include: +

+
    +
  • discounts to paid events such as industry nights
  • +
  • the ability to vote and run for committee positions
  • +
  • the ability to join our projects run during the winter and summer breaks.
  • +
+
+
+ {getIsMembershipOpen() ? ( + + ) : ( +

+ Memberships are temporarily closed for the new year. Please check back later.

-
    -
  • discounts to paid events such as industry nights
  • -
  • the ability to vote and run for committee positions
  • -
  • the ability to join our projects run during the winter and summer breaks.
  • -
+ )} +
+ ) + ) : ( + <> + {" "} +

Projects

+
+
+ {p1Loading ? ( +

Loading...

+ ) : !pastProjects || pastProjects.length == 0 ? ( +

+ This user has not participated in any projects +

+ ) : ( +
+ {pastProjects.map((project, index) => ( +
+ +
+ ))} +
+ )}
- {getIsMembershipOpen() ? ( - - ) : ( -

- Memberships are temporarily closed for the new year. Please check back later. -

- )} -
+ )}
diff --git a/src/app/profile/settings/@email/form.tsx b/src/app/profile/settings/@email/form.tsx index cf3220268..24f2ae4f8 100644 --- a/src/app/profile/settings/@email/form.tsx +++ b/src/app/profile/settings/@email/form.tsx @@ -3,6 +3,7 @@ import { useReverification, useUser } from "@clerk/nextjs" import { EmailAddressResource } from "@clerk/types" import { zodResolver } from "@hookform/resolvers/zod" +import { se } from "date-fns/locale" import { useEffect, useState } from "react" import { FormProvider, useForm } from "react-hook-form" import { z } from "zod" @@ -43,9 +44,10 @@ const defaultValues: FormSchema = { const EmailForm = (props: { user_id: string; email?: Partial }) => { const utils = api.useUtils() const { isLoaded, isSignedIn, user } = useUser() + const [step, setStep] = useState<"submitForm" | "enterCode" | "verifying" | "updated">("submitForm") const [countdown, setCountdown] = useState(0) + const [send, setSend] = useState(false) const [code, setCode] = useState("") - const [step, setStep] = useState<"submitForm" | "enterCode" | "verifying" | "updated">("submitForm") const [emailObj, setEmailObj] = useState() const createEmailAddress = useReverification((email: string) => user?.createEmailAddress({ email })) useEffect(() => { @@ -57,6 +59,19 @@ const EmailForm = (props: { user_id: string; email?: Partial }) => { return () => clearInterval(timer) } }, [countdown]) + + useEffect(() => { + if (step === "enterCode" && emailObj && send) { + emailObj.prepareVerification({ strategy: "email_code" }) + setSend(false) + setCountdown(60) + window.scrollTo({ + top: 0, + behavior: "smooth", + }) + } + }, [emailObj, step, send]) + const updateEmail = api.users.updateEmail.useMutation({ onError: (error) => { toast({ @@ -80,20 +95,25 @@ const EmailForm = (props: { user_id: string; email?: Partial }) => { } const sendOtp = async (values: FormSchema) => { + setSend(true) try { - const res = await createEmailAddress(values.new_email.trim()) - await user.reload() + if (values.new_email.trim() === user?.primaryEmailAddress?.emailAddress) { + toast({ + variant: "destructive", + title: "Error", + description: "New email must be different from current email", + }) + return + } - const emailAddress = user.emailAddresses.find((a) => a.id === res?.id) - setEmailObj(emailAddress) + if (!user.emailAddresses.some((e) => e.emailAddress === values.new_email.trim())) { + const res = await createEmailAddress(values.new_email.trim()) + await user.reload() + const emailAddress = user.emailAddresses.find((a) => a.id === res?.id) + setEmailObj(emailAddress) + } - await emailAddress?.prepareVerification({ strategy: "email_code" }) - setCountdown(60) setStep("enterCode") - window.scrollTo({ - top: 0, - behavior: "smooth", - }) } catch (error) { console.error(JSON.stringify(error, null, 2)) toast({ diff --git a/src/app/profile/settings/@personal/form.tsx b/src/app/profile/settings/@personal/form.tsx index 6b067ef70..430cd10d0 100644 --- a/src/app/profile/settings/@personal/form.tsx +++ b/src/app/profile/settings/@personal/form.tsx @@ -82,7 +82,7 @@ const PersonalForm = (props: { defaultValues?: Partial }) => { const { getValues, setError } = form const onSubmit = async (data: FormSchema) => { - if (data.github !== "") { + if (data.github && data.github !== "") { const { status: githubStatus } = await fetch(`https://api.github.com/users/${data.github}`) if (githubStatus !== 200) { diff --git a/src/server/api/routers/projects/get-projects.ts b/src/server/api/routers/projects/get-projects.ts index 29db470fd..4687a6a22 100644 --- a/src/server/api/routers/projects/get-projects.ts +++ b/src/server/api/routers/projects/get-projects.ts @@ -39,8 +39,18 @@ export const getProjectByName = publicRatedProcedure(Ratelimit.fixedWindow(60, " }) export const getProjectByUser = protectedRatedProcedure(Ratelimit.fixedWindow(60, "30s")) - .input(z.object({ user: z.string() })) + .input(z.object({ user: z.string(), isPublic: z.boolean().optional() })) .query(async ({ input, ctx }) => { + const conditions = [] + + if (input.user) { + conditions.push(arrayContains(Project.members, [input.user])) + } + + if (typeof input.isPublic === "boolean") { + conditions.push(eq(Project.is_public, input.isPublic)) + } + const projectData = await ctx.db.query.Project.findMany({ columns: { logo_path: true, @@ -60,7 +70,7 @@ export const getProjectByUser = protectedRatedProcedure(Ratelimit.fixedWindow(60 application_url: true, is_public: true, }, - where: input.user ? arrayContains(Project.members, [input.user]) : undefined, + where: conditions.length > 0 ? and(...conditions) : undefined, }) return projectData diff --git a/src/server/api/routers/users/update-email.ts b/src/server/api/routers/users/update-email.ts index 4d4154fe6..18a7c84ef 100644 --- a/src/server/api/routers/users/update-email.ts +++ b/src/server/api/routers/users/update-email.ts @@ -1,11 +1,12 @@ import { clerkClient } from "@clerk/nextjs/server" import { TRPCError } from "@trpc/server" import { Ratelimit } from "@upstash/ratelimit" -import { eq } from "drizzle-orm" +import { eq, sql } from "drizzle-orm" +import type { InferSelectModel } from "drizzle-orm" import { z } from "zod" import { protectedRatedProcedure } from "~/server/api/trpc" -import { User } from "~/server/db/schema" +import { Project, User } from "~/server/db/schema" export const updateEmail = protectedRatedProcedure(Ratelimit.fixedWindow(4, "30s")) .input(z.object({ userId: z.string(), oldEmail: z.string().email(), newEmail: z.string().email() })) @@ -42,39 +43,46 @@ export const updateEmail = protectedRatedProcedure(Ratelimit.fixedWindow(4, "30s message: `Clerk user with id: ${input.userId} does not have a primary email address???`, }) - const updateClerkUserEmail = async (oldEmailAddressId: string, newEmailAddress: string) => { - const clerk = await clerkClient() - const nonPrimaryEmail = clerkUser.emailAddresses.find((e) => e.emailAddress == input.newEmail.toLowerCase()) - const externalAccount = clerkUser.externalAccounts.find( - (account) => account.emailAddress === input.oldEmail.toLowerCase(), - ) - // if (externalAccount) { - // // TODO: correct this!! - // const mess = await clerk.users.deleteUserExternalAccount({ - // userId: user.clerk_id, - // externalAccountId: externalAccount.id, - // }) - // if (!mess.deleted) throw new TRPCError({ code: "BAD_REQUEST", message: "Delete external account failed" }) - // } - if (nonPrimaryEmail) { - await clerk.users.updateUser(user.clerk_id, { - primaryEmailAddressID: nonPrimaryEmail.id, - }) - await clerk.emailAddresses.deleteEmailAddress(oldEmailAddressId) - } - } // Clerk will always return the lowercased email from its API if (clerkUser.primaryEmailAddress?.emailAddress !== input.oldEmail.toLowerCase()) throw new TRPCError({ code: "BAD_REQUEST", message: "Old email does not match" }) + + // use for sso accounts + // const externalAccount = clerkUser.externalAccounts.find( + // (account) => account.emailAddress === input.oldEmail.toLowerCase(), + // ) + try { - await updateClerkUserEmail(clerkUser.primaryEmailAddressId, input.newEmail) + await updateClerkUserEmail(user, clerkUser.primaryEmailAddressId) } catch (err) { throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: err instanceof Error ? err.message : "Failed to update email", }) } + await ctx.db.update(User).set({ email: input.newEmail }).where(eq(User.id, input.userId)) + await ctx.db + .update(Project) + .set({ + members: sql`array_replace(${Project.members}, ${input.oldEmail}, ${input.newEmail})`, + }) + .where(sql`${input.oldEmail} = ANY(${Project.members})`) return user }) + +const updateClerkUserEmail = async (user: InferSelectModel, oldEmailAddressId: string) => { + const clerk = await clerkClient() + + // if (externalAccount) { + // // TODO: correct this!! Use for sso + // const mess = await clerk.users.deleteUserExternalAccount({ + // userId: user.clerk_id, + // externalAccountId: externalAccount.id, + // }) + // if (!mess.deleted) throw new TRPCError({ code: "BAD_REQUEST", message: "Delete external account failed" }) + // } + + await clerk.emailAddresses.deleteEmailAddress(oldEmailAddressId) +} From e3e5dd52cc501e15b397fb07977815247e427e4c Mon Sep 17 00:00:00 2001 From: ErikaKK <491649804@qq.com> Date: Thu, 18 Sep 2025 13:19:14 +0800 Subject: [PATCH 09/12] fix error and layout --- src/app/profile/[id]/profile.tsx | 2 +- src/app/profile/settings/@email/form.tsx | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/app/profile/[id]/profile.tsx b/src/app/profile/[id]/profile.tsx index 36cf407a6..c41d5856f 100644 --- a/src/app/profile/[id]/profile.tsx +++ b/src/app/profile/[id]/profile.tsx @@ -41,7 +41,7 @@ const ProfilePage = ({ id, currentUser }: ProfilePageProps) => { ./profile
-
+
{user.role && {user.role}}

{user.preferred_name}

diff --git a/src/app/profile/settings/@email/form.tsx b/src/app/profile/settings/@email/form.tsx index 24f2ae4f8..5dbfdbaf4 100644 --- a/src/app/profile/settings/@email/form.tsx +++ b/src/app/profile/settings/@email/form.tsx @@ -61,15 +61,16 @@ const EmailForm = (props: { user_id: string; email?: Partial }) => { }, [countdown]) useEffect(() => { - if (step === "enterCode" && emailObj && send) { - emailObj.prepareVerification({ strategy: "email_code" }) - setSend(false) - setCountdown(60) - window.scrollTo({ - top: 0, - behavior: "smooth", - }) + const run = async () => { + if (step === "enterCode" && emailObj && send) { + await emailObj.prepareVerification({ strategy: "email_code" }) + setSend(false) + setCountdown(60) + window.scrollTo({ top: 0, behavior: "smooth" }) + } } + + run() }, [emailObj, step, send]) const updateEmail = api.users.updateEmail.useMutation({ From edc9bf7b5736d1252dc9d4aac0f5bd34da588dff Mon Sep 17 00:00:00 2001 From: ErikaKK <491649804@qq.com> Date: Thu, 18 Sep 2025 14:03:56 +0800 Subject: [PATCH 10/12] add error handling --- src/app/profile/settings/@email/form.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/app/profile/settings/@email/form.tsx b/src/app/profile/settings/@email/form.tsx index 5dbfdbaf4..0f63628f5 100644 --- a/src/app/profile/settings/@email/form.tsx +++ b/src/app/profile/settings/@email/form.tsx @@ -63,7 +63,16 @@ const EmailForm = (props: { user_id: string; email?: Partial }) => { useEffect(() => { const run = async () => { if (step === "enterCode" && emailObj && send) { - await emailObj.prepareVerification({ strategy: "email_code" }) + await emailObj.prepareVerification({ strategy: "email_code" }).catch((e) => { + console.error(e) + toast({ + variant: "destructive", + title: "Error sending OTP", + description: `${(e as { message?: string })?.message ?? ""}`, + }) + setSend(false) + return + }) setSend(false) setCountdown(60) window.scrollTo({ top: 0, behavior: "smooth" }) From 167d4942f960694eff27395ea98727bfa4109fe7 Mon Sep 17 00:00:00 2001 From: ErikaKK <491649804@qq.com> Date: Thu, 18 Sep 2025 14:15:25 +0800 Subject: [PATCH 11/12] edit error handling --- src/app/profile/settings/@email/form.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/app/profile/settings/@email/form.tsx b/src/app/profile/settings/@email/form.tsx index 0f63628f5..b38958797 100644 --- a/src/app/profile/settings/@email/form.tsx +++ b/src/app/profile/settings/@email/form.tsx @@ -63,23 +63,26 @@ const EmailForm = (props: { user_id: string; email?: Partial }) => { useEffect(() => { const run = async () => { if (step === "enterCode" && emailObj && send) { - await emailObj.prepareVerification({ strategy: "email_code" }).catch((e) => { + try { + await emailObj.prepareVerification({ strategy: "email_code" }) + } catch (e) { console.error(e) toast({ variant: "destructive", title: "Error sending OTP", - description: `${(e as { message?: string })?.message ?? ""}`, + description: e instanceof Error ? e.message : "Unknown error", }) setSend(false) return - }) + } + setSend(false) setCountdown(60) window.scrollTo({ top: 0, behavior: "smooth" }) } } - run() + void run() }, [emailObj, step, send]) const updateEmail = api.users.updateEmail.useMutation({ @@ -133,6 +136,7 @@ const EmailForm = (props: { user_id: string; email?: Partial }) => { }) } } + const onSubmit = async (data: FormSchema) => { setStep("verifying") try { From 5686b4e6e2658df6d163e0ea816215ef01651007 Mon Sep 17 00:00:00 2001 From: ErikaKK <491649804@qq.com> Date: Fri, 19 Sep 2025 09:59:04 +0800 Subject: [PATCH 12/12] delete some unused code and comments. edit a bit --- src/app/profile/settings/@email/form.tsx | 1 - src/app/profile/settings/@email/page.tsx | 7 +++-- src/app/profile/settings/@personal/form.tsx | 31 ++++++++------------- src/server/api/routers/users/update.ts | 12 +------- 4 files changed, 17 insertions(+), 34 deletions(-) diff --git a/src/app/profile/settings/@email/form.tsx b/src/app/profile/settings/@email/form.tsx index b38958797..be9fa6599 100644 --- a/src/app/profile/settings/@email/form.tsx +++ b/src/app/profile/settings/@email/form.tsx @@ -3,7 +3,6 @@ import { useReverification, useUser } from "@clerk/nextjs" import { EmailAddressResource } from "@clerk/types" import { zodResolver } from "@hookform/resolvers/zod" -import { se } from "date-fns/locale" import { useEffect, useState } from "react" import { FormProvider, useForm } from "react-hook-form" import { z } from "zod" diff --git a/src/app/profile/settings/@email/page.tsx b/src/app/profile/settings/@email/page.tsx index 4b570919f..d2d77d1fd 100644 --- a/src/app/profile/settings/@email/page.tsx +++ b/src/app/profile/settings/@email/page.tsx @@ -15,9 +15,10 @@ export default async function Socials() {

Change your email

-

- You may change your email address associated with your account here. An email will be sent to your new email - address. +

+ You may change your email address associated with your account here. An email that contains verification code + will be sent to your new email address. It can take up to 10 minutes. Make sure to check your spam folder if + you can't find it. Resend the code if you need.

diff --git a/src/app/profile/settings/@personal/form.tsx b/src/app/profile/settings/@personal/form.tsx index 430cd10d0..128d2f12a 100644 --- a/src/app/profile/settings/@personal/form.tsx +++ b/src/app/profile/settings/@personal/form.tsx @@ -65,12 +65,17 @@ const PersonalForm = (props: { defaultValues?: Partial }) => { const updateUser = api.users.update.useMutation({ onSuccess: async () => { await utils.users.getCurrent.refetch() + toast({ + title: "Update successful", + description: "Your personal details have been updated successfully.", + }) }, + onError: () => { toast({ variant: "destructive", title: "Failed to update user details", - description: "An error occurred while trying to update your personal details. Please try again later.", + description: "An error occurred while trying to update your personal details. Please try again.", }) }, }) @@ -84,7 +89,6 @@ const PersonalForm = (props: { defaultValues?: Partial }) => { const onSubmit = async (data: FormSchema) => { if (data.github && data.github !== "") { const { status: githubStatus } = await fetch(`https://api.github.com/users/${data.github}`) - if (githubStatus !== 200) { toast({ variant: "destructive", @@ -98,23 +102,12 @@ const PersonalForm = (props: { defaultValues?: Partial }) => { return } } - try { - updateUser.mutate({ - ...data, - student_number: !data.isUWA ? null : data.student_number, - uni: data.isUWA ? "UWA" : data.uni, - }) - toast({ - title: "Update successful", - description: "Your personal details have been updated successfully.", - }) - } catch (err) { - toast({ - variant: "destructive", - title: "Failed to update user details", - description: "An error occurred while trying to update your personal details. Please try again later.", - }) - } + + updateUser.mutate({ + ...data, + student_number: !data.isUWA ? null : data.student_number, + uni: data.isUWA ? "UWA" : data.uni, + }) } return ( diff --git a/src/server/api/routers/users/update.ts b/src/server/api/routers/users/update.ts index 9060504f6..a6308a636 100644 --- a/src/server/api/routers/users/update.ts +++ b/src/server/api/routers/users/update.ts @@ -21,15 +21,6 @@ export const update = protectedRatedProcedure(Ratelimit.fixedWindow(4, "30s")) message: "Preferred name is required", }) .optional(), - // email: z - // .string() - // .email({ - // message: "Invalid email address", - // }) - // .min(2, { - // message: "Email is required", - // }) - // .optional(), pronouns: z .string() .min(2, { @@ -65,7 +56,7 @@ export const update = protectedRatedProcedure(Ratelimit.fixedWindow(4, "30s")) } catch (err: unknown) { // Narrow the error type if (err instanceof Error) { - throw new Error(`Failed to update user metadata: ${err?.message}`) + throw new Error(`Failed to update user metadata: ${err.message}`) } else { throw new Error("Failed to update user metadata: unknown error") } @@ -76,7 +67,6 @@ export const update = protectedRatedProcedure(Ratelimit.fixedWindow(4, "30s")) .set({ name: input.name?.trim(), preferred_name: input.preferred_name?.trim(), - // email: input.email?.trim(), pronouns: input.pronouns?.trim(), student_number: input.student_number?.trim() ?? null, university: input.uni?.trim() ?? null,