Skip to content

Commit 4ca81f1

Browse files
authored
Merge pull request #250 from apsinghdev/imp/add-pitch
[imp]: add pitch to investors
2 parents 95cd4cd + ba50039 commit 4ca81f1

8 files changed

Lines changed: 851 additions & 52 deletions

File tree

apps/web/src/app/(main)/(landing)/pitch/page.tsx

Lines changed: 723 additions & 0 deletions
Large diffs are not rendered by default.

apps/web/src/app/(main)/(landing)/pricing/page.tsx

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ import { motion } from "framer-motion";
66
import { Check, CornerDownRight, Target, Terminal } from "lucide-react";
77
import Image from "next/image";
88
import Link from "next/link";
9-
import React from "react";
9+
import React, { useEffect } from "react";
1010
import PrimaryButton from "@/components/ui/custom-button";
1111
import PaymentFlow from "@/components/payment/PaymentFlow";
1212
import { ActiveTag } from "@/components/ui/ActiveTag";
13+
import { usePathname } from "next/navigation";
1314
const opensoxFeatures = [
1415
{
1516
id: 1,
@@ -66,7 +67,7 @@ const whySub = [
6667
},
6768
{
6869
content:
69-
"This offer is only available for the first 1000 (20 slots booked) users",
70+
"This offer is only available for the first 1000 (64 slots booked) users",
7071
},
7172
{
7273
content:
@@ -109,6 +110,28 @@ const premiumPlanCard = {
109110
};
110111

111112
const Pricing = () => {
113+
const pathname = usePathname();
114+
const callbackUrl = `${pathname}#pro-price-card`;
115+
116+
useEffect(() => {
117+
if (window.location.hash === "#pro-price-card") {
118+
const element = document.getElementById("pro-price-card");
119+
if (element) {
120+
setTimeout(() => {
121+
element.scrollIntoView({ behavior: "smooth", block: "start" });
122+
}, 100);
123+
}
124+
}
125+
if (window.location.hash === "#testimonials") {
126+
const element = document.getElementById("testimonials");
127+
if (element) {
128+
setTimeout(() => {
129+
element.scrollIntoView({ behavior: "smooth", block: "start" });
130+
}, 100);
131+
}
132+
}
133+
}, []);
134+
112135
return (
113136
<>
114137
<main className="w-full overflow-hidden flex flex-col items-center justify-center relative">
@@ -247,7 +270,7 @@ const Pricing = () => {
247270
</div>
248271
<div className="flex flex-col lg:flex-row items-stretch justify-center gap-6">
249272
<PricingCard />
250-
<SecondaryPricingCard />
273+
<SecondaryPricingCard callbackUrl={callbackUrl} />
251274
</div>
252275
</div>
253276
</div>
@@ -347,7 +370,7 @@ const PricingCard = () => {
347370
);
348371
};
349372

350-
const SecondaryPricingCard = () => {
373+
const SecondaryPricingCard = ({ callbackUrl }: { callbackUrl: string }) => {
351374
const premiumPlanId = process.env.NEXT_PUBLIC_YEARLY_PREMIUM_PLAN_ID;
352375
const planIdOk =
353376
typeof premiumPlanId === "string" && premiumPlanId.length > 0;
@@ -375,7 +398,10 @@ const SecondaryPricingCard = () => {
375398
</div>
376399
</div>
377400

378-
<div className="w-full border-dashed border-border-primary px-6 lg:px-10 py-4">
401+
<div
402+
id="pro-price-card"
403+
className="w-full border-dashed border-border-primary px-6 lg:px-10 py-4"
404+
>
379405
<div className="flex items-center gap-4 flex-wrap">
380406
<h2 className="text-6xl lg:text-[90px] lg:leading-[82px] tracking-tight font-semibold">
381407
$49{" "}
@@ -401,7 +427,16 @@ const SecondaryPricingCard = () => {
401427
buttonClassName={`w-full max-w-[500px] mx-auto font-semibold ${
402428
planIdOk ? "" : "opacity-60 cursor-not-allowed"
403429
}`}
430+
callbackUrl={callbackUrl}
404431
/>
432+
<div className="flex justify-center mt-3">
433+
<Link
434+
href="/pitch"
435+
className="text-sm text-text-tertiary hover:text-brand-purple-light transition-colors lowercase"
436+
>
437+
still not sure? read my pitch to you.
438+
</Link>
439+
</div>
405440
</div>
406441
<div className="w-full border-dashed border-border-primary px-6 lg:px-10 py-4 flex flex-col gap-4 flex-1">
407442
<h2 className="text-lg lg:text-xl tracking-tight text-left font-bold">
@@ -552,7 +587,7 @@ const TestimonialsSection = () => {
552587
};
553588

554589
return (
555-
<div className=" text-white ">
590+
<div className=" text-white " id="testimonials">
556591
<Header title="What our Pro customers say about us" />
557592
<div className="border-b border-[#252525] w-full min-h-[80dvh] grid grid-cols-1 lg:grid-cols-7">
558593
<div className="lg:col-span-2 flex flex-col font-medium divide-y divide-[#252525]">

apps/web/src/components/landing-sections/CTA.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const CTA = () => {
1515
className="absolute inset-0 w-full h-full -z-10 object-cover rounded-3xl"
1616
/>
1717
<div className="space-y-2">
18-
<h2 className='text-4xl text-[40px] w-full lg:text-7xl font-medium text-balance text-center max-w-2xl tracking-tighter'>Ready to Find Your Perfect Repo?</h2>
18+
<h2 className='text-4xl text-[40px] w-full lg:text-7xl font-medium text-balance text-center max-w-2xl tracking-tighter'>Ready to dive into Open Source?</h2>
1919
<p className="text-center tracking-tight lg:text-2xl font-light">Join 10,000+ engineers accelerating in open-source.</p>
2020
</div>
2121
<Link href='/dashboard/home' className='cursor-pointer z-30'>

apps/web/src/components/landing-sections/Hero.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,20 @@ const Hero = () => {
5151
variants={itemVariants}
5252
className="flex items-center justify-center gap-2 mb-4 [will-change:transform,opacity] motion-reduce:transition-none motion-reduce:transform-none"
5353
>
54-
<div className="inline-flex items-center gap-2 px-4 py-2 rounded-md bg-black/40 backdrop-blur-sm border border">
55-
<span className="text-text-secondary text-sm font-medium">Backed by</span>
54+
<Link
55+
href="/pitch"
56+
className="inline-flex items-center gap-2 px-4 py-2 rounded-md bg-black/40 backdrop-blur-sm border border hover:bg-black/60 transition-colors cursor-pointer"
57+
>
58+
<span className="text-text-secondary text-sm font-medium">
59+
Backed by
60+
</span>
5661
<div className="flex items-center gap-1.5">
5762
<div className="w-5 h-5 bg-gradient-to-br from-[#FF6154] to-[#FF8C00] rounded flex items-center justify-center">
5863
<span className="text-white text-xs font-bold">U</span>
5964
</div>
6065
<span className="text-white text-sm font-medium">sers</span>
6166
</div>
62-
</div>
67+
</Link>
6368
</motion.div>
6469
<motion.h1
6570
variants={itemVariants}

apps/web/src/components/landing-sections/footer.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ const Footer = () => {
6565
>
6666
Pricing
6767
</Link>
68+
<Link
69+
href="/pitch"
70+
className="text-[#b1b1b1] hover:text-white transition-colors text-xs"
71+
>
72+
My pitch to you
73+
</Link>
6874
<Link
6975
href="/blogs"
7076
className="text-[#b1b1b1] hover:text-white transition-colors text-xs"

apps/web/src/components/payment/PaymentFlow.tsx

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ interface PaymentFlowProps {
1414
description?: string;
1515
buttonText?: string;
1616
buttonClassName?: string;
17+
callbackUrl?: string;
1718
}
1819

1920
/**
@@ -41,6 +42,7 @@ const PaymentFlow: React.FC<PaymentFlowProps> = ({
4142
description = "Payment",
4243
buttonText = "Invest",
4344
buttonClassName,
45+
callbackUrl,
4446
}) => {
4547
const { data: session, status: sessionStatus } = useSession();
4648
const router = useRouter();
@@ -50,6 +52,7 @@ const PaymentFlow: React.FC<PaymentFlowProps> = ({
5052
amount: number; // Stored for display purposes only
5153
} | null>(null);
5254

55+
const utils = trpc.useUtils();
5356
const createOrderMutation = (trpc.payment as any).createOrder.useMutation();
5457
const verifyPaymentMutation = (
5558
trpc.payment as any
@@ -71,7 +74,26 @@ const PaymentFlow: React.FC<PaymentFlowProps> = ({
7174
planId: planId,
7275
});
7376

74-
// Show success and redirect
77+
// payment verification succeeded - proceed with redirect
78+
// subscription cache refresh is decoupled as best-effort background action
79+
// errors in refresh won't affect the successful payment verification
80+
(async () => {
81+
try {
82+
await (utils.user as any).subscriptionStatus.invalidate();
83+
await Promise.race([
84+
(utils.user as any).subscriptionStatus.fetch(undefined),
85+
new Promise((resolve) => setTimeout(resolve, 3000)), // 3s timeout
86+
]);
87+
} catch (refreshError) {
88+
console.warn(
89+
"subscription cache refresh failed (non-fatal):",
90+
refreshError
91+
);
92+
}
93+
})();
94+
95+
// redirect immediately after successful verification
96+
// checkout page will refetch subscription status if cache refresh failed
7597
router.push("/checkout");
7698
} catch (error) {
7799
console.error("Verification failed:", error);
@@ -101,7 +123,8 @@ const PaymentFlow: React.FC<PaymentFlowProps> = ({
101123
}
102124

103125
if (sessionStatus === "unauthenticated" || !session) {
104-
router.push("/login?callbackUrl=/pricing");
126+
const redirectUrl = callbackUrl || "/pricing";
127+
router.push(`/login?callbackUrl=${encodeURIComponent(redirectUrl)}`);
105128
return;
106129
}
107130

@@ -152,7 +175,8 @@ const PaymentFlow: React.FC<PaymentFlowProps> = ({
152175
} catch (error: any) {
153176
console.warn("Failed to create order:", error);
154177
setIsProcessing(false);
155-
router.push("/login?callbackUrl=/pricing");
178+
const redirectUrl = callbackUrl || "/pricing";
179+
router.push(`/login?callbackUrl=${encodeURIComponent(redirectUrl)}`);
156180
}
157181
};
158182

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,46 @@
1-
'use client'
2-
import { motion } from 'framer-motion'
3-
import React from 'react'
4-
import { FlickeringGrid } from './flickering-grid'
5-
import { colors } from '@/lib/design-tokens'
1+
"use client";
2+
import { motion } from "framer-motion";
3+
import React from "react";
4+
import { FlickeringGrid } from "./flickering-grid";
5+
import { colors } from "@/lib/design-tokens";
66

7-
const Header = ({title}: {title: string}) => {
8-
return (
9-
<div className="px-[30px] py-10 h-[160px] relative overflow-hidden border-b border w-full">
10-
<motion.h4
11-
initial={{ opacity: 0, y: 30, filter: 'blur(10px)' }}
12-
animate={{ opacity: 1, y: 0, filter: 'blur(0px)' }}
13-
transition={{ duration: 0.6, ease: 'easeOut', type: 'spring', delay: 0.3 }}
14-
className='font-medium inset-0 flex items-center justify-center text-3xl lg:text-5xl tracking-tight absolute z-30 text-center text-balance'
15-
>
16-
{title}
17-
</motion.h4>
18-
<div
19-
style={{
20-
background: 'radial-gradient(circle at center, #101010 30%, transparent 100%)',
21-
}}
22-
className="h-full w-[100%] right-0 top-0 z-20 absolute"
23-
/>
24-
<div className="absolute right-0 w-[100%] h-full top-0 z-10 opacity-50">
25-
<FlickeringGrid
26-
className="absolute -z-0 top-0 right-0"
27-
squareSize={3}
28-
gridGap={6}
29-
color={colors.brand.purple.grid}
30-
maxOpacity={1}
31-
flickerChance={0.1}
32-
height={200}
33-
width={2000}
34-
/>
35-
</div>
36-
</div>
37-
)
38-
}
7+
const Header = ({ title }: { title: string | React.ReactNode }) => {
8+
return (
9+
<div className="px-[30px] py-10 h-[160px] relative overflow-hidden border-b border w-full">
10+
<motion.h4
11+
initial={{ opacity: 0, y: 30, filter: "blur(10px)" }}
12+
animate={{ opacity: 1, y: 0, filter: "blur(0px)" }}
13+
transition={{
14+
duration: 0.6,
15+
ease: "easeOut",
16+
type: "spring",
17+
delay: 0.3,
18+
}}
19+
className="font-medium inset-0 flex items-center justify-center text-3xl lg:text-5xl tracking-tight absolute z-30 text-center text-balance"
20+
>
21+
{title}
22+
</motion.h4>
23+
<div
24+
style={{
25+
background:
26+
"radial-gradient(circle at center, #101010 30%, transparent 100%)",
27+
}}
28+
className="h-full w-[100%] right-0 top-0 z-20 absolute"
29+
/>
30+
<div className="absolute right-0 w-[100%] h-full top-0 z-10 opacity-50">
31+
<FlickeringGrid
32+
className="absolute -z-0 top-0 right-0"
33+
squareSize={3}
34+
gridGap={6}
35+
color={colors.brand.purple.grid}
36+
maxOpacity={1}
37+
flickerChance={0.1}
38+
height={200}
39+
width={2000}
40+
/>
41+
</div>
42+
</div>
43+
);
44+
};
3945

40-
export default Header
46+
export default Header;

apps/web/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"moduleResolution": "bundler",
1616
"resolveJsonModule": true,
1717
"isolatedModules": true,
18-
"jsx": "react-jsx",
18+
"jsx": "preserve",
1919
"incremental": true,
2020
"plugins": [
2121
{

0 commit comments

Comments
 (0)