Skip to content

Commit 109c65d

Browse files
Resolved merge conflict in onboarding page
2 parents de8e209 + d5790ce commit 109c65d

2 files changed

Lines changed: 190 additions & 0 deletions

File tree

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
"use client";
2+
3+
import { useEffect, useState } from "react";
4+
import { cn } from "@/lib/utils";
5+
import { Check } from "lucide-react";
6+
7+
// Section configuration - maps to each wrapper component
8+
const SECTIONS = [
9+
{ id: "about-section", label: "About Us" },
10+
{ id: "software", label: "Software Development" },
11+
{ id: "expect", label: "What to Expect" },
12+
{ id: "roadmap", label: "Your Roadmap" },
13+
{ id: "house-rules", label: "House Rules" },
14+
{ id: "team", label: "Meet the Team" },
15+
{ id: "partners", label: "Our Partners" },
16+
{ id: "welcome", label: "Welcome Aboard" },
17+
];
18+
19+
export default function OnboardingStepper() {
20+
const [activeSection, setActiveSection] = useState<string>(SECTIONS[0]?.id || "about-section");
21+
22+
useEffect(() => {
23+
// Intersection Observer to track which section is in view
24+
const observerOptions = {
25+
root: null,
26+
rootMargin: "-50% 0px -50% 0px", // Trigger when section crosses middle of viewport
27+
threshold: 0,
28+
};
29+
30+
const observerCallback = (entries: IntersectionObserverEntry[]) => {
31+
entries.forEach((entry) => {
32+
if (entry.isIntersecting) {
33+
setActiveSection(entry.target.id);
34+
}
35+
});
36+
};
37+
38+
const observer = new IntersectionObserver(
39+
observerCallback,
40+
observerOptions
41+
);
42+
43+
// Observe each section
44+
SECTIONS.forEach(({ id }) => {
45+
const element = document.getElementById(id);
46+
if (element) observer.observe(element);
47+
});
48+
49+
return () => observer.disconnect();
50+
}, []);
51+
52+
// Scroll to section when circle is clicked
53+
const scrollToSection = (sectionId: string) => {
54+
const element = document.getElementById(sectionId);
55+
if (element) {
56+
element.scrollIntoView({ behavior: "smooth", block: "center" });
57+
}
58+
};
59+
60+
return (
61+
<nav
62+
className="fixed left-6 top-1/2 z-50 hidden -translate-y-1/2 lg:block"
63+
aria-label="Onboarding progress"
64+
>
65+
<div className="relative flex flex-col">
66+
{/* Connecting line background */}
67+
<div className="absolute left-2 top-2 h-[calc(100%-16px)] w-0.5 bg-gradient-to-b from-gray-300 to-gray-200 dark:from-gray-700 dark:to-gray-800" />
68+
69+
{/* Progress line that fills as you scroll */}
70+
<div
71+
className="absolute left-2 top-2 w-0.5 bg-gradient-to-b from-cyan-500 via-blue-500 to-purple-600 transition-all duration-500 ease-out"
72+
style={{
73+
height: `${(SECTIONS.findIndex((s) => s.id === activeSection) / (SECTIONS.length - 1)) * 100}%`
74+
}}
75+
/>
76+
77+
{SECTIONS.map((section, index) => {
78+
const isActive = activeSection === section.id;
79+
const isPassed = SECTIONS.findIndex((s) => s.id === activeSection) > index;
80+
const isLast = index === SECTIONS.length - 1;
81+
82+
return (
83+
<div
84+
key={section.id}
85+
className={cn(
86+
"relative z-10",
87+
!isLast && "mb-8"
88+
)}
89+
>
90+
<button
91+
onClick={() => scrollToSection(section.id)}
92+
className="group relative flex items-center"
93+
aria-label={`Go to ${section.label}`}
94+
aria-current={isActive ? "step" : undefined}
95+
>
96+
{/* Circle indicator */}
97+
<div
98+
className={cn(
99+
"relative flex h-5 w-5 items-center justify-center rounded-full border-2 transition-all duration-300",
100+
"hover:scale-110 focus-visible:scale-110 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2",
101+
isActive && "scale-125 border-transparent bg-gradient-to-br from-cyan-500 via-blue-600 to-purple-600 shadow-lg shadow-blue-500/50",
102+
isPassed && !isActive && "border-blue-600 bg-blue-600 shadow-md shadow-blue-500/30",
103+
!isActive && !isPassed && "border-gray-400 bg-white dark:border-gray-600 dark:bg-gray-900"
104+
)}
105+
>
106+
{/* Checkmark for passed sections */}
107+
{isPassed && !isActive && (
108+
<Check className="h-3 w-3 text-white" strokeWidth={3} />
109+
)}
110+
111+
{/* Pulsing ring for active section */}
112+
{isActive && (
113+
<>
114+
<div className="absolute inset-0 rounded-full bg-gradient-to-br from-cyan-500 via-blue-600 to-purple-600 animate-ping opacity-75" />
115+
<div className="absolute inset-0 rounded-full bg-white dark:bg-gray-900" style={{ transform: "scale(0.4)" }} />
116+
</>
117+
)}
118+
</div>
119+
120+
{/* Enhanced tooltip label on hover */}
121+
<div className="pointer-events-none absolute left-8 flex items-center gap-2">
122+
<div className="h-px w-3 bg-gradient-to-r from-gray-400 to-transparent opacity-0 transition-all duration-200 group-hover:w-4 group-hover:opacity-100 dark:from-gray-600" />
123+
<span
124+
className={cn(
125+
"whitespace-nowrap rounded-lg px-4 py-2 text-sm font-medium shadow-xl backdrop-blur-sm transition-all duration-200",
126+
"border border-gray-200 bg-white/95 text-gray-900 dark:border-gray-700 dark:bg-gray-800/95 dark:text-white",
127+
"opacity-0 -translate-x-2 group-hover:translate-x-0 group-hover:opacity-100",
128+
isActive && "border-blue-500/20 bg-gradient-to-r from-blue-50 to-purple-50 dark:from-blue-950/50 dark:to-purple-950/50"
129+
)}
130+
>
131+
{section.label}
132+
{isPassed && !isActive && (
133+
<span className="ml-2 text-xs text-blue-600 dark:text-blue-400">✓</span>
134+
)}
135+
</span>
136+
</div>
137+
</button>
138+
</div>
139+
);
140+
})}
141+
</div>
142+
</nav>
143+
);
144+
}

