From 7e3382fb242d910ac4697eb77399b63d34c2efc3 Mon Sep 17 00:00:00 2001 From: Lorenzo Corallo Date: Mon, 19 Jan 2026 18:57:06 +0100 Subject: [PATCH 1/3] feat: move to OTP Login --- next.config.js => next.config.ts | 6 +- package.json | 21 +- pnpm-lock.yaml | 507 +++++++++--------- src/app/dashboard/(active)/account/page.tsx | 28 +- .../dashboard/(active)/account/set-name.tsx | 61 +++ .../dashboard/(active)/account/telegram.tsx | 6 +- .../dashboard/(active)/complete-profile.tsx | 17 + src/app/dashboard/(active)/page.tsx | 4 +- src/app/dashboard/layout.tsx | 17 +- src/app/layout.tsx | 11 +- src/app/login/can-i-access.tsx | 12 +- src/app/login/layout.tsx | 10 - .../login/{github.tsx => login-button.tsx} | 25 +- src/app/login/login-form.tsx | 202 +++++++ src/app/login/page.tsx | 26 +- src/app/login/success/page.tsx | 19 - src/app/onboarding/layout.tsx | 3 - src/app/onboarding/link/logout.tsx | 25 + src/app/onboarding/link/page.tsx | 7 +- src/app/onboarding/link/telegram.tsx | 7 +- src/app/onboarding/no-role/page.tsx | 43 ++ src/app/page.tsx | 15 +- src/components/header.tsx | 2 +- src/components/sidebar/nav-user.tsx | 59 +- src/components/ui/alert.tsx | 36 +- src/components/ui/dropdown-menu.tsx | 2 +- src/components/ui/input-otp.tsx | 71 +++ src/components/ui/sidebar.tsx | 12 +- src/components/user-card.tsx | 4 +- src/{env.js => env.ts} | 0 src/lib/auth.ts | 4 +- src/lib/trpc/client.tsx | 3 +- src/lib/utils.ts | 2 +- 33 files changed, 831 insertions(+), 436 deletions(-) rename next.config.js => next.config.ts (87%) create mode 100644 src/app/dashboard/(active)/account/set-name.tsx create mode 100644 src/app/dashboard/(active)/complete-profile.tsx delete mode 100644 src/app/login/layout.tsx rename src/app/login/{github.tsx => login-button.tsx} (52%) create mode 100644 src/app/login/login-form.tsx delete mode 100644 src/app/login/success/page.tsx create mode 100644 src/app/onboarding/link/logout.tsx create mode 100644 src/app/onboarding/no-role/page.tsx create mode 100644 src/components/ui/input-otp.tsx rename src/{env.js => env.ts} (100%) diff --git a/next.config.js b/next.config.ts similarity index 87% rename from next.config.js rename to next.config.ts index 36c4e71..7f58c0c 100644 --- a/next.config.js +++ b/next.config.ts @@ -2,7 +2,7 @@ * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful * for Docker builds. */ -import "./src/env.js"; +import "./src/env.ts"; /** @type {import("next").NextConfig} */ const config = { @@ -14,8 +14,8 @@ const config = { }, output: "standalone", experimental: { - reactCompiler: true - } + reactCompiler: true, + }, }; export default config; diff --git a/package.json b/package.json index a1b9cd7..a33c2ba 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "@hookform/resolvers": "^3.9.1", - "@polinetwork/backend": "^0.9.1", + "@polinetwork/backend": "file:../backend/package/dist/", "@radix-ui/react-alert-dialog": "^1.1.3", "@radix-ui/react-avatar": "^1.1.1", "@radix-ui/react-collapsible": "^1.1.1", @@ -30,19 +30,20 @@ "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.4", - "@t3-oss/env-nextjs": "^0.10.1", - "@tanstack/react-query": "^5.74.3", + "@t3-oss/env-nextjs": "^0.13.10", + "@tanstack/react-query": "^5.90.19", "@tanstack/react-table": "^8.21.2", - "@trpc/client": "11.1.1", - "@trpc/next": "11.1.1", - "@trpc/react-query": "11.1.1", - "@trpc/tanstack-react-query": "11.1.1", + "@trpc/client": "11.5.1", + "@trpc/next": "11.5.1", + "@trpc/react-query": "11.5.1", + "@trpc/tanstack-react-query": "11.5.1", "babel-plugin-react-compiler": "19.1.0-rc.2", - "better-auth": "^1.2.7", + "better-auth": "^1.4.15", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", "geist": "^1.3.0", + "input-otp": "^1.4.2", "lucide-react": "^0.525.0", "next": "^15.3.4", "next-themes": "^0.4.4", @@ -56,11 +57,11 @@ "tailwind-merge": "^3.0.1", "tailwind-scrollbar": "^4.0.2", "tailwindcss-animate": "^1.0.7", - "zod": "^3.23.3" + "zod": "^4.3.5" }, "devDependencies": { "@tailwindcss/postcss": "^4.1.4", - "@trpc/server": "11.1.1", + "@trpc/server": "11.5.1", "@types/eslint": "^8.56.10", "@types/node": "^20.14.10", "@types/react": "^18.3.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5105c8f..7f46898 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: specifier: ^3.9.1 version: 3.10.0(react-hook-form@7.55.0(react@18.3.1)) '@polinetwork/backend': - specifier: ^0.9.1 - version: 0.9.1 + specifier: file:../backend/package/dist/ + version: dist@file:../backend/package/dist '@radix-ui/react-alert-dialog': specifier: ^1.1.3 version: 1.1.6(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -51,32 +51,32 @@ importers: specifier: ^1.1.4 version: 1.1.8(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@t3-oss/env-nextjs': - specifier: ^0.10.1 - version: 0.10.1(typescript@5.7.3)(zod@3.24.2) + specifier: ^0.13.10 + version: 0.13.10(typescript@5.7.3)(zod@4.3.5) '@tanstack/react-query': - specifier: ^5.74.3 - version: 5.74.3(react@18.3.1) + specifier: ^5.90.19 + version: 5.90.19(react@18.3.1) '@tanstack/react-table': specifier: ^8.21.2 version: 8.21.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@trpc/client': - specifier: 11.1.1 - version: 11.1.1(@trpc/server@11.1.1(typescript@5.7.3))(typescript@5.7.3) + specifier: 11.5.1 + version: 11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3) '@trpc/next': - specifier: 11.1.1 - version: 11.1.1(@tanstack/react-query@5.74.3(react@18.3.1))(@trpc/client@11.1.1(@trpc/server@11.1.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/react-query@11.1.1(@tanstack/react-query@5.74.3(react@18.3.1))(@trpc/client@11.1.1(@trpc/server@11.1.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/server@11.1.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3))(@trpc/server@11.1.1(typescript@5.7.3))(next@15.3.4(babel-plugin-react-compiler@19.1.0-rc.2)(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)(typescript@5.7.3) + specifier: 11.5.1 + version: 11.5.1(@tanstack/react-query@5.90.19(react@18.3.1))(@trpc/client@11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/react-query@11.5.1(@tanstack/react-query@5.90.19(react@18.3.1))(@trpc/client@11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/server@11.5.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3))(@trpc/server@11.5.1(typescript@5.7.3))(next@15.3.4(babel-plugin-react-compiler@19.1.0-rc.2)(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)(typescript@5.7.3) '@trpc/react-query': - specifier: 11.1.1 - version: 11.1.1(@tanstack/react-query@5.74.3(react@18.3.1))(@trpc/client@11.1.1(@trpc/server@11.1.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/server@11.1.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + specifier: 11.5.1 + version: 11.5.1(@tanstack/react-query@5.90.19(react@18.3.1))(@trpc/client@11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/server@11.5.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) '@trpc/tanstack-react-query': - specifier: 11.1.1 - version: 11.1.1(@tanstack/react-query@5.74.3(react@18.3.1))(@trpc/client@11.1.1(@trpc/server@11.1.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/server@11.1.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + specifier: 11.5.1 + version: 11.5.1(@tanstack/react-query@5.90.19(react@18.3.1))(@trpc/client@11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/server@11.5.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) babel-plugin-react-compiler: specifier: 19.1.0-rc.2 version: 19.1.0-rc.2 better-auth: - specifier: ^1.2.7 - version: 1.2.7 + specifier: ^1.4.15 + version: 1.4.15(next@15.3.4(babel-plugin-react-compiler@19.1.0-rc.2)(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) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -89,6 +89,9 @@ importers: geist: specifier: ^1.3.0 version: 1.3.1(next@15.3.4(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + input-otp: + specifier: ^1.4.2 + version: 1.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) lucide-react: specifier: ^0.525.0 version: 0.525.0(react@18.3.1) @@ -129,15 +132,15 @@ importers: specifier: ^1.0.7 version: 1.0.7(tailwindcss@4.1.4) zod: - specifier: ^3.23.3 - version: 3.24.2 + specifier: ^4.3.5 + version: 4.3.5 devDependencies: '@tailwindcss/postcss': specifier: ^4.1.4 version: 4.1.4 '@trpc/server': - specifier: 11.1.1 - version: 11.1.1(typescript@5.7.3) + specifier: 11.5.1 + version: 11.5.1(typescript@5.7.3) '@types/eslint': specifier: ^8.56.10 version: 8.56.12 @@ -196,11 +199,26 @@ packages: resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} engines: {node: '>=6.9.0'} - '@better-auth/utils@0.2.4': - resolution: {integrity: sha512-ayiX87Xd5sCHEplAdeMgwkA0FgnXsEZBgDn890XHHwSWNqqRZDYOq3uj2Ei2leTv1I2KbG5HHn60Ah1i2JWZjQ==} + '@better-auth/core@1.4.15': + resolution: {integrity: sha512-uAvq8YA7SaS7v+TrvH/Kwt7LAJihzUqB3FX8VweDsqu3gn5t51M+Bve+V1vVWR9qBAtC6cN68V6b+scxZxDY4A==} + peerDependencies: + '@better-auth/utils': 0.3.0 + '@better-fetch/fetch': 1.1.21 + better-call: 1.1.8 + jose: ^6.1.0 + kysely: ^0.28.5 + nanostores: ^1.0.1 + + '@better-auth/telemetry@1.4.15': + resolution: {integrity: sha512-7NW/2PS4RN85rv+ozpAezP/kSLPZeWkxqcA6RA/CFXqWp2YR2e5q5E6Hym1qBgVBkoAQa3lWFdX3b+jEs+vvrQ==} + peerDependencies: + '@better-auth/core': 1.4.15 - '@better-fetch/fetch@1.1.18': - resolution: {integrity: sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA==} + '@better-auth/utils@0.3.0': + resolution: {integrity: sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw==} + + '@better-fetch/fetch@1.1.21': + resolution: {integrity: sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A==} '@emnapi/runtime@1.4.1': resolution: {integrity: sha512-LMshMVP0ZhACNjQNYXiU1iZJ6QCcv0lUdPDPugqGvCGXt5xtRVBPdtA0qU12pEXZzpWAhWlZYptfdAFq10DOVQ==} @@ -238,9 +256,6 @@ packages: '@floating-ui/utils@0.2.9': resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} - '@hexagon/base64@1.1.28': - resolution: {integrity: sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==} - '@hookform/resolvers@3.10.0': resolution: {integrity: sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==} peerDependencies: @@ -369,9 +384,6 @@ packages: cpu: [x64] os: [win32] - '@levischuck/tiny-cbor@0.2.11': - resolution: {integrity: sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==} - '@next/env@15.3.4': resolution: {integrity: sha512-ZkdYzBseS6UjYzz6ylVKPOK+//zLWvD6Ta+vpoye8cW11AjiQjGYVibF0xuvT4L0iJfAPfZLFidaEzAOywyOAQ==} @@ -426,12 +438,13 @@ packages: cpu: [x64] os: [win32] - '@noble/ciphers@0.6.0': - resolution: {integrity: sha512-mIbq/R9QXk5/cTfESb1OKtyFnk7oc1Om/8onA1158K9/OZUQFDEVy55jVTato+xmp3XX6F6Qh0zz0Nc1AxAlRQ==} + '@noble/ciphers@2.1.1': + resolution: {integrity: sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw==} + engines: {node: '>= 20.19.0'} - '@noble/hashes@1.7.1': - resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} - engines: {node: ^14.21.3 || >=16} + '@noble/hashes@2.0.1': + resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==} + engines: {node: '>= 20.19.0'} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -449,25 +462,6 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} - '@peculiar/asn1-android@2.3.16': - resolution: {integrity: sha512-a1viIv3bIahXNssrOIkXZIlI2ePpZaNmR30d4aBL99mu2rO+mT9D6zBsp7H6eROWGtmwv0Ionp5olJurIo09dw==} - - '@peculiar/asn1-ecc@2.3.15': - resolution: {integrity: sha512-/HtR91dvgog7z/WhCVdxZJ/jitJuIu8iTqiyWVgRE9Ac5imt2sT/E4obqIVGKQw7PIy+X6i8lVBoT6wC73XUgA==} - - '@peculiar/asn1-rsa@2.3.15': - resolution: {integrity: sha512-p6hsanvPhexRtYSOHihLvUUgrJ8y0FtOM97N5UEpC+VifFYyZa0iZ5cXjTkZoDwxJ/TTJ1IJo3HVTB2JJTpXvg==} - - '@peculiar/asn1-schema@2.3.15': - resolution: {integrity: sha512-QPeD8UA8axQREpgR5UTAfu2mqQmm97oUqahDtNdBcfj3qAnoXzFdQW+aNf/tD2WVXF8Fhmftxoj0eMIT++gX2w==} - - '@peculiar/asn1-x509@2.3.15': - resolution: {integrity: sha512-0dK5xqTqSLaxv1FHXIcd4Q/BZNuopg+u1l23hT9rOmQ1g4dNtw0g/RnEi+TboB0gOwGtrWn269v27cMgchFIIg==} - - '@polinetwork/backend@0.9.1': - resolution: {integrity: sha512-4kOE6WepZ6gLPqo/rXlfCiiGsTxOmvBy80idH98E0iFAseJqYX5PWLevinl3klsavo4et6G7HyYogw1ZRUGpTQ==} - engines: {node: '>=22.14.0', npm: '>=10.9.2'} - '@radix-ui/number@1.1.0': resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} @@ -1013,12 +1007,8 @@ packages: '@rushstack/eslint-patch@1.10.5': resolution: {integrity: sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A==} - '@simplewebauthn/browser@13.1.0': - resolution: {integrity: sha512-WuHZ/PYvyPJ9nxSzgHtOEjogBhwJfC8xzYkPC+rR/+8chl/ft4ngjiK8kSU5HtRJfczupyOh33b25TjYbvwAcg==} - - '@simplewebauthn/server@13.1.1': - resolution: {integrity: sha512-1hsLpRHfSuMB9ee2aAdh0Htza/X3f4djhYISrggqGe3xopNjOcePiSDkDDoPzDYaaMCrbqGP1H2TYU7bgL9PmA==} - engines: {node: '>=20.0.0'} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} @@ -1026,23 +1016,39 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - '@t3-oss/env-core@0.10.1': - resolution: {integrity: sha512-GcKZiCfWks5CTxhezn9k5zWX3sMDIYf6Kaxy2Gx9YEQftFcz8hDRN56hcbylyAO3t4jQnQ5ifLawINsNgCDpOg==} + '@t3-oss/env-core@0.13.10': + resolution: {integrity: sha512-NNFfdlJ+HmPHkLi2HKy7nwuat9SIYOxei9K10lO2YlcSObDILY7mHZNSHsieIM3A0/5OOzw/P/b+yLvPdaG52g==} peerDependencies: + arktype: ^2.1.0 typescript: '>=5.0.0' - zod: ^3.0.0 + valibot: ^1.0.0-beta.7 || ^1.0.0 + zod: ^3.24.0 || ^4.0.0 peerDependenciesMeta: + arktype: + optional: true typescript: optional: true + valibot: + optional: true + zod: + optional: true - '@t3-oss/env-nextjs@0.10.1': - resolution: {integrity: sha512-iy2qqJLnFh1RjEWno2ZeyTu0ufomkXruUsOZludzDIroUabVvHsrSjtkHqwHp1/pgPUzN3yBRHMILW162X7x2Q==} + '@t3-oss/env-nextjs@0.13.10': + resolution: {integrity: sha512-JfSA2WXOnvcc/uMdp31paMsfbYhhdvLLRxlwvrnlPE9bwM/n0Z+Qb9xRv48nPpvfMhOrkrTYw1I5Yc06WIKBJQ==} peerDependencies: + arktype: ^2.1.0 typescript: '>=5.0.0' - zod: ^3.0.0 + valibot: ^1.0.0-beta.7 || ^1.0.0 + zod: ^3.24.0 || ^4.0.0 peerDependenciesMeta: + arktype: + optional: true typescript: optional: true + valibot: + optional: true + zod: + optional: true '@tailwindcss/node@4.1.4': resolution: {integrity: sha512-MT5118zaiO6x6hNA04OWInuAiP1YISXql8Z+/Y8iisV5nuhM8VXlyhRuqc2PEviPszcXI66W44bCIk500Oolhw==} @@ -1132,11 +1138,11 @@ packages: '@tailwindcss/postcss@4.1.4': resolution: {integrity: sha512-bjV6sqycCEa+AQSt2Kr7wpGF1bOZJ5wsqnLEkqSbM/JEHxx/yhMH8wHmdkPyApF9xhHeMSwnnkDUUMMM/hYnXw==} - '@tanstack/query-core@5.74.3': - resolution: {integrity: sha512-Mqk+5o3qTuAiZML248XpNH8r2cOzl15+LTbUsZQEwvSvn1GU4VQhvqzAbil36p+MBxpr/58oBSnRzhrBevDhfg==} + '@tanstack/query-core@5.90.19': + resolution: {integrity: sha512-GLW5sjPVIvH491VV1ufddnfldyVB+teCnpPIvweEfkpRx7CfUmUGhoh9cdcUKBh/KwVxk22aNEDxeTsvmyB/WA==} - '@tanstack/react-query@5.74.3': - resolution: {integrity: sha512-QrycUn0wxjVPzITvQvOxFRdhlAwIoOQSuav7qWD4SWCoKCdLbyRZ2vji2GuBq/glaxbF4wBx3fqcYRDOt8KDTA==} + '@tanstack/react-query@5.90.19': + resolution: {integrity: sha512-qTZRZ4QyTzQc+M0IzrbKHxSeISUmRB3RPGmao5bT+sI6ayxSRhn0FXEnT5Hg3as8SBFcRosrXXRFB+yAcxVxJQ==} peerDependencies: react: ^18 || ^19 @@ -1151,19 +1157,19 @@ packages: resolution: {integrity: sha512-uvXk/U4cBiFMxt+p9/G7yUWI/UbHYbyghLCjlpWZ3mLeIZiUBSKcUnw9UnKkdRz7Z/N4UBuFLWQdJCjUe7HjvA==} engines: {node: '>=12'} - '@trpc/client@11.1.1': - resolution: {integrity: sha512-Beiv56hlt+ARbijtUsCQOvKJw/FkfuVk4W9TzJl285iIJw8iCA3ZTwTG9GjIeSgeR7d2MyOeUbI26LV255n33A==} + '@trpc/client@11.5.1': + resolution: {integrity: sha512-7I6JJ1I1lxv3S87ht3FAIZi0XxQa7hnQ9K+Oo5BH7cGO8ZtWe9Ftq6ItdkuDfpsnsRPcR2h158AMWbNs/iptqg==} peerDependencies: - '@trpc/server': 11.1.1 + '@trpc/server': 11.5.1 typescript: '>=5.7.2' - '@trpc/next@11.1.1': - resolution: {integrity: sha512-4uz4S+QtpVGPENm4Kp7HSvMW+nXocBvI+A+dkwVvM3R9MIU3lIQ35H1fQ1aN/O+PlR4LOs5FceCwC1LjWkScZQ==} + '@trpc/next@11.5.1': + resolution: {integrity: sha512-MWb7Jqa0XYx0z2he7Eq7HSILc2z9oDqUtsbWz+S6LWCemewRSRzmG8UuoiZm+ICEoRa452RmqeQH1EYiPCWTUg==} peerDependencies: '@tanstack/react-query': ^5.59.15 - '@trpc/client': 11.1.1 - '@trpc/react-query': 11.1.1 - '@trpc/server': 11.1.1 + '@trpc/client': 11.5.1 + '@trpc/react-query': 11.5.1 + '@trpc/server': 11.5.1 next: '*' react: '>=16.8.0' react-dom: '>=16.8.0' @@ -1174,27 +1180,27 @@ packages: '@trpc/react-query': optional: true - '@trpc/react-query@11.1.1': - resolution: {integrity: sha512-TzKwTBsohUS23vusIIYJpFVp1p1rjEryTv9YCRnHtR6oDxR7W0aLh/TYyQ4chAhQ9edmdhVKeUetAQpF5HvUTA==} + '@trpc/react-query@11.5.1': + resolution: {integrity: sha512-Y+e0Lp7na1LAjoYqtHVjxpfayOuvvnuchS723b4dsI/V0h3irBuI+bx38S3m7RgaIh/L1av1SldFrKIauk+r/A==} peerDependencies: - '@tanstack/react-query': ^5.67.1 - '@trpc/client': 11.1.1 - '@trpc/server': 11.1.1 + '@tanstack/react-query': ^5.80.3 + '@trpc/client': 11.5.1 + '@trpc/server': 11.5.1 react: '>=18.2.0' react-dom: '>=18.2.0' typescript: '>=5.7.2' - '@trpc/server@11.1.1': - resolution: {integrity: sha512-ZjPN3ypBHvGMAlMgeZPrxlRcH/3dn4AK0s5Ph1z+E6uiAvIQVCj7ZoMlXeeBsIy4THGDAk953jHVW2kMnlbb4g==} + '@trpc/server@11.5.1': + resolution: {integrity: sha512-KIDzHRS5m8U1ncPwjgtOtPWK9lNO0kYL7b+lnvKXRqowSAQIEC/z6y7g/dkt4Aqv3DKI/STLydt2/afrP1QrxQ==} peerDependencies: typescript: '>=5.7.2' - '@trpc/tanstack-react-query@11.1.1': - resolution: {integrity: sha512-yLEGAixJk4f4epjuilhO2wZV/u6/Gwl0rjTep1C71HcLB09kJdphACf2c8z2fGnKpAbMDSlg3YXzR92eItkR4w==} + '@trpc/tanstack-react-query@11.5.1': + resolution: {integrity: sha512-1irzKOXhasMq09pHvLqJPTTwaEULIoNfFtoeLNkLnOVHLGvfHkS9qvpVjinRyW1aiQi7OqFDeaUDrQhtbP6tVA==} peerDependencies: - '@tanstack/react-query': ^5.67.1 - '@trpc/client': 11.1.1 - '@trpc/server': 11.1.1 + '@tanstack/react-query': ^5.80.3 + '@trpc/client': 11.5.1 + '@trpc/server': 11.5.1 react: '>=18.2.0' react-dom: '>=18.2.0' typescript: '>=5.7.2' @@ -1342,10 +1348,6 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} - asn1js@3.0.6: - resolution: {integrity: sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==} - engines: {node: '>=12.0.0'} - ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} @@ -1371,11 +1373,75 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - better-auth@1.2.7: - resolution: {integrity: sha512-2hCB263GSrgetsMUZw8vv9O1e4S4AlYJW3P4e8bX9u3Q3idv4u9BzDFCblpTLuL4YjYovghMCN0vurAsctXOAQ==} + better-auth@1.4.15: + resolution: {integrity: sha512-XZr4GnFPbjvf8wip8AAjTrpGNn3Sba600zT+DgsR3NNCMWCt9aD8+nuRah6BHwHWnVP1nfnby07tPmti72SRBw==} + peerDependencies: + '@lynx-js/react': '*' + '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 + '@sveltejs/kit': ^2.0.0 + '@tanstack/react-start': ^1.0.0 + '@tanstack/solid-start': ^1.0.0 + better-sqlite3: ^12.0.0 + drizzle-kit: '>=0.31.4' + drizzle-orm: '>=0.41.0' + mongodb: ^6.0.0 || ^7.0.0 + mysql2: ^3.0.0 + next: ^14.0.0 || ^15.0.0 || ^16.0.0 + pg: ^8.0.0 + prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + solid-js: ^1.0.0 + svelte: ^4.0.0 || ^5.0.0 + vitest: ^2.0.0 || ^3.0.0 || ^4.0.0 + vue: ^3.0.0 + peerDependenciesMeta: + '@lynx-js/react': + optional: true + '@prisma/client': + optional: true + '@sveltejs/kit': + optional: true + '@tanstack/react-start': + optional: true + '@tanstack/solid-start': + optional: true + better-sqlite3: + optional: true + drizzle-kit: + optional: true + drizzle-orm: + optional: true + mongodb: + optional: true + mysql2: + optional: true + next: + optional: true + pg: + optional: true + prisma: + optional: true + react: + optional: true + react-dom: + optional: true + solid-js: + optional: true + svelte: + optional: true + vitest: + optional: true + vue: + optional: true - better-call@1.0.8: - resolution: {integrity: sha512-/PV8JLqDRUN7JyBPbklVsS/8E4SO3pnf8hbpa8B7xrBrr+BBYpeOAxoqtnsyk/pRs35vNB4MZx8cn9dBuNlLDA==} + better-call@1.1.8: + resolution: {integrity: sha512-XMQ2rs6FNXasGNfMjzbyroSwKwYbZ/T3IxruSS6U2MJRsSYh3wYtG3o6H00ZlKZ/C/UPOAD97tqgQJNsxyeTXw==} + peerDependencies: + zod: ^4.0.0 + peerDependenciesMeta: + zod: + optional: true brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -1511,6 +1577,9 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + dist@file:../backend/package/dist: + resolution: {directory: ../backend/package/dist, type: directory} + doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -1842,6 +1911,12 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + 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'} @@ -1970,8 +2045,8 @@ packages: resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true - jose@5.9.6: - resolution: {integrity: sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==} + jose@6.1.3: + resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2000,9 +2075,9 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kysely@0.27.6: - resolution: {integrity: sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ==} - engines: {node: '>=14.0.0'} + kysely@0.28.10: + resolution: {integrity: sha512-ksNxfzIW77OcZ+QWSAPC7yDqUSaIVwkTWnTPNiIy//vifNbwsSgQ57OkkncHxxpcBHM3LRfLAZVEh7kjq5twVA==} + engines: {node: '>=20.0.0'} language-subtag-registry@0.3.23: resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} @@ -2130,9 +2205,9 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanostores@0.11.4: - resolution: {integrity: sha512-k1oiVNN4hDK8NcNERSZLQiMfRzEGtfnvZvdBvey3SQbgn8Dcrk0h1I6vpxApjb10PFUflZrgJ2WEZyJQ+5v7YQ==} - engines: {node: ^18.0.0 || >=20.0.0} + nanostores@1.1.0: + resolution: {integrity: sha512-yJBmDJr18xy47dbNVlHcgdPrulSn1nhSE6Ns9vTG+Nx9VPT6iV1MD6aQFp/t52zpf82FhLLTXAXr30NuCnxvwA==} + engines: {node: ^20.0.0 || >=22.0.0} natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -2337,13 +2412,6 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - pvtsutils@1.3.6: - resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} - - pvutils@1.1.3: - resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==} - engines: {node: '>=6.0.0'} - queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -2428,8 +2496,8 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rou3@0.5.1: - resolution: {integrity: sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ==} + rou3@0.7.12: + resolution: {integrity: sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg==} run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -2657,18 +2725,10 @@ packages: engines: {node: '>=14.17'} hasBin: true - typescript@5.8.3: - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} - engines: {node: '>=14.17'} - hasBin: true - unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} - uncrypto@0.1.3: - resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} - undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} @@ -2727,8 +2787,8 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - zod@3.24.2: - resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} + zod@4.3.5: + resolution: {integrity: sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==} snapshots: @@ -2743,12 +2803,26 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@better-auth/utils@0.2.4': + '@better-auth/core@1.4.15(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.5))(jose@6.1.3)(kysely@0.28.10)(nanostores@1.1.0)': dependencies: - typescript: 5.8.3 - uncrypto: 0.1.3 + '@better-auth/utils': 0.3.0 + '@better-fetch/fetch': 1.1.21 + '@standard-schema/spec': 1.1.0 + better-call: 1.1.8(zod@4.3.5) + jose: 6.1.3 + kysely: 0.28.10 + nanostores: 1.1.0 + zod: 4.3.5 - '@better-fetch/fetch@1.1.18': {} + '@better-auth/telemetry@1.4.15(@better-auth/core@1.4.15(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.5))(jose@6.1.3)(kysely@0.28.10)(nanostores@1.1.0))': + dependencies: + '@better-auth/core': 1.4.15(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.5))(jose@6.1.3)(kysely@0.28.10)(nanostores@1.1.0) + '@better-auth/utils': 0.3.0 + '@better-fetch/fetch': 1.1.21 + + '@better-auth/utils@0.3.0': {} + + '@better-fetch/fetch@1.1.21': {} '@emnapi/runtime@1.4.1': dependencies: @@ -2795,8 +2869,6 @@ snapshots: '@floating-ui/utils@0.2.9': {} - '@hexagon/base64@1.1.28': {} - '@hookform/resolvers@3.10.0(react-hook-form@7.55.0(react@18.3.1))': dependencies: react-hook-form: 7.55.0(react@18.3.1) @@ -2891,8 +2963,6 @@ snapshots: '@img/sharp-win32-x64@0.34.1': optional: true - '@levischuck/tiny-cbor@0.2.11': {} - '@next/env@15.3.4': {} '@next/eslint-plugin-next@15.3.4': @@ -2923,9 +2993,9 @@ snapshots: '@next/swc-win32-x64-msvc@15.3.4': optional: true - '@noble/ciphers@0.6.0': {} + '@noble/ciphers@2.1.1': {} - '@noble/hashes@1.7.1': {} + '@noble/hashes@2.0.1': {} '@nodelib/fs.scandir@2.1.5': dependencies: @@ -2941,41 +3011,6 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} - '@peculiar/asn1-android@2.3.16': - dependencies: - '@peculiar/asn1-schema': 2.3.15 - asn1js: 3.0.6 - tslib: 2.8.1 - - '@peculiar/asn1-ecc@2.3.15': - dependencies: - '@peculiar/asn1-schema': 2.3.15 - '@peculiar/asn1-x509': 2.3.15 - asn1js: 3.0.6 - tslib: 2.8.1 - - '@peculiar/asn1-rsa@2.3.15': - dependencies: - '@peculiar/asn1-schema': 2.3.15 - '@peculiar/asn1-x509': 2.3.15 - asn1js: 3.0.6 - tslib: 2.8.1 - - '@peculiar/asn1-schema@2.3.15': - dependencies: - asn1js: 3.0.6 - pvtsutils: 1.3.6 - tslib: 2.8.1 - - '@peculiar/asn1-x509@2.3.15': - dependencies: - '@peculiar/asn1-schema': 2.3.15 - asn1js: 3.0.6 - pvtsutils: 1.3.6 - tslib: 2.8.1 - - '@polinetwork/backend@0.9.1': {} - '@radix-ui/number@1.1.0': {} '@radix-ui/primitive@1.1.1': {} @@ -3498,17 +3533,7 @@ snapshots: '@rushstack/eslint-patch@1.10.5': {} - '@simplewebauthn/browser@13.1.0': {} - - '@simplewebauthn/server@13.1.1': - dependencies: - '@hexagon/base64': 1.1.28 - '@levischuck/tiny-cbor': 0.2.11 - '@peculiar/asn1-android': 2.3.16 - '@peculiar/asn1-ecc': 2.3.15 - '@peculiar/asn1-rsa': 2.3.15 - '@peculiar/asn1-schema': 2.3.15 - '@peculiar/asn1-x509': 2.3.15 + '@standard-schema/spec@1.1.0': {} '@swc/counter@0.1.3': {} @@ -3516,18 +3541,17 @@ snapshots: dependencies: tslib: 2.8.1 - '@t3-oss/env-core@0.10.1(typescript@5.7.3)(zod@3.24.2)': - dependencies: - zod: 3.24.2 + '@t3-oss/env-core@0.13.10(typescript@5.7.3)(zod@4.3.5)': optionalDependencies: typescript: 5.7.3 + zod: 4.3.5 - '@t3-oss/env-nextjs@0.10.1(typescript@5.7.3)(zod@3.24.2)': + '@t3-oss/env-nextjs@0.13.10(typescript@5.7.3)(zod@4.3.5)': dependencies: - '@t3-oss/env-core': 0.10.1(typescript@5.7.3)(zod@3.24.2) - zod: 3.24.2 + '@t3-oss/env-core': 0.13.10(typescript@5.7.3)(zod@4.3.5) optionalDependencies: typescript: 5.7.3 + zod: 4.3.5 '@tailwindcss/node@4.1.4': dependencies: @@ -3595,11 +3619,11 @@ snapshots: postcss: 8.5.2 tailwindcss: 4.1.4 - '@tanstack/query-core@5.74.3': {} + '@tanstack/query-core@5.90.19': {} - '@tanstack/react-query@5.74.3(react@18.3.1)': + '@tanstack/react-query@5.90.19(react@18.3.1)': dependencies: - '@tanstack/query-core': 5.74.3 + '@tanstack/query-core': 5.90.19 react: 18.3.1 '@tanstack/react-table@8.21.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': @@ -3610,41 +3634,41 @@ snapshots: '@tanstack/table-core@8.21.2': {} - '@trpc/client@11.1.1(@trpc/server@11.1.1(typescript@5.7.3))(typescript@5.7.3)': + '@trpc/client@11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3)': dependencies: - '@trpc/server': 11.1.1(typescript@5.7.3) + '@trpc/server': 11.5.1(typescript@5.7.3) typescript: 5.7.3 - '@trpc/next@11.1.1(@tanstack/react-query@5.74.3(react@18.3.1))(@trpc/client@11.1.1(@trpc/server@11.1.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/react-query@11.1.1(@tanstack/react-query@5.74.3(react@18.3.1))(@trpc/client@11.1.1(@trpc/server@11.1.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/server@11.1.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3))(@trpc/server@11.1.1(typescript@5.7.3))(next@15.3.4(babel-plugin-react-compiler@19.1.0-rc.2)(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)(typescript@5.7.3)': + '@trpc/next@11.5.1(@tanstack/react-query@5.90.19(react@18.3.1))(@trpc/client@11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/react-query@11.5.1(@tanstack/react-query@5.90.19(react@18.3.1))(@trpc/client@11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/server@11.5.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3))(@trpc/server@11.5.1(typescript@5.7.3))(next@15.3.4(babel-plugin-react-compiler@19.1.0-rc.2)(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)(typescript@5.7.3)': dependencies: - '@trpc/client': 11.1.1(@trpc/server@11.1.1(typescript@5.7.3))(typescript@5.7.3) - '@trpc/server': 11.1.1(typescript@5.7.3) + '@trpc/client': 11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3) + '@trpc/server': 11.5.1(typescript@5.7.3) next: 15.3.4(babel-plugin-react-compiler@19.1.0-rc.2)(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) typescript: 5.7.3 optionalDependencies: - '@tanstack/react-query': 5.74.3(react@18.3.1) - '@trpc/react-query': 11.1.1(@tanstack/react-query@5.74.3(react@18.3.1))(@trpc/client@11.1.1(@trpc/server@11.1.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/server@11.1.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@tanstack/react-query': 5.90.19(react@18.3.1) + '@trpc/react-query': 11.5.1(@tanstack/react-query@5.90.19(react@18.3.1))(@trpc/client@11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/server@11.5.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) - '@trpc/react-query@11.1.1(@tanstack/react-query@5.74.3(react@18.3.1))(@trpc/client@11.1.1(@trpc/server@11.1.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/server@11.1.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': + '@trpc/react-query@11.5.1(@tanstack/react-query@5.90.19(react@18.3.1))(@trpc/client@11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/server@11.5.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': dependencies: - '@tanstack/react-query': 5.74.3(react@18.3.1) - '@trpc/client': 11.1.1(@trpc/server@11.1.1(typescript@5.7.3))(typescript@5.7.3) - '@trpc/server': 11.1.1(typescript@5.7.3) + '@tanstack/react-query': 5.90.19(react@18.3.1) + '@trpc/client': 11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3) + '@trpc/server': 11.5.1(typescript@5.7.3) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) typescript: 5.7.3 - '@trpc/server@11.1.1(typescript@5.7.3)': + '@trpc/server@11.5.1(typescript@5.7.3)': dependencies: typescript: 5.7.3 - '@trpc/tanstack-react-query@11.1.1(@tanstack/react-query@5.74.3(react@18.3.1))(@trpc/client@11.1.1(@trpc/server@11.1.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/server@11.1.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': + '@trpc/tanstack-react-query@11.5.1(@tanstack/react-query@5.90.19(react@18.3.1))(@trpc/client@11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/server@11.5.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': dependencies: - '@tanstack/react-query': 5.74.3(react@18.3.1) - '@trpc/client': 11.1.1(@trpc/server@11.1.1(typescript@5.7.3))(typescript@5.7.3) - '@trpc/server': 11.1.1(typescript@5.7.3) + '@tanstack/react-query': 5.90.19(react@18.3.1) + '@trpc/client': 11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3) + '@trpc/server': 11.5.1(typescript@5.7.3) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) typescript: 5.7.3 @@ -3847,12 +3871,6 @@ snapshots: get-intrinsic: 1.2.7 is-array-buffer: 3.0.5 - asn1js@3.0.6: - dependencies: - pvtsutils: 1.3.6 - pvutils: 1.1.3 - tslib: 2.8.1 - ast-types-flow@0.0.8: {} async-function@1.0.0: {} @@ -3871,27 +3889,33 @@ snapshots: balanced-match@1.0.2: {} - better-auth@1.2.7: + better-auth@1.4.15(next@15.3.4(babel-plugin-react-compiler@19.1.0-rc.2)(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: - '@better-auth/utils': 0.2.4 - '@better-fetch/fetch': 1.1.18 - '@noble/ciphers': 0.6.0 - '@noble/hashes': 1.7.1 - '@simplewebauthn/browser': 13.1.0 - '@simplewebauthn/server': 13.1.1 - better-call: 1.0.8 + '@better-auth/core': 1.4.15(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.5))(jose@6.1.3)(kysely@0.28.10)(nanostores@1.1.0) + '@better-auth/telemetry': 1.4.15(@better-auth/core@1.4.15(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.5))(jose@6.1.3)(kysely@0.28.10)(nanostores@1.1.0)) + '@better-auth/utils': 0.3.0 + '@better-fetch/fetch': 1.1.21 + '@noble/ciphers': 2.1.1 + '@noble/hashes': 2.0.1 + better-call: 1.1.8(zod@4.3.5) defu: 6.1.4 - jose: 5.9.6 - kysely: 0.27.6 - nanostores: 0.11.4 - zod: 3.24.2 + jose: 6.1.3 + kysely: 0.28.10 + nanostores: 1.1.0 + zod: 4.3.5 + optionalDependencies: + next: 15.3.4(babel-plugin-react-compiler@19.1.0-rc.2)(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) - better-call@1.0.8: + better-call@1.1.8(zod@4.3.5): dependencies: - '@better-fetch/fetch': 1.1.18 - rou3: 0.5.1 + '@better-auth/utils': 0.3.0 + '@better-fetch/fetch': 1.1.21 + rou3: 0.7.12 set-cookie-parser: 2.7.1 - uncrypto: 0.1.3 + optionalDependencies: + zod: 4.3.5 brace-expansion@1.1.11: dependencies: @@ -4036,6 +4060,8 @@ snapshots: detect-node-es@1.1.0: {} + dist@file:../backend/package/dist: {} + doctrine@2.1.0: dependencies: esutils: 2.0.3 @@ -4529,6 +4555,11 @@ snapshots: inherits@2.0.4: {} + 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 @@ -4666,7 +4697,7 @@ snapshots: jiti@2.4.2: {} - jose@5.9.6: {} + jose@6.1.3: {} js-tokens@4.0.0: {} @@ -4695,7 +4726,7 @@ snapshots: dependencies: json-buffer: 3.0.1 - kysely@0.27.6: {} + kysely@0.28.10: {} language-subtag-registry@0.3.23: {} @@ -4792,7 +4823,7 @@ snapshots: nanoid@3.3.8: {} - nanostores@0.11.4: {} + nanostores@1.1.0: {} natural-compare@1.4.0: {} @@ -4951,12 +4982,6 @@ snapshots: punycode@2.3.1: {} - pvtsutils@1.3.6: - dependencies: - tslib: 2.8.1 - - pvutils@1.1.3: {} - queue-microtask@1.2.3: {} react-dom@18.3.1(react@18.3.1): @@ -5044,7 +5069,7 @@ snapshots: dependencies: glob: 7.2.3 - rou3@0.5.1: {} + rou3@0.7.12: {} run-parallel@1.2.0: dependencies: @@ -5336,8 +5361,6 @@ snapshots: typescript@5.7.3: {} - typescript@5.8.3: {} - unbox-primitive@1.1.0: dependencies: call-bound: 1.0.3 @@ -5345,8 +5368,6 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 - uncrypto@0.1.3: {} - undici-types@6.19.8: {} uri-js@4.4.1: @@ -5418,4 +5439,4 @@ snapshots: yocto-queue@0.1.0: {} - zod@3.24.2: {} + zod@4.3.5: {} diff --git a/src/app/dashboard/(active)/account/page.tsx b/src/app/dashboard/(active)/account/page.tsx index 6e0dde7..b6356c1 100644 --- a/src/app/dashboard/(active)/account/page.tsx +++ b/src/app/dashboard/(active)/account/page.tsx @@ -2,6 +2,8 @@ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { getInitials } from "@/lib/utils"; import { getServerSession } from "@/server/auth"; import { Telegram } from "./telegram"; +import { SetName } from "./set-name"; +import { CircleAlert, UserIcon } from "lucide-react"; export default async function Account() { const { data: session } = await getServerSession(); @@ -20,16 +22,28 @@ export default async function Account() { {user.image && ( )} - - {getInitials(user.name)} + + {user.name ? getInitials(user.name) : } -
-

{user.name}

-

{user.email}

-
-

Telegram:

+
+
+ {!user.name && } + Name: + {user.name ? +

{user.name}

+ : + + } +
+ +
+ Email: +

{user.email}

+
+
+ Telegram:
diff --git a/src/app/dashboard/(active)/account/set-name.tsx b/src/app/dashboard/(active)/account/set-name.tsx new file mode 100644 index 0000000..c648b62 --- /dev/null +++ b/src/app/dashboard/(active)/account/set-name.tsx @@ -0,0 +1,61 @@ +"use client" + +import { auth } from "@/lib/auth" +import { type FormEvent, useState } from "react" +import { Button } from "@/components/ui/button" +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { useRouter } from "next/navigation" + +export function SetName({ initialName }: { initialName?: string }) { + const router = useRouter() + const [name, setName] = useState(initialName ?? "") + const [open, setOpen] = useState(false) + + async function handleUpdate(e: FormEvent) { + e.preventDefault() + await auth.updateUser({ name }) + setOpen(false) + router.refresh() + } + + return + + + + + + Set your name + + Make changes to your profile here. Click save when you're + done. + + +
+
+
+ + setName(e.target.value)} /> +
+
+ + + + + + +
+
+
+} + diff --git a/src/app/dashboard/(active)/account/telegram.tsx b/src/app/dashboard/(active)/account/telegram.tsx index 48e4e65..a9ee3b2 100644 --- a/src/app/dashboard/(active)/account/telegram.tsx +++ b/src/app/dashboard/(active)/account/telegram.tsx @@ -24,13 +24,13 @@ function ShowTelegram({ }) { const trpc = useTRPC(); const { data, isLoading } = useQuery( - trpc.tg.permissions.getRole.queryOptions({ userId }), + trpc.tg.permissions.getRoles.queryOptions({ userId }), ); return ( <> @{username} - {!isLoading && data && data.role !== "user" && ( - (role: {data.role}) + {!isLoading && data?.roles?.length && ( + (roles: {data.roles.join(", ")}) )} ); diff --git a/src/app/dashboard/(active)/complete-profile.tsx b/src/app/dashboard/(active)/complete-profile.tsx new file mode 100644 index 0000000..10c4ee2 --- /dev/null +++ b/src/app/dashboard/(active)/complete-profile.tsx @@ -0,0 +1,17 @@ +"use client" + +import { Button } from "@/components/ui/button" +import { type User } from "better-auth" +import { UserRoundPenIcon } from "lucide-react" +import Link from "next/link" + +export function CompleteProfile({ user }: { user: User }) { + return !user.name && (
+ +

+ Your profile is incomplete, please enter the missing information. +

+ +
) +} + diff --git a/src/app/dashboard/(active)/page.tsx b/src/app/dashboard/(active)/page.tsx index 5ac1aff..4aa3363 100644 --- a/src/app/dashboard/(active)/page.tsx +++ b/src/app/dashboard/(active)/page.tsx @@ -1,10 +1,12 @@ import { getServerSession } from "@/server/auth"; +import { CompleteProfile } from "./complete-profile"; export default async function AdminHome() { - const session = await getServerSession(); + const { data: session } = await getServerSession(); return ( session && (
+

Home

) diff --git a/src/app/dashboard/layout.tsx b/src/app/dashboard/layout.tsx index e95703e..7e09d0a 100644 --- a/src/app/dashboard/layout.tsx +++ b/src/app/dashboard/layout.tsx @@ -1,7 +1,7 @@ -import { Header } from "@/components/header"; import { SidebarProvider } from "@/components/ui/sidebar"; import { getServerSession } from "@/server/auth"; import { redirect } from "next/navigation"; +import { getQueryClient, trpc } from "@/lib/trpc/server"; export default async function AdminLayout({ children, @@ -12,10 +12,17 @@ export default async function AdminLayout({ // console.log(session) if (!session.data) redirect("/login"); + const tgId = session.data.user.telegramId + if (!tgId) redirect("/onboarding/link"); + // if (session?.user.role === USER_ROLE.INACTIVE) ; + // if (session?.user.role === USER_ROLE.DISABLED) redirect("/dashboard/disabled"); + + const qc = getQueryClient() + const { roles } = await qc.fetchQuery(trpc.tg.permissions.getRoles.queryOptions({ userId: tgId })) + if (!roles || roles.length === 0) redirect("/onboarding/no-role") + if (roles.includes("creator")) redirect("/onboarding/unauthorized") + return ( -
-
- {children} -
+ {children} ); } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index f54a92b..bef6666 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -2,7 +2,7 @@ import { GeistSans } from "geist/font/sans"; import { type Metadata } from "next"; import "@/index.css"; import { TRPCReactProvider } from "@/lib/trpc/client"; -import { HEADER_HEIGHT } from "@/components/header"; +import { Header, HEADER_HEIGHT } from "@/components/header"; import { ThemeProvider } from "@/components/theme-provider"; import { Toaster } from "@/components/ui/sonner"; import { TooltipProvider } from "@/components/ui/tooltip"; @@ -43,8 +43,13 @@ export default function RootLayout({ disableTransitionOnChange > - {children} - + +
+
+ {children} +
+
+
diff --git a/src/app/login/can-i-access.tsx b/src/app/login/can-i-access.tsx index 78c5d84..24ef7c2 100644 --- a/src/app/login/can-i-access.tsx +++ b/src/app/login/can-i-access.tsx @@ -40,10 +40,10 @@ export function CanIAccess() { />

- 1. Login with GitHub + 1. Login with email

-

- (provider might change) +

+ We send an OTP to your email

@@ -58,7 +58,7 @@ export function CanIAccess() { className="place-self-center justify-self-center" />
-

+

2. Link your Telegram account

@@ -81,8 +81,8 @@ export function CanIAccess() { 3. Use the dashboard

- If your role is not recognized,
be patient until the IT team - fixes it! + If your role is not recognized,
be patient until the IT + team fixes it!

diff --git a/src/app/login/layout.tsx b/src/app/login/layout.tsx deleted file mode 100644 index 6a65f20..0000000 --- a/src/app/login/layout.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { Header } from "@/components/header"; - -export default function Layout({ children }: { children: React.ReactNode }) { - return ( -
-
- {children} -
- ); -} diff --git a/src/app/login/github.tsx b/src/app/login/login-button.tsx similarity index 52% rename from src/app/login/github.tsx rename to src/app/login/login-button.tsx index 29b01cd..239c4fb 100644 --- a/src/app/login/github.tsx +++ b/src/app/login/login-button.tsx @@ -1,35 +1,18 @@ "use client"; -import Image from "next/image"; -import { signIn } from "@/lib/auth"; import { Card, CardContent } from "@/components/ui/card"; import { LogInIcon } from "lucide-react"; +import Link from "next/link"; -export function Github({ callbackURL }: { callbackURL: string }) { +export function LoginButton() { return ( - + ); } diff --git a/src/app/login/login-form.tsx b/src/app/login/login-form.tsx new file mode 100644 index 0000000..9373f75 --- /dev/null +++ b/src/app/login/login-form.tsx @@ -0,0 +1,202 @@ +"use client"; + +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardHeader, + CardTitle, + CardDescription, +} from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { useState } from "react"; +import { Loader2 } from "lucide-react"; +import { auth } from "@/lib/auth"; +import { toast } from "sonner"; +import { + InputOTP, + InputOTPGroup, + InputOTPSlot, +} from "@/components/ui/input-otp"; +import { useRouter } from "next/navigation"; + +export default function LoginForm() { + const [email, setEmail] = useState(""); + const [sent, setSent] = useState(false); + + return ( +
+ {!sent ? ( + setEmail(v)} + onSend={() => setSent(true)} + /> + ) : ( + + )} +
+ ); +} + +function EmailCard({ + email, + onChange, + onSend, +}: { + email: string; + onChange: (value: string) => void; + onSend: () => void; +}) { + const [loading, setLoading] = useState(false); + + async function sendOtp() { + const { data, error } = await auth.emailOtp.sendVerificationOtp({ + type: "sign-in", + email, + fetchOptions: { + onRequest: () => { + setLoading(true); + }, + onResponse: () => { + setLoading(false); + }, + }, + }); + + if (data?.success) { + onSend(); + toast.success("OTP sent via email!"); + return + } + + toast.error("There was an unexpected error"); + console.error({ error }); + } + + return ( + + + Sign In + + Enter your email below to login to your account + + + +
{ + e.preventDefault(); + await sendOtp(); + }} + > +
+ + { + onChange(e.target.value); + }} + value={email} + /> +
+ +
+
+
+ ); +} + +function OTPCard({ email }: { email: string }) { + const router = useRouter() + const [loading, setLoading] = useState(false); + const [otp, setOtp] = useState(""); + + async function verifyOtp() { + const { data, error } = await auth.signIn.emailOtp({ + email, + otp, + fetchOptions: { + onRequest: () => { + setLoading(true); + }, + onResponse: () => { + setLoading(false); + }, + }, + }); + + if (data) { + toast.success("Successfully logged in!"); + return router.replace("/dashboard") + } + + if (error.code === "INVALID_OTP") toast.error("You entered an invalid OTP"); + else { + toast.error("There was an unexpected error") + console.error({ error }); + } + } + return ( + + + Sign In + + Enter the OTP we sent to your email + + + +
{ + e.preventDefault(); + await verifyOtp(); + }} + > +
+ + setOtp(v)} + autoComplete="off" + data-1p-ignore data-lpignore="true" data-protonpass-ignore="true" + type="text" + > + + + + + + + + + +
+ +
+
+
+ ); +} diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 8586f93..ed99477 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -1,26 +1,14 @@ -import { redirect } from "next/navigation"; -import { Github } from "./github"; import { getServerSession } from "@/server/auth"; -import { getBaseUrl } from "@/lib/utils"; -import { WhatIs } from "./what-is"; -import { CanIAccess } from "./can-i-access"; - -export default async function SignInPage({ - searchParams, -}: { - searchParams: Promise<{ callbackUrl?: string }>; -}) { - const { callbackUrl } = await searchParams; - const callbackURL = `${getBaseUrl()}${callbackUrl ?? "/login/success"}`; +import LoginForm from "./login-form"; +import { redirect } from "next/navigation"; - const session = await getServerSession(); - if (session.data?.user) redirect("/login/success"); +export default async function Page() { + const { data: session } = await getServerSession() + if (session) return redirect("/dashboard") return ( -
- - - +
+
); } diff --git a/src/app/login/success/page.tsx b/src/app/login/success/page.tsx deleted file mode 100644 index 32464e9..0000000 --- a/src/app/login/success/page.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { redirect } from "next/navigation"; -import { getServerSession } from "@/server/auth"; -import { getQueryClient, trpc } from "@/lib/trpc/server"; - -export default async function LoginSuccess() { - const session = await getServerSession(); - if (!session.data) redirect("/login"); - - const tgId = session.data.user.telegramId - if (!tgId) redirect("/onboarding/link"); - // if (session?.user.role === USER_ROLE.INACTIVE) ; - // if (session?.user.role === USER_ROLE.DISABLED) redirect("/dashboard/disabled"); - - const qc = getQueryClient() - const { role } = await qc.fetchQuery(trpc.tg.permissions.getRole.queryOptions({ userId: tgId })) - if (role === "user") redirect("/onboarding/no-role") - if (role === "creator") redirect("/onboarding/unauthorized") - return redirect("/dashboard") -} diff --git a/src/app/onboarding/layout.tsx b/src/app/onboarding/layout.tsx index 6a65f20..91e1c96 100644 --- a/src/app/onboarding/layout.tsx +++ b/src/app/onboarding/layout.tsx @@ -1,9 +1,6 @@ -import { Header } from "@/components/header"; - export default function Layout({ children }: { children: React.ReactNode }) { return (
-
{children}
); diff --git a/src/app/onboarding/link/logout.tsx b/src/app/onboarding/link/logout.tsx new file mode 100644 index 0000000..6ff1d34 --- /dev/null +++ b/src/app/onboarding/link/logout.tsx @@ -0,0 +1,25 @@ +"use client" + +import { auth } from "../../../lib/auth" +import { useRouter } from "next/navigation"; +import { toast } from "sonner"; + +export function Logout({ email }: { email: string }) { + const router = useRouter() + + async function handleLogout() { + const { data, error } = await auth.signOut() + if (data?.success || !error) { + toast.success("Successfully logged out") + return router.replace("/") + } + + toast.error("There was an unexpected error") + console.error({ error }) + } + + return
+

Logged in as {email}

+ Wrong account? +
+} diff --git a/src/app/onboarding/link/page.tsx b/src/app/onboarding/link/page.tsx index 5d64978..0a77ef1 100644 --- a/src/app/onboarding/link/page.tsx +++ b/src/app/onboarding/link/page.tsx @@ -5,13 +5,14 @@ import { Card } from "@/components/ui/card"; import { getServerSession } from "@/server/auth"; import { redirect } from "next/navigation"; import { env } from "@/env"; +import { Logout } from "./logout"; const BOT_USERNAME = env.NODE_ENV === "production" ? "pn_ts_dev_bot" : "pn_ts_devlocal_bot"; export default async function OnboardingLink() { - const { data } = await getServerSession(); - if (data?.user.telegramId) redirect("/login/success"); + const { data: session } = await getServerSession(); + if (!session || session.user.telegramId) redirect("/dashboard"); return (
@@ -34,6 +35,8 @@ export default async function OnboardingLink() {
+
+
); diff --git a/src/app/onboarding/link/telegram.tsx b/src/app/onboarding/link/telegram.tsx index cff261c..a752edc 100644 --- a/src/app/onboarding/link/telegram.tsx +++ b/src/app/onboarding/link/telegram.tsx @@ -44,11 +44,11 @@ export function TelegramLink({ botUsername }: { botUsername: string }) { const handleComplete = useCallback(() => { setSuccess(true); - refetch(); + void refetch(); remove(); setTimeout(() => { - router.push("/login/success"); + router.push("/dashboard"); }, 5000); }, [router, refetch, remove]); @@ -65,8 +65,7 @@ export function TelegramLink({ botUsername }: { botUsername: string }) { }); if (res.error) return console.error("custom error", res.error); - if (res.data instanceof APIError) - return console.error("better-auth APIError", res.data); + setExpired(false); update({ code: res.data.code, diff --git a/src/app/onboarding/no-role/page.tsx b/src/app/onboarding/no-role/page.tsx new file mode 100644 index 0000000..3ce84e8 --- /dev/null +++ b/src/app/onboarding/no-role/page.tsx @@ -0,0 +1,43 @@ +import Image from "next/image"; +import loginSvg2 from "@/assets/svg/login-2.svg"; +import { Card } from "@/components/ui/card"; +import { getServerSession } from "@/server/auth"; +import { redirect } from "next/navigation"; +import { Logout } from "../link/logout"; +import { getQueryClient, trpc } from "@/lib/trpc/server"; + +export default async function OnboardingNoRole() { + const { data: session } = await getServerSession(); + if (!session) redirect("/login"); + if (!session.user.telegramId) redirect("/onboarding/link") + + const qc = getQueryClient() + const { roles } = await qc.fetchQuery(trpc.tg.permissions.getRoles.queryOptions({ userId: session.user.telegramId })) + if (roles?.includes("creator")) redirect("/onboarding/unauthorized") + if (roles && roles.length > 0) redirect("/dashboard") + + return ( +
+ +
+ insert credentials +
+

+ Your account is not enabled +

+

+ Please contact an IT member +

+
+
+ + +
+
+ ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx index c88edbe..20085ea 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,5 +1,18 @@ import { redirect } from "next/navigation"; +import { LoginButton } from "./login/login-button"; +import { getServerSession } from "@/server/auth"; +import { WhatIs } from "./login/what-is"; +import { CanIAccess } from "./login/can-i-access"; export default async function IndexPage() { - return redirect("/login"); + const session = await getServerSession(); + if (session.data?.user) redirect("/dashboard"); + + return ( +
+ + + +
+ ); } diff --git a/src/components/header.tsx b/src/components/header.tsx index cf031cc..654f481 100644 --- a/src/components/header.tsx +++ b/src/components/header.tsx @@ -11,7 +11,7 @@ export async function Header() { return (
- +
@@ -36,10 +32,10 @@ export function NavUser({ user }: { user: User }) { size="lg" className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground cursor-pointer" > - + - {getInitials(user.name)} + {user.name ? getInitials(user.name) : }
@@ -51,25 +47,10 @@ export function NavUser({ user }: { user: User }) { - -
- - - - {getInitials(user.name)} - - -
- {user.name} - {user.email} -
-
-
- @@ -77,23 +58,17 @@ export function NavUser({ user }: { user: User }) { Account - {/* - - - Notifications + { + await signOut(); + router.replace("/login"); + }} + > + + Log out - */} - - { - await signOut(); - redirect("/login"); - }} - > - - Log out -
diff --git a/src/components/ui/alert.tsx b/src/components/ui/alert.tsx index 95d41e5..62a69c3 100644 --- a/src/components/ui/alert.tsx +++ b/src/components/ui/alert.tsx @@ -1,26 +1,23 @@ -import * as React from "react"; -import { cva, type VariantProps } from "class-variance-authority"; +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" -import { cn } from "@/lib/utils"; +import { cn } from "@/lib/utils" const alertVariants = cva( - "relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7", + "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", { variants: { variant: { - default: "bg-background text-foreground", + default: "bg-sidebar text-foreground", destructive: "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", - warning: - "bg-amber-300/20 text-amber-600 [&>svg]:text-amber-600 border-amber-600/50 dark:border-amber-300/50 dark:text-amber-300 dark:[&>svg]:text-amber-300", - info: "bg-blue-300/20 text-blue-600 [&>svg]:text-blue-600 border-blue-600/50 dark:border-blue-300/50 dark:bg-blue-900/30 dark:text-blue-300 dark:[&>svg]:text-blue-300", }, }, defaultVariants: { variant: "default", }, - }, -); + } +) const Alert = React.forwardRef< HTMLDivElement, @@ -32,8 +29,8 @@ const Alert = React.forwardRef< className={cn(alertVariants({ variant }), className)} {...props} /> -)); -Alert.displayName = "Alert"; +)) +Alert.displayName = "Alert" const AlertTitle = React.forwardRef< HTMLParagraphElement, @@ -41,14 +38,11 @@ const AlertTitle = React.forwardRef< >(({ className, ...props }, ref) => (
-)); -AlertTitle.displayName = "AlertTitle"; +)) +AlertTitle.displayName = "AlertTitle" const AlertDescription = React.forwardRef< HTMLParagraphElement, @@ -59,7 +53,7 @@ const AlertDescription = React.forwardRef< className={cn("text-sm [&_p]:leading-relaxed", className)} {...props} /> -)); -AlertDescription.displayName = "AlertDescription"; +)) +AlertDescription.displayName = "AlertDescription" -export { Alert, AlertTitle, AlertDescription }; +export { Alert, AlertTitle, AlertDescription } diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx index 2078c54..028b1f5 100644 --- a/src/components/ui/dropdown-menu.tsx +++ b/src/components/ui/dropdown-menu.tsx @@ -163,7 +163,7 @@ const DropdownMenuSeparator = React.forwardRef< >(({ className, ...props }, ref) => ( )); diff --git a/src/components/ui/input-otp.tsx b/src/components/ui/input-otp.tsx new file mode 100644 index 0000000..1580a9c --- /dev/null +++ b/src/components/ui/input-otp.tsx @@ -0,0 +1,71 @@ +"use client"; + +import * as React from "react"; +import { OTPInput, OTPInputContext } from "input-otp"; +import { Dot } from "lucide-react"; + +import { cn } from "@/lib/utils"; + +const InputOTP = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, containerClassName, ...props }, ref) => ( + +)); +InputOTP.displayName = "InputOTP"; + +const InputOTPGroup = React.forwardRef< + React.ElementRef<"div">, + React.ComponentPropsWithoutRef<"div"> +>(({ className, ...props }, ref) => ( +
+)); +InputOTPGroup.displayName = "InputOTPGroup"; + +const InputOTPSlot = React.forwardRef< + React.ElementRef<"div">, + React.ComponentPropsWithoutRef<"div"> & { index: number } +>(({ index, className, ...props }, ref) => { + const inputOTPContext = React.useContext(OTPInputContext); + const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]!; + + return ( +
+ {char} + {hasFakeCaret && ( +
+
+
+ )} +
+ ); +}); +InputOTPSlot.displayName = "InputOTPSlot"; + +const InputOTPSeparator = React.forwardRef< + React.ElementRef<"div">, + React.ComponentPropsWithoutRef<"div"> +>(({ ...props }, ref) => ( +
+ +
+)); +InputOTPSeparator.displayName = "InputOTPSeparator"; + +export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }; diff --git a/src/components/ui/sidebar.tsx b/src/components/ui/sidebar.tsx index 870909e..050e3d5 100644 --- a/src/components/ui/sidebar.tsx +++ b/src/components/ui/sidebar.tsx @@ -469,7 +469,7 @@ const SidebarGroupAction = React.forwardRef< ref={ref} data-sidebar="group-action" className={cn( - "text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", + "text-sidebar-foreground ring-sidebar-ring hover:bg-accent/40 hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", // Increases the hit area of the button on mobile. "after:absolute after:-inset-2 md:after:hidden", "group-data-[collapsible=icon]:hidden", @@ -521,13 +521,13 @@ const SidebarMenuItem = React.forwardRef< SidebarMenuItem.displayName = "SidebarMenuItem"; const sidebarMenuButtonVariants = cva( - "peer/menu-button cursor-pointer flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", + "peer/menu-button cursor-pointer flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-accent/40 hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", { variants: { variant: { - default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", + default: "hover:bg-accent/40 hover:text-sidebar-accent-foreground", outline: - "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]", + "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-accent/40 hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]", }, size: { default: "h-8 text-sm", @@ -615,7 +615,7 @@ const SidebarMenuAction = React.forwardRef< ref={ref} data-sidebar="menu-action" className={cn( - "text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", + "text-sidebar-foreground ring-sidebar-ring hover:bg-accent/40 hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", // Increases the hit area of the button on mobile. "after:absolute after:-inset-2 md:after:hidden", "peer-data-[size=sm]/menu-button:top-1", @@ -731,7 +731,7 @@ const SidebarMenuSubButton = React.forwardRef< data-size={size} data-active={isActive} className={cn( - "text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", + "text-sidebar-foreground ring-sidebar-ring hover:bg-accent/40 hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", "data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground", size === "sm" && "text-xs", size === "md" && "text-sm", diff --git a/src/components/user-card.tsx b/src/components/user-card.tsx index 19fb169..21d9dcb 100644 --- a/src/components/user-card.tsx +++ b/src/components/user-card.tsx @@ -1,5 +1,5 @@ "use client"; -import { type LucideIcon, XIcon } from "lucide-react"; +import { type LucideIcon, XIcon, UserIcon } from "lucide-react"; import { useTransition } from "react"; import { Button } from "./ui/button"; import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar"; @@ -22,7 +22,7 @@ export function UserCard({ user, icon: Icon = XIcon, buttonAction }: Props) { )} - {getInitials(user.name)} + {user.name ? getInitials(user.name) : }

{user.name}

diff --git a/src/env.js b/src/env.ts similarity index 100% rename from src/env.js rename to src/env.ts diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 99dcebf..b9ee24f 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -2,6 +2,8 @@ import { createAuthClient } from "better-auth/react"; import { AUTH_PATH, type TelegramPlugin } from "@polinetwork/backend"; import { getBaseUrl } from "./utils"; import type { BetterAuthClientPlugin } from "better-auth"; +import { emailOTPClient } from "better-auth/client/plugins"; +import { nextCookies } from "better-auth/next-js"; const telegramPlugin = () => { return { @@ -13,7 +15,7 @@ const telegramPlugin = () => { export const auth = createAuthClient({ baseURL: getBaseUrl(), basePath: AUTH_PATH, - plugins: [telegramPlugin()], + plugins: [telegramPlugin(), emailOTPClient(), nextCookies()], }); export const { signIn, signOut, getSession, useSession } = auth; diff --git a/src/lib/trpc/client.tsx b/src/lib/trpc/client.tsx index f8064ca..995c870 100644 --- a/src/lib/trpc/client.tsx +++ b/src/lib/trpc/client.tsx @@ -9,6 +9,7 @@ import { useState } from "react"; import { makeQueryClient } from "./query-client"; import { TRPC_PATH, type AppRouter } from "@polinetwork/backend"; import { getBaseUrl } from "../utils"; +import SuperJSON from "superjson"; export const { TRPCProvider, useTRPC } = createTRPCContext(); let browserQueryClient: QueryClient; @@ -40,8 +41,8 @@ export function TRPCReactProvider( createTRPCClient({ links: [ httpBatchLink({ - //transformer: superjson, url, + transformer: SuperJSON }), ], }), diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 3b57cc9..d47061a 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -22,5 +22,5 @@ export function getInitials(name: string): string { } export async function wait(ms: number): Promise { - return new Promise(res => setTimeout(res, ms)) + return new Promise((res) => setTimeout(res, ms)); } From 3d58b05d1f086f3be9b85b8b4b13417666440ecf Mon Sep 17 00:00:00 2001 From: Lorenzo Corallo Date: Mon, 19 Jan 2026 19:04:12 +0100 Subject: [PATCH 2/3] fix: bump backend package --- package.json | 2 +- pnpm-lock.yaml | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index a33c2ba..b26f77b 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "@hookform/resolvers": "^3.9.1", - "@polinetwork/backend": "file:../backend/package/dist/", + "@polinetwork/backend": "^0.14.0", "@radix-ui/react-alert-dialog": "^1.1.3", "@radix-ui/react-avatar": "^1.1.1", "@radix-ui/react-collapsible": "^1.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7f46898..c3a6571 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: specifier: ^3.9.1 version: 3.10.0(react-hook-form@7.55.0(react@18.3.1)) '@polinetwork/backend': - specifier: file:../backend/package/dist/ - version: dist@file:../backend/package/dist + specifier: ^0.14.0 + version: 0.14.0 '@radix-ui/react-alert-dialog': specifier: ^1.1.3 version: 1.1.6(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -462,6 +462,10 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} + '@polinetwork/backend@0.14.0': + resolution: {integrity: sha512-VSkSLetxprjPu38WFK580uQ3VBtflM4Srz+D32ONtL0u9SUjixSq5lNwPXcQmGqiByrV3RIA3Z2WBJY2A9Lk4w==} + engines: {node: '>=24.8.0', pnpm: '>=10.17.1'} + '@radix-ui/number@1.1.0': resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} @@ -1577,9 +1581,6 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} - dist@file:../backend/package/dist: - resolution: {directory: ../backend/package/dist, type: directory} - doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -3011,6 +3012,8 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} + '@polinetwork/backend@0.14.0': {} + '@radix-ui/number@1.1.0': {} '@radix-ui/primitive@1.1.1': {} @@ -4060,8 +4063,6 @@ snapshots: detect-node-es@1.1.0: {} - dist@file:../backend/package/dist: {} - doctrine@2.1.0: dependencies: esutils: 2.0.3 From fd7c27ac5a5fd2535644b1c49e504b18fb0da9ea Mon Sep 17 00:00:00 2001 From: Lorenzo Corallo Date: Wed, 21 Jan 2026 02:44:03 +0100 Subject: [PATCH 3/3] fix: biome fix and merge conflict --- src/app/dashboard/(active)/account/page.tsx | 26 +--- .../dashboard/(active)/account/set-name.tsx | 65 +++++----- .../dashboard/(active)/complete-profile.tsx | 23 ++-- src/app/dashboard/layout.tsx | 4 +- src/app/layout.tsx | 6 +- src/app/login/login-button.tsx | 2 +- src/app/login/login-form.tsx | 122 +++++++----------- src/app/login/page.tsx | 2 +- src/app/onboarding/link/logout.tsx | 19 ++- src/app/onboarding/link/page.tsx | 6 +- src/app/onboarding/no-role/page.tsx | 28 ++-- src/app/page.tsx | 4 +- src/components/sidebar/nav-user.tsx | 9 +- src/components/user-card.tsx | 8 +- src/lib/auth.ts | 4 +- 15 files changed, 147 insertions(+), 181 deletions(-) diff --git a/src/app/dashboard/(active)/account/page.tsx b/src/app/dashboard/(active)/account/page.tsx index 682ef6d..7ee6670 100644 --- a/src/app/dashboard/(active)/account/page.tsx +++ b/src/app/dashboard/(active)/account/page.tsx @@ -1,16 +1,9 @@ -<<<<<<< HEAD -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { getInitials } from "@/lib/utils"; -import { getServerSession } from "@/server/auth"; -import { Telegram } from "./telegram"; -import { SetName } from "./set-name"; -import { CircleAlert, UserIcon } from "lucide-react"; -======= +import { CircleAlert, UserIcon } from "lucide-react" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { getInitials } from "@/lib/utils" import { getServerSession } from "@/server/auth" +import { SetName } from "./set-name" import { Telegram } from "./telegram" ->>>>>>> main export default async function Account() { const { data: session } = await getServerSession() @@ -24,28 +17,17 @@ export default async function Account() {
-<<<<<<< HEAD - {user.image && ( - - )} + {user.image && } {user.name ? getInitials(user.name) : } -======= - {user.image && } - {getInitials(user.name)} ->>>>>>> main
{!user.name && } Name: - {user.name ? -

{user.name}

- : - - } + {user.name ?

{user.name}

: }
diff --git a/src/app/dashboard/(active)/account/set-name.tsx b/src/app/dashboard/(active)/account/set-name.tsx index c648b62..70c6fb8 100644 --- a/src/app/dashboard/(active)/account/set-name.tsx +++ b/src/app/dashboard/(active)/account/set-name.tsx @@ -1,6 +1,6 @@ "use client" -import { auth } from "@/lib/auth" +import { useRouter } from "next/navigation" import { type FormEvent, useState } from "react" import { Button } from "@/components/ui/button" import { @@ -15,7 +15,7 @@ import { } from "@/components/ui/dialog" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" -import { useRouter } from "next/navigation" +import { auth } from "@/lib/auth" export function SetName({ initialName }: { initialName?: string }) { const router = useRouter() @@ -29,33 +29,38 @@ export function SetName({ initialName }: { initialName?: string }) { router.refresh() } - return - - - - - - Set your name - - Make changes to your profile here. Click save when you're - done. - - -
-
-
- - setName(e.target.value)} /> + return ( + + + + + + + Set your name + Make changes to your profile here. Click save when you're done. + + +
+
+ + setName(e.target.value)} + /> +
-
- - - - - - - - -
+ + + + + + + + + + ) } - diff --git a/src/app/dashboard/(active)/complete-profile.tsx b/src/app/dashboard/(active)/complete-profile.tsx index 10c4ee2..8de2c3f 100644 --- a/src/app/dashboard/(active)/complete-profile.tsx +++ b/src/app/dashboard/(active)/complete-profile.tsx @@ -1,17 +1,20 @@ "use client" -import { Button } from "@/components/ui/button" -import { type User } from "better-auth" +import type { User } from "better-auth" import { UserRoundPenIcon } from "lucide-react" import Link from "next/link" +import { Button } from "@/components/ui/button" export function CompleteProfile({ user }: { user: User }) { - return !user.name && (
- -

- Your profile is incomplete, please enter the missing information. -

- -
) + return ( + !user.name && ( +
+ +

Your profile is incomplete, please enter the missing information.

+ + + +
+ ) + ) } - diff --git a/src/app/dashboard/layout.tsx b/src/app/dashboard/layout.tsx index e8e843b..f5e9888 100644 --- a/src/app/dashboard/layout.tsx +++ b/src/app/dashboard/layout.tsx @@ -1,7 +1,7 @@ -import { SidebarProvider } from "@/components/ui/sidebar" -import { getServerSession } from "@/server/auth" import { redirect } from "next/navigation" +import { SidebarProvider } from "@/components/ui/sidebar" import { getQueryClient, trpc } from "@/lib/trpc/server" +import { getServerSession } from "@/server/auth" export default async function AdminLayout({ children }: { children: React.ReactNode }) { const session = await getServerSession() diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 9ec3e5d..985c474 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,11 +1,11 @@ import { GeistSans } from "geist/font/sans" -import { type Metadata } from "next" +import type { Metadata } from "next" import "@/index.css" -import { TRPCReactProvider } from "@/lib/trpc/client" -import { Header, HEADER_HEIGHT } from "@/components/header" +import { HEADER_HEIGHT, Header } from "@/components/header" import { ThemeProvider } from "@/components/theme-provider" import { Toaster } from "@/components/ui/sonner" import { TooltipProvider } from "@/components/ui/tooltip" +import { TRPCReactProvider } from "@/lib/trpc/client" const desc = "PoliNetwork Admin Dashboard" diff --git a/src/app/login/login-button.tsx b/src/app/login/login-button.tsx index b0bb87f..51c6226 100644 --- a/src/app/login/login-button.tsx +++ b/src/app/login/login-button.tsx @@ -1,7 +1,7 @@ "use client" -import { Card, CardContent } from "@/components/ui/card" import { LogInIcon } from "lucide-react" import Link from "next/link" +import { Card, CardContent } from "@/components/ui/card" export function LoginButton() { return ( diff --git a/src/app/login/login-form.tsx b/src/app/login/login-form.tsx index 9373f75..aae38a4 100644 --- a/src/app/login/login-form.tsx +++ b/src/app/login/login-form.tsx @@ -1,43 +1,29 @@ -"use client"; +"use client" -import { Button } from "@/components/ui/button"; -import { - Card, - CardContent, - CardHeader, - CardTitle, - CardDescription, -} from "@/components/ui/card"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { useState } from "react"; -import { Loader2 } from "lucide-react"; -import { auth } from "@/lib/auth"; -import { toast } from "sonner"; -import { - InputOTP, - InputOTPGroup, - InputOTPSlot, -} from "@/components/ui/input-otp"; -import { useRouter } from "next/navigation"; +import { Loader2 } from "lucide-react" +import { useRouter } from "next/navigation" +import { useState } from "react" +import { toast } from "sonner" +import { Button } from "@/components/ui/button" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Input } from "@/components/ui/input" +import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/ui/input-otp" +import { Label } from "@/components/ui/label" +import { auth } from "@/lib/auth" export default function LoginForm() { - const [email, setEmail] = useState(""); - const [sent, setSent] = useState(false); + const [email, setEmail] = useState("") + const [sent, setSent] = useState(false) return (
{!sent ? ( - setEmail(v)} - onSend={() => setSent(true)} - /> + setEmail(v)} onSend={() => setSent(true)} /> ) : ( )}
- ); + ) } function EmailCard({ @@ -45,11 +31,11 @@ function EmailCard({ onChange, onSend, }: { - email: string; - onChange: (value: string) => void; - onSend: () => void; + email: string + onChange: (value: string) => void + onSend: () => void }) { - const [loading, setLoading] = useState(false); + const [loading, setLoading] = useState(false) async function sendOtp() { const { data, error } = await auth.emailOtp.sendVerificationOtp({ @@ -57,22 +43,22 @@ function EmailCard({ email, fetchOptions: { onRequest: () => { - setLoading(true); + setLoading(true) }, onResponse: () => { - setLoading(false); + setLoading(false) }, }, - }); + }) if (data?.success) { - onSend(); - toast.success("OTP sent via email!"); + onSend() + toast.success("OTP sent via email!") return } - toast.error("There was an unexpected error"); - console.error({ error }); + toast.error("There was an unexpected error") + console.error({ error }) } return ( @@ -87,8 +73,8 @@ function EmailCard({
{ - e.preventDefault(); - await sendOtp(); + e.preventDefault() + await sendOtp() }} >
@@ -99,28 +85,24 @@ function EmailCard({ placeholder="mario.rossi@example.org" required onChange={(e) => { - onChange(e.target.value); + onChange(e.target.value) }} value={email} />
- ); + ) } function OTPCard({ email }: { email: string }) { const router = useRouter() - const [loading, setLoading] = useState(false); - const [otp, setOtp] = useState(""); + const [loading, setLoading] = useState(false) + const [otp, setOtp] = useState("") async function verifyOtp() { const { data, error } = await auth.signIn.emailOtp({ @@ -128,39 +110,37 @@ function OTPCard({ email }: { email: string }) { otp, fetchOptions: { onRequest: () => { - setLoading(true); + setLoading(true) }, onResponse: () => { - setLoading(false); + setLoading(false) }, }, - }); + }) if (data) { - toast.success("Successfully logged in!"); + toast.success("Successfully logged in!") return router.replace("/dashboard") } - if (error.code === "INVALID_OTP") toast.error("You entered an invalid OTP"); + if (error.code === "INVALID_OTP") toast.error("You entered an invalid OTP") else { toast.error("There was an unexpected error") - console.error({ error }); + console.error({ error }) } } return ( Sign In - - Enter the OTP we sent to your email - + Enter the OTP we sent to your email
{ - e.preventDefault(); - await verifyOtp(); + e.preventDefault() + await verifyOtp() }} >
@@ -171,7 +151,9 @@ function OTPCard({ email }: { email: string }) { value={otp} onChange={(v) => setOtp(v)} autoComplete="off" - data-1p-ignore data-lpignore="true" data-protonpass-ignore="true" + data-1p-ignore + data-lpignore="true" + data-protonpass-ignore="true" type="text" > @@ -184,19 +166,11 @@ function OTPCard({ email }: { email: string }) {
-
- ); + ) } diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 2d535d0..e701bc7 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -1,6 +1,6 @@ +import { redirect } from "next/navigation" import { getServerSession } from "@/server/auth" import LoginForm from "./login-form" -import { redirect } from "next/navigation" export default async function Page() { const { data: session } = await getServerSession() diff --git a/src/app/onboarding/link/logout.tsx b/src/app/onboarding/link/logout.tsx index 6ff1d34..8cca84f 100644 --- a/src/app/onboarding/link/logout.tsx +++ b/src/app/onboarding/link/logout.tsx @@ -1,8 +1,8 @@ "use client" +import { useRouter } from "next/navigation" +import { toast } from "sonner" import { auth } from "../../../lib/auth" -import { useRouter } from "next/navigation"; -import { toast } from "sonner"; export function Logout({ email }: { email: string }) { const router = useRouter() @@ -18,8 +18,15 @@ export function Logout({ email }: { email: string }) { console.error({ error }) } - return
-

Logged in as {email}

- Wrong account? -
+ return ( +
+

+ Logged in as {email} +

+ Wrong account?{" "} + +
+ ) } diff --git a/src/app/onboarding/link/page.tsx b/src/app/onboarding/link/page.tsx index 0f99f04..64cf606 100644 --- a/src/app/onboarding/link/page.tsx +++ b/src/app/onboarding/link/page.tsx @@ -1,11 +1,11 @@ import Image from "next/image" -import { TelegramLink } from "./telegram" +import { redirect } from "next/navigation" import loginSvg2 from "@/assets/svg/login-2.svg" import { Card } from "@/components/ui/card" -import { getServerSession } from "@/server/auth" -import { redirect } from "next/navigation" import { env } from "@/env" +import { getServerSession } from "@/server/auth" import { Logout } from "./logout" +import { TelegramLink } from "./telegram" const BOT_USERNAME = env.NODE_ENV === "production" ? "pn_ts_dev_bot" : "pn_ts_devlocal_bot" diff --git a/src/app/onboarding/no-role/page.tsx b/src/app/onboarding/no-role/page.tsx index 3ce84e8..c890e55 100644 --- a/src/app/onboarding/no-role/page.tsx +++ b/src/app/onboarding/no-role/page.tsx @@ -1,14 +1,14 @@ -import Image from "next/image"; -import loginSvg2 from "@/assets/svg/login-2.svg"; -import { Card } from "@/components/ui/card"; -import { getServerSession } from "@/server/auth"; -import { redirect } from "next/navigation"; -import { Logout } from "../link/logout"; -import { getQueryClient, trpc } from "@/lib/trpc/server"; +import Image from "next/image" +import { redirect } from "next/navigation" +import loginSvg2 from "@/assets/svg/login-2.svg" +import { Card } from "@/components/ui/card" +import { getQueryClient, trpc } from "@/lib/trpc/server" +import { getServerSession } from "@/server/auth" +import { Logout } from "../link/logout" export default async function OnboardingNoRole() { - const { data: session } = await getServerSession(); - if (!session) redirect("/login"); + const { data: session } = await getServerSession() + if (!session) redirect("/login") if (!session.user.telegramId) redirect("/onboarding/link") const qc = getQueryClient() @@ -27,17 +27,13 @@ export default async function OnboardingNoRole() { alt="insert credentials" />
-

- Your account is not enabled -

-

- Please contact an IT member -

+

Your account is not enabled

+

Please contact an IT member

- ); + ) } diff --git a/src/app/page.tsx b/src/app/page.tsx index 96f8e1b..eda2643 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,8 +1,8 @@ import { redirect } from "next/navigation" -import { LoginButton } from "./login/login-button" import { getServerSession } from "@/server/auth" -import { WhatIs } from "./login/what-is" import { CanIAccess } from "./login/can-i-access" +import { LoginButton } from "./login/login-button" +import { WhatIs } from "./login/what-is" export default async function IndexPage() { const session = await getServerSession() diff --git a/src/components/sidebar/nav-user.tsx b/src/components/sidebar/nav-user.tsx index b6422ad..7c406e6 100644 --- a/src/components/sidebar/nav-user.tsx +++ b/src/components/sidebar/nav-user.tsx @@ -1,7 +1,9 @@ "use client" +import type { User } from "better-auth" import { ChevronsUpDown, LogOut, Settings, UserIcon } from "lucide-react" - +import Link from "next/link" +import { useRouter } from "next/navigation" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { DropdownMenu, @@ -11,11 +13,8 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar" -import Link from "next/link" -import { getInitials } from "@/lib/utils" import { signOut } from "@/lib/auth" -import { useRouter } from "next/navigation" -import type { User } from "better-auth" +import { getInitials } from "@/lib/utils" export function NavUser({ user }: { user: User }) { const router = useRouter() diff --git a/src/components/user-card.tsx b/src/components/user-card.tsx index 18fb472..4210b26 100644 --- a/src/components/user-card.tsx +++ b/src/components/user-card.tsx @@ -1,10 +1,10 @@ "use client" -import { type LucideIcon, XIcon, UserIcon } from "lucide-react" +import type { User } from "better-auth" +import { type LucideIcon, UserIcon, XIcon } from "lucide-react" import { useTransition } from "react" -import { Button } from "./ui/button" -import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar" import { getInitials } from "@/lib/utils" -import type { User } from "better-auth" +import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar" +import { Button } from "./ui/button" type Props = { user: User diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 0f5399a..bac714a 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -1,9 +1,9 @@ -import { createAuthClient } from "better-auth/react" import { AUTH_PATH, type TelegramPlugin } from "@polinetwork/backend" -import { getBaseUrl } from "./utils" import type { BetterAuthClientPlugin } from "better-auth" import { emailOTPClient } from "better-auth/client/plugins" import { nextCookies } from "better-auth/next-js" +import { createAuthClient } from "better-auth/react" +import { getBaseUrl } from "./utils" const telegramPlugin = () => { return {