diff --git a/.env.example b/.env.example index b52f127..08b0651 100644 --- a/.env.example +++ b/.env.example @@ -10,6 +10,14 @@ NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_placeholder CLERK_SECRET_KEY=sk_test_placeholder +# Clerk Custom Auth Pages +NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in +NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up + +# Clerk Redirect After Auth +NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard +NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/dashboard + # ----------------------------- # Stripe # ----------------------------- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ef47a7..a08b41b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,12 +28,4 @@ jobs: run: npm run lint - name: Type check - run: npx tsc --noEmit - - - name: Build - run: npm run build - env: - # Dummy values for build (not used at runtime) - NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: pk_test_placeholder - NEXT_PUBLIC_APP_URL: https://replimap.com - NEXT_PUBLIC_API_URL: https://api.replimap.com + run: npx tsc --noEmit \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs index c85fb67..7435843 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -10,6 +10,9 @@ const compat = new FlatCompat({ }); const eslintConfig = [ + { + ignores: [".next/**", ".source/**", "node_modules/**"], + }, ...compat.extends("next/core-web-vitals", "next/typescript"), ]; diff --git a/package-lock.json b/package-lock.json index c94ad68..e8fc759 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,8 @@ "name": "replimap-frontend", "version": "0.1.0", "dependencies": { + "@clerk/nextjs": "^6.36.7", + "@clerk/themes": "^2.4.47", "@hookform/resolvers": "^3.10.0", "@radix-ui/react-accordion": "1.2.2", "@radix-ui/react-alert-dialog": "1.1.4", @@ -99,6 +101,121 @@ "node": ">=6.9.0" } }, + "node_modules/@clerk/backend": { + "version": "2.29.2", + "resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-2.29.2.tgz", + "integrity": "sha512-HflWfWG0jfnOB++3bu1N0lzAgOuapJRmkfX1jdtF3M+Wn4QVHczLMt2To9VIGiTt62+kRUWLV4SONzVvCoOcsA==", + "license": "MIT", + "dependencies": { + "@clerk/shared": "^3.42.0", + "@clerk/types": "^4.101.10", + "standardwebhooks": "^1.0.0", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=18.17.0" + } + }, + "node_modules/@clerk/clerk-react": { + "version": "5.59.3", + "resolved": "https://registry.npmjs.org/@clerk/clerk-react/-/clerk-react-5.59.3.tgz", + "integrity": "sha512-r1gmAYxhXs+QkXjDwj5Eqvm0Io8PtJ4FKkA45khiAzIQXcaQLFq/wFy7d1K8OSIYAIdFbuO0bnIOU/FdgWOc+A==", + "license": "MIT", + "dependencies": { + "@clerk/shared": "^3.42.0", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", + "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" + } + }, + "node_modules/@clerk/nextjs": { + "version": "6.36.7", + "resolved": "https://registry.npmjs.org/@clerk/nextjs/-/nextjs-6.36.7.tgz", + "integrity": "sha512-sH+bZsdKAdxAL6/E6w3QZarDz6Y2F1pavp1eAZRVitckiZFZJMzBDdNC6K1vOyQdzke1Y3jUwLcHfIF+oswBOA==", + "license": "MIT", + "dependencies": { + "@clerk/backend": "^2.29.2", + "@clerk/clerk-react": "^5.59.3", + "@clerk/shared": "^3.42.0", + "@clerk/types": "^4.101.10", + "server-only": "0.0.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "next": "^13.5.7 || ^14.2.25 || ^15.2.3 || ^16", + "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", + "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" + } + }, + "node_modules/@clerk/shared": { + "version": "3.42.0", + "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-3.42.0.tgz", + "integrity": "sha512-sJUur/7jnHHlAsdoDosxpOmfV05VR7K5rvqlFskj3GaAMFEJrvdOztw0hmhBGVSWiCpjTZfdGITegton8mo7mQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "csstype": "3.1.3", + "dequal": "2.0.3", + "glob-to-regexp": "0.4.1", + "js-cookie": "3.0.5", + "std-env": "^3.9.0", + "swr": "2.3.4" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", + "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@clerk/shared/node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/@clerk/themes": { + "version": "2.4.47", + "resolved": "https://registry.npmjs.org/@clerk/themes/-/themes-2.4.47.tgz", + "integrity": "sha512-Sko1+VlF1SGUAZSIh86R3zHn9iGKyPEAlJUtGcVadjWMOO/+sDLAINgDP8xEm/RSYgi2UVvCTbkRSmN0kW5OLg==", + "license": "MIT", + "dependencies": { + "@clerk/shared": "^3.42.0", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=18.17.0" + } + }, + "node_modules/@clerk/types": { + "version": "4.101.10", + "resolved": "https://registry.npmjs.org/@clerk/types/-/types-4.101.10.tgz", + "integrity": "sha512-qlmgnAm/IeK02RKEKVN8/Glx07xw/Lcv67jBfikM8HXhHc5v7bfYLD8UiWTr6H2RGtvB09cIt9JezRRlsuVBew==", + "license": "MIT", + "dependencies": { + "@clerk/shared": "^3.42.0" + }, + "engines": { + "node": ">=18.17.0" + } + }, "node_modules/@date-fns/tz": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.2.0.tgz", @@ -3067,6 +3184,12 @@ "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", "license": "MIT" }, + "node_modules/@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", + "license": "MIT" + }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", @@ -6077,6 +6200,12 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", + "license": "Unlicense" + }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -7327,6 +7456,12 @@ "node": ">=10.13.0" } }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" + }, "node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -8174,6 +8309,15 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -11203,6 +11347,22 @@ "dev": true, "license": "MIT" }, + "node_modules/standardwebhooks": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz", + "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==", + "license": "MIT", + "dependencies": { + "@stablelib/base64": "^1.0.0", + "fast-sha256": "^1.3.0" + } + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "license": "MIT" + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -11434,6 +11594,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swr": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.4.tgz", + "integrity": "sha512-bYd2lrhc+VarcpkgWclcUi92wYCpOgMws9Sd1hG1ntAu0NEy+14CbotuFjshBU2kt9rYj9TSmDcybpxpeTU1fg==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/tailwind-merge": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz", diff --git a/package.json b/package.json index 53feffa..6fd632b 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,8 @@ "lint": "eslint ." }, "dependencies": { + "@clerk/nextjs": "^6.36.7", + "@clerk/themes": "^2.4.47", "@hookform/resolvers": "^3.10.0", "@radix-ui/react-accordion": "1.2.2", "@radix-ui/react-alert-dialog": "1.1.4", diff --git a/src/app/(auth)/layout.tsx b/src/app/(auth)/layout.tsx new file mode 100644 index 0000000..afdbe43 --- /dev/null +++ b/src/app/(auth)/layout.tsx @@ -0,0 +1,7 @@ +export default function AuthLayout({ + children, +}: { + children: React.ReactNode; +}) { + return
{children}
; +} diff --git a/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx b/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx new file mode 100644 index 0000000..927a2b7 --- /dev/null +++ b/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx @@ -0,0 +1,19 @@ +import { SignIn } from "@clerk/nextjs"; + +// Force dynamic rendering - Clerk components require runtime +export const dynamic = "force-dynamic"; + +export default function SignInPage() { + return ( +
+ +
+ ); +} diff --git a/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx b/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx new file mode 100644 index 0000000..d331e75 --- /dev/null +++ b/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx @@ -0,0 +1,19 @@ +import { SignUp } from "@clerk/nextjs"; + +// Force dynamic rendering - Clerk components require runtime +export const dynamic = "force-dynamic"; + +export default function SignUpPage() { + return ( +
+ +
+ ); +} diff --git a/src/app/dashboard/layout.tsx b/src/app/dashboard/layout.tsx new file mode 100644 index 0000000..aaa1994 --- /dev/null +++ b/src/app/dashboard/layout.tsx @@ -0,0 +1,14 @@ +import { Header } from "@/components/header"; + +export default function DashboardLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + <> +
+ {children} + + ); +} diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx new file mode 100644 index 0000000..35643c3 --- /dev/null +++ b/src/app/dashboard/page.tsx @@ -0,0 +1,52 @@ +import { currentUser } from "@clerk/nextjs/server"; +import { redirect } from "next/navigation"; + +// Force dynamic rendering - this page requires auth +export const dynamic = "force-dynamic"; + +export default async function DashboardPage() { + const user = await currentUser(); + + if (!user) { + redirect("/sign-in"); + } + + const displayName = + user.firstName || user.emailAddresses[0]?.emailAddress || "User"; + + return ( +
+
+

+ Welcome, {displayName} +

+

Your RepliMap Dashboard

+ +
+
+

License

+

Free Tier

+

+ 3 scans remaining this month +

+
+ +
+

Usage

+

0 Scans

+

+ 0 resources analyzed +

+
+ +
+

Quick Start

+ + pip install replimap + +
+
+
+
+ ); +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 85eb7df..67afbcb 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,6 @@ import type { Metadata } from "next"; import localFont from "next/font/local"; +import { ClerkProviderWrapper } from "@/components/clerk-provider"; import { ThemeProvider } from "@/components/theme-provider"; import "./globals.css"; @@ -98,27 +99,29 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - - - {/* JSON-LD for SEO */} -