‎apps/codebility/app/auth/onboarding/page.tsx‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
<<<<<<< HEAD
12
// app/auth/onboarding/page.tsx (patched)
23

4+
=======
5+
>>>>>>> origin/dev
36
"use client";
47

58
import { useRef } from "react";
@@ -12,6 +15,10 @@ import IsAndIsntSectionWrapper from "./IsAndIsntSectionWrapper";
1215
import IsNotSectionWrapper from "./IsNotSectionWrapper";
1316
import MindsetSectionWrapper from "./MindsetSectionWrapper";
1417
import OnboardingClientWrapper from "./OnboardingClientWrapper";
18+
19+
20+
import OnboardingStepper from "./OnboardingStepper";
21+
1522
import PartnersSectionWrapper from "./PartnersSectionWrapper";
1623
import RoadMapWrapper from "./RoadMapSectionWrapper";
1724
import SoftwareSectionWrapper from "./SoftwareSectionWrapper";
@@ -23,6 +30,7 @@ export default function OnboardingPage() {
2330

2431
return (
2532
<main className="relative min-h-screen">
33+
2634
<OnboardingClientWrapper />
2735
<SoftwareSectionWrapper />
2836
{/* <MindsetSectionWrapper /> */}
@@ -38,3 +46,41 @@ export default function OnboardingPage() {
3846
</main>
3947
);
4048
}
49+
50+
{/* Vertical stepper navigation */}
51+
<OnboardingStepper />
52+
53+
<OnboardingClientWrapper />
54+
55+
{/* Each section wrapped with ID for stepper tracking */}
56+
<section id="software">
57+
<SoftwareSectionWrapper />
58+
</section>
59+
60+
<section id="expect">
61+
<ExpectSectionWrapper />
62+
</section>
63+
64+
<section id="roadmap">
65+
<RoadMapWrapper roadmapRef={roadmapRef} />
66+
</section>
67+
68+
<section id="house-rules">
69+
<HouseRulesSectionWrapper />
70+
</section>
71+
72+
<section id="team">
73+
<TeamSectionWrapper />
74+
</section>
75+
76+
<section id="partners">
77+
<PartnersSectionWrapper />
78+
</section>
79+
80+
<section id="welcome">
81+
<WellcomeSectionWrapper />
82+
</section>
83+
</main>
84+
);
85+
}
86+

0 commit comments

Comments
 (0)