Ashraf Al-Kassem Claude Sonnet 4.6 commited on
Commit
e284c06
·
1 Parent(s): 8274810

fix(routing): fix broken sign-in/signup links, admin redirect, and Google OAuth

Browse files

- Website CTAs (Header, Footer, CTASection, PlanCard, plans page) now use
<a href="/app/..."> for cross-app navigation — Next.js <Link> was silently
doing SPA navigation within the Website, which has no /login or /signup routes.
- APP_URL changed from hardcoded localhost:3000 to /app (relative, works in any env).
- nginx admin redirect changed from exact-match to regex so /admin/login,
/admin/users, etc. are all covered.
- Google OAuth redirect_uri now includes the /app basePath prefix to match the
actual callback route at /app/auth/google/callback.

NOTE: Google Cloud Console must also have /app/auth/google/callback added as an
authorized redirect URI for Google OAuth to work end-to-end.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Website/next-env.d.ts CHANGED
@@ -1,6 +1,6 @@
1
  /// <reference types="next" />
2
  /// <reference types="next/image-types/global" />
3
- import "./.next/dev/types/routes.d.ts";
4
 
5
  // NOTE: This file should not be edited
6
  // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
 
1
  /// <reference types="next" />
2
  /// <reference types="next/image-types/global" />
3
+ import "./.next/types/routes.d.ts";
4
 
5
  // NOTE: This file should not be edited
6
  // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
