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 && (
-
- setIsEditing(true)}>
- {" "}
- Edit Profile{" "}
-
+
+
+ {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 && (
+
+
+ Edit Profile
+
+
+ )}
+ >
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" ? (
+
+ ) : (
+
+ )}
+
+ )
+}
+
+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{" "}
+
+
+ discord.codersforcauses.org
+
+
+
+
+ (
+
+ Github username
+
+
+
+
+ Sign up at{" "}
+
+
+ github.com/signup
+
+
+
+
+
+ )}
+ />
+ (
+
+ Discord username
+
+
+
+
+ Sign up at{" "}
+
+
+ discord.com/register
+
+
+
+
+
+ )}
+ />
+ (
+
+
+
+
+ I wish to receive emails about future CFC events
+
+
+ )}
+ />
{updateUser.isLoading ? "Updating your info" : "Update"}
diff --git a/src/app/profile/settings/@personal/page.tsx b/src/app/profile/settings/@personal/page.tsx
index 59a72f068..1b20dd76e 100644
--- a/src/app/profile/settings/@personal/page.tsx
+++ b/src/app/profile/settings/@personal/page.tsx
@@ -15,13 +15,16 @@ export default async function Personal() {
isUWA: !!data?.student_number,
student_number: data?.student_number ?? undefined,
uni: data?.university ?? undefined,
+ github: data?.github ?? undefined,
+ discord: data?.discord ?? undefined,
+ subscribe: data?.subscribe ?? true,
}
return (
Personal details
-
Update your personal details. All fields here are required.
+
Fields marked with * are required.
diff --git a/src/app/profile/settings/@socials/form.tsx b/src/app/profile/settings/@socials/form.tsx
deleted file mode 100644
index e2c0bb4e4..000000000
--- a/src/app/profile/settings/@socials/form.tsx
+++ /dev/null
@@ -1,146 +0,0 @@
-"use client"
-
-import { zodResolver } from "@hookform/resolvers/zod"
-import Link from "next/link"
-import * as React from "react"
-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 { 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({
- github: z.string().optional(),
- discord: z.string().optional(),
-})
-
-type FormSchema = z.infer
-
-const defaultValues: FormSchema = {
- github: "",
- discord: "",
-}
-
-const SocialsForm = (props: { defaultValues?: Partial }) => {
- const [loading, setLoading] = React.useState(false)
- const utils = api.useUtils()
- const updateSocials = api.users.updateSocials.useMutation({
- onSuccess: async () => {
- await utils.users.getCurrent.refetch()
- },
- })
-
- const form = useForm({
- resolver: zodResolver(formSchema),
- defaultValues: props.defaultValues ?? defaultValues,
- })
-
- const onSubmit = async (data: FormSchema) => {
- setLoading(true)
- try {
- if (data.github !== "") {
- const { status: githubStatus } = await fetch(`https://api.github.com/users/${data.github}`)
-
- if (githubStatus !== 200) {
- form.setError("github", {
- type: "custom",
- message: "Github username not found",
- })
- return
- }
- }
-
- updateSocials.mutate({
- github: (data.github ?? "").trim() || null,
- discord: (data.discord ?? "").trim() || null,
- })
- } catch (error) {
- console.log(error)
- toast({
- variant: "destructive",
- title: "Failed to update user socials",
- description: "An error occurred while trying to update your socials. Please try again later.",
- })
- } finally {
- setLoading(false)
- }
- }
-
- return (
-
-
-
- )
-}
-
-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 && (
-
+
- Edit Profile
+ Edit Profile
)}
>
-
+
Projects
-
Coming soon...
+
+
+ {p1Loading ? (
+
Loading...
+ ) : !pastProjects || pastProjects.length == 0 ? (
+
No past participated projects
+ ) : (
+
+ {pastProjects.map((project, index) => (
+
+
+
+ ))}
+
+ )}
+
+
+ View upcoming projects
+
+
+
+
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 && (
-
-
- Edit Profile
-
+
+
+ {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 && (
+
+
+ Edit Profile
+
+
+ )}
-
-
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.
+
+
- )}
-
-
-
View upcoming projects
-
+ {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 }) => {
) : (
-
+
+
+
)
}
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,