Website/src/app/about/page.tsx CHANGED
@@ -177,7 +177,7 @@ export default function AboutPage() {
177
  headline="Ready to work with us?"
178
  subheadline="Start with the Free plan or sign up to see LeadPilot in action."
179
  primaryLabel="Start for Free"
180
- primaryHref="/signup"
181
  secondaryLabel="Contact Us"
182
  secondaryHref="/contact"
183
  />
 
177
  headline="Ready to work with us?"
178
  subheadline="Start with the Free plan or sign up to see LeadPilot in action."
179
  primaryLabel="Start for Free"
180
+ primaryHref="/app/signup"
181
  secondaryLabel="Contact Us"
182
  secondaryHref="/contact"
183
  />
Website/src/app/plans/page.tsx CHANGED
@@ -10,7 +10,7 @@ import { useCatalog, type PlanCatalogEntry } from "@/lib/catalog";
10
  const tiers = [
11
  {
12
  name: "Free", tagline: "Start capturing and qualifying leads today", price: "$0", priceNote: "/ month, forever",
13
- available: true, highlighted: true, ctaLabel: "Get Started Free", ctaHref: "/signup",
14
  features: ["Lead capture (limited monthly volume)", "AI qualification prompts (basic)", "Manual routing to team members", "Email notifications on new leads", "Basic analytics dashboard", "1 team seat", "Community support"],
15
  notIncluded: ["Automated routing rules", "CRM integrations", "Advanced AI playbooks", "Priority support"],
16
  },
@@ -240,9 +240,9 @@ export default function PlansPage() {
240
  </ul>
241
 
242
  {tier.available ? (
243
- <Link href={tier.ctaHref} className="block text-center px-6 py-3 rounded-xl font-semibold text-white text-sm mk-btn-primary">
244
  {tier.ctaLabel}
245
- </Link>
246
  ) : (
247
  <a href="#waitlist" className="block text-center px-6 py-3 rounded-xl font-semibold text-sm mk-coming-soon-btn">
248
  Join Waitlist
 
10
  const tiers = [
11
  {
12
  name: "Free", tagline: "Start capturing and qualifying leads today", price: "$0", priceNote: "/ month, forever",
13
+ available: true, highlighted: true, ctaLabel: "Get Started Free", ctaHref: "/app/signup",
14
  features: ["Lead capture (limited monthly volume)", "AI qualification prompts (basic)", "Manual routing to team members", "Email notifications on new leads", "Basic analytics dashboard", "1 team seat", "Community support"],
15
  notIncluded: ["Automated routing rules", "CRM integrations", "Advanced AI playbooks", "Priority support"],
16
  },
 
240
  </ul>
241
 
242
  {tier.available ? (
243
+ <a href={tier.ctaHref} className="block text-center px-6 py-3 rounded-xl font-semibold text-white text-sm mk-btn-primary">
244
  {tier.ctaLabel}
245
+ </a>
246
  ) : (
247
  <a href="#waitlist" className="block text-center px-6 py-3 rounded-xl font-semibold text-sm mk-coming-soon-btn">
248
  Join Waitlist
Website/src/app/use-cases/page.tsx CHANGED
@@ -222,7 +222,7 @@ export default function UseCasesPage() {
222
  headline="Ready to try it in your business?"
223
  subheadline="Start with the Free plan — no credit card, no commitment."
224
  primaryLabel="Start for Free"
225
- primaryHref="/signup"
226
  secondaryLabel="See Plans"
227
  secondaryHref="/plans"
228
  />
 
222
  headline="Ready to try it in your business?"
223
  subheadline="Start with the Free plan — no credit card, no commitment."
224
  primaryLabel="Start for Free"
225
+ primaryHref="/app/signup"
226
  secondaryLabel="See Plans"
227
  secondaryHref="/plans"
228
  />
Website/src/components/CTASection.tsx CHANGED
@@ -14,7 +14,7 @@ export default function CTASection({
14
  headline = "Your leads aren't waiting. Neither should you.",
15
  subheadline = "Start capturing and qualifying leads today — free, no credit card required.",
16
  primaryLabel = "Start for Free",
17
- primaryHref = "/signup",
18
  secondaryLabel = "See Product",
19
  secondaryHref = "/product",
20
  }: Props) {
@@ -55,12 +55,12 @@ export default function CTASection({
55
  </AnimateOnScroll>
56
  <AnimateOnScroll animation="zoomIn" delay={160}>
57
  <div className="flex flex-col sm:flex-row items-center justify-center gap-4">
58
- <Link
59
  href={primaryHref}
60
  className="px-9 py-4 rounded-xl font-semibold text-base mk-btn-primary"
61
  >
62
  {primaryLabel}
63
- </Link>
64
  <Link
65
  href={secondaryHref}
66
  className="px-9 py-4 rounded-xl font-semibold text-sm mk-btn-ghost-dark"
 
14
  headline = "Your leads aren't waiting. Neither should you.",
15
  subheadline = "Start capturing and qualifying leads today — free, no credit card required.",
16
  primaryLabel = "Start for Free",
17
+ primaryHref = "/app/signup",
18
  secondaryLabel = "See Product",
19
  secondaryHref = "/product",
20
  }: Props) {
 
55
  </AnimateOnScroll>
56
  <AnimateOnScroll animation="zoomIn" delay={160}>
57
  <div className="flex flex-col sm:flex-row items-center justify-center gap-4">
58
+ <a
59
  href={primaryHref}
60
  className="px-9 py-4 rounded-xl font-semibold text-base mk-btn-primary"
61
  >
62
  {primaryLabel}
63
+ </a>
64
  <Link
65
  href={secondaryHref}
66
  className="px-9 py-4 rounded-xl font-semibold text-sm mk-btn-ghost-dark"
Website/src/components/Footer.tsx CHANGED
@@ -11,7 +11,7 @@ const cols = {
11
  Company: [
12
  { label: "About", href: "/about" },
13
  { label: "Contact", href: "/contact" },
14
- { label: "Start Free", href: "/signup" },
15
  ],
16
  Legal: [
17
  { label: "Privacy Policy", href: "#" },
@@ -79,12 +79,12 @@ export default function MarketingFooter() {
79
  </div>
80
 
81
  {/* Mini CTA */}
82
- <Link
83
- href="/signup"
84
  className="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl text-sm font-semibold text-white mk-btn-primary"
85
  >
86
  Start for Free →
87
- </Link>
88
  </div>
89
 
90
  {/* Nav columns */}
@@ -99,9 +99,11 @@ export default function MarketingFooter() {
99
  <ul className="space-y-3" role="list">
100
  {links.map(({ label, href }) => (
101
  <li key={label}>
102
- <Link href={href} className="text-sm mk-footer-link">
103
- {label}
104
- </Link>
 
 
105
  </li>
106
  ))}
107
  </ul>
 
11
  Company: [
12
  { label: "About", href: "/about" },
13
  { label: "Contact", href: "/contact" },
14
+ { label: "Start Free", href: "/app/signup" },
15
  ],
16
  Legal: [
17
  { label: "Privacy Policy", href: "#" },
 
79
  </div>
80
 
81
  {/* Mini CTA */}
82
+ <a
83
+ href="/app/signup"
84
  className="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl text-sm font-semibold text-white mk-btn-primary"
85
  >
86
  Start for Free →
87
+ </a>
88
  </div>
89
 
90
  {/* Nav columns */}
 
99
  <ul className="space-y-3" role="list">
100
  {links.map(({ label, href }) => (
101
  <li key={label}>
102
+ {href.startsWith("/app") ? (
103
+ <a href={href} className="text-sm mk-footer-link">{label}</a>
104
+ ) : (
105
+ <Link href={href} className="text-sm mk-footer-link">{label}</Link>
106
+ )}
107
  </li>
108
  ))}
109
  </ul>
Website/src/components/Header.tsx CHANGED
@@ -118,18 +118,18 @@ export default function MarketingHeader() {
118
 
119
  {/* Desktop CTAs */}
120
  <div className="hidden md:flex items-center gap-3">
121
- <Link
122
- href="/login"
123
  className="text-sm font-medium transition-colors duration-200 mk-footer-link px-2 py-1"
124
  >
125
  Log in
126
- </Link>
127
- <Link
128
- href="/signup"
129
  className="px-4 py-2 rounded-lg text-sm font-semibold text-white mk-btn-primary"
130
  >
131
  Start for Free
132
- </Link>
133
  </div>
134
 
135
  {/* Mobile toggle */}
@@ -199,20 +199,20 @@ export default function MarketingHeader() {
199
  className="flex flex-col gap-2.5 mt-4 pt-4"
200
  style={{ borderTop: "1px solid rgba(255,255,255,0.07)" }}
201
  >
202
- <Link
203
- href="/login"
204
  className="block py-2.5 rounded-lg text-sm font-medium text-center mk-footer-link"
205
  onClick={() => setOpen(false)}
206
  >
207
  Log in
208
- </Link>
209
- <Link
210
- href="/signup"
211
  className="block py-2.5 rounded-xl text-sm font-semibold text-white text-center mk-btn-primary"
212
  onClick={() => setOpen(false)}
213
  >
214
  Start for Free
215
- </Link>
216
  </div>
217
  </div>
218
  </div>
 
118
 
119
  {/* Desktop CTAs */}
120
  <div className="hidden md:flex items-center gap-3">
121
+ <a
122
+ href="/app/login"
123
  className="text-sm font-medium transition-colors duration-200 mk-footer-link px-2 py-1"
124
  >
125
  Log in
126
+ </a>
127
+ <a
128
+ href="/app/signup"
129
  className="px-4 py-2 rounded-lg text-sm font-semibold text-white mk-btn-primary"
130
  >
131
  Start for Free
132
+ </a>
133
  </div>
134
 
135
  {/* Mobile toggle */}
 
199
  className="flex flex-col gap-2.5 mt-4 pt-4"
200
  style={{ borderTop: "1px solid rgba(255,255,255,0.07)" }}
201
  >
202
+ <a
203
+ href="/app/login"
204
  className="block py-2.5 rounded-lg text-sm font-medium text-center mk-footer-link"
205
  onClick={() => setOpen(false)}
206
  >
207
  Log in
208
+ </a>
209
+ <a
210
+ href="/app/signup"
211
  className="block py-2.5 rounded-xl text-sm font-semibold text-white text-center mk-btn-primary"
212
  onClick={() => setOpen(false)}
213
  >
214
  Start for Free
215
+ </a>
216
  </div>
217
  </div>
218
  </div>
Website/src/components/PlanCard.tsx CHANGED
@@ -16,7 +16,7 @@ interface Props {
16
 
17
  export default function PlanCard({
18
  name, tagline, price, priceNote, features, notIncluded = [],
19
- ctaLabel, ctaHref = "/signup", available, highlighted = false,
20
  }: Props) {
21
  return (
22
  <div
@@ -79,9 +79,9 @@ export default function PlanCard({
79
  </ul>
80
 
81
  {available ? (
82
- <Link href={ctaHref} className="block text-center px-6 py-3 rounded-xl font-semibold text-sm mk-btn-primary">
83
  {ctaLabel}
84
- </Link>
85
  ) : (
86
  <a href="#waitlist" className="block text-center px-6 py-3 rounded-xl font-semibold text-sm mk-coming-soon-btn">
87
  Join Waitlist
 
16
 
17
  export default function PlanCard({
18
  name, tagline, price, priceNote, features, notIncluded = [],
19
+ ctaLabel, ctaHref = "/app/signup", available, highlighted = false,
20
  }: Props) {
21
  return (
22
  <div
 
79
  </ul>
80
 
81
  {available ? (
82
+ <a href={ctaHref} className="block text-center px-6 py-3 rounded-xl font-semibold text-sm mk-btn-primary">
83
  {ctaLabel}
84
+ </a>
85
  ) : (
86
  <a href="#waitlist" className="block text-center px-6 py-3 rounded-xl font-semibold text-sm mk-coming-soon-btn">
87
  Join Waitlist
Website/src/lib/api.ts CHANGED
@@ -209,7 +209,7 @@ export interface CatalogTemplate {
209
  clone_count: number;
210
  }
211
 
212
- export const APP_URL = "http://localhost:3000";
213
 
214
  export async function getPlans(): Promise<CatalogPlan[]> {
215
  try {
 
209
  clone_count: number;
210
  }
211
 
212
+ export const APP_URL = "/app";
213
 
214
  export async function getPlans(): Promise<CatalogPlan[]> {
215
  try {
backend/app/api/v1/auth.py CHANGED
@@ -512,8 +512,9 @@ def get_google_redirect_uri() -> str:
512
 
513
  # Use the frontend base URL so the redirect_uri matches the frontend callback page,
514
  # not the backend server (which runs on a different port in local dev).
 
515
  base = (settings.APP_BASE_URL or settings.FRONTEND_URL).rstrip("/")
516
- redirect_uri = f"{base}/auth/google/callback"
517
  logger.info(f"Google OAuth: Generated redirect_uri={redirect_uri}")
518
  return redirect_uri
519
 
 
512
 
513
  # Use the frontend base URL so the redirect_uri matches the frontend callback page,
514
  # not the backend server (which runs on a different port in local dev).
515
+ # The Frontend app is mounted at /app (basePath), so the callback route is /app/auth/google/callback.
516
  base = (settings.APP_BASE_URL or settings.FRONTEND_URL).rstrip("/")
517
+ redirect_uri = f"{base}/app/auth/google/callback"
518
  logger.info(f"Google OAuth: Generated redirect_uri={redirect_uri}")
519
  return redirect_uri
520
 
nginx.conf CHANGED
@@ -42,8 +42,8 @@ http {
42
  location = /signup {
43
  return 301 /app/signup;
44
  }
45
- location = /admin {
46
- return 301 /app/admin;
47
  }
48
 
49
  # Backend API
 
42
  location = /signup {
43
  return 301 /app/signup;
44
  }
45
+ location ~ ^/admin(/.*)?$ {
46
+ return 301 /app$request_uri;
47
  }
48
 
49
  # Backend API