Skip to content

Commit 5b51ccb

Browse files
authored
Merge pull request #458 from CodebilityDev/dev
nov 17 updates
2 parents 868c7bc + 47cae05 commit 5b51ccb

11 files changed

Lines changed: 267 additions & 1138 deletions

File tree

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { cn } from "@/lib/utils";
55
import { Check } from "lucide-react";
66

77
// Section configuration - maps to each wrapper component
8+
// Line 9-17: Section labels array
89
const SECTIONS = [
910
{ id: "about-section", label: "About Us" },
1011
{ id: "software", label: "Software Development" },
@@ -13,7 +14,7 @@ const SECTIONS = [
1314
{ id: "house-rules", label: "House Rules" },
1415
{ id: "team", label: "Meet the Team" },
1516
{ id: "partners", label: "Our Partners" },
16-
{ id: "welcome", label: "Welcome Aboard" },
17+
{ id: "welcome", label: "Signup Now!" }, // ✅ CHANGED: "Welcome Aboard" → "Signup Now!"
1718
];
1819

1920
export default function OnboardingStepper() {
@@ -119,12 +120,15 @@ export default function OnboardingStepper() {
119120

120121
{/* Enhanced tooltip label on hover */}
121122
<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+
<div className={cn(
124+
"h-px w-3 bg-gradient-to-r from-gray-400 to-transparent transition-all duration-200 dark:from-gray-600",
125+
(isLast || isActive) ? "w-4 opacity-100" : "opacity-0 group-hover:w-4 group-hover:opacity-100"
126+
)} />
123127
<span
124128
className={cn(
125129
"whitespace-nowrap rounded-lg px-4 py-2 text-sm font-medium shadow-xl backdrop-blur-sm transition-all duration-200",
126130
"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",
131+
(isLast || isActive) ? "translate-x-0 opacity-100" : "opacity-0 -translate-x-2 group-hover:translate-x-0 group-hover:opacity-100",
128132
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"
129133
)}
130134
>

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

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
// app/auth/onboarding/page.tsx
21
"use client";
32

43
import { useRef } from "react";
4+
import { useRouter } from "next/navigation";
55
import ExpectSectionWrapper from "./ExpectSectionWrapper";
66
import HouseRulesSectionWrapper from "./HouseRulesSectionWrapper";
77
import OnboardingClientWrapper from "./OnboardingClientWrapper";
@@ -11,13 +11,20 @@ import RoadMapWrapper from "./RoadMapSectionWrapper";
1111
import SoftwareSectionWrapper from "./SoftwareSectionWrapper";
1212
import TeamSectionWrapper from "./TeamSectionWrapper";
1313
import WellcomeSectionWrapper from "./WellcomeSectionWrapper";
14+
import { Button } from "@codevs/ui/button";
1415

1516
export default function OnboardingPage() {
1617
const roadmapRef = useRef<HTMLDivElement>(null);
18+
const router = useRouter();
19+
20+
// Handle mobile signup button click
21+
const handleSignupClick = () => {
22+
router.push('/auth/sign-up');
23+
};
1724

1825
return (
1926
<main className="relative min-h-screen">
20-
{/* Vertical stepper navigation */}
27+
{/* Vertical stepper navigation (desktop only) */}
2128
<OnboardingStepper />
2229

2330
<OnboardingClientWrapper />
@@ -50,6 +57,36 @@ export default function OnboardingPage() {
5057
<section id="welcome">
5158
<WellcomeSectionWrapper />
5259
</section>
60+
61+
{/* ✅ NEW: Mobile-only persistent signup button (lines 48-67) */}
62+
<div className="fixed bottom-6 right-6 z-50 lg:hidden">
63+
<Button
64+
onClick={handleSignupClick}
65+
className="
66+
group
67+
relative
68+
overflow-hidden
69+
rounded-full
70+
bg-gradient-to-br from-cyan-500 via-blue-600 to-purple-600
71+
px-6 py-3
72+
font-semibold text-white
73+
shadow-2xl shadow-blue-500/30
74+
transition-all duration-300
75+
hover:scale-105 hover:shadow-blue-500/50
76+
active:scale-95
77+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2
78+
"
79+
aria-label="Go to signup page"
80+
>
81+
{/* Animated gradient background on hover */}
82+
<span className="absolute inset-0 bg-gradient-to-br from-cyan-400 via-blue-500 to-purple-500 opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
83+
84+
{/* Button text */}
85+
<span className="relative z-10 flex items-center gap-2">
86+
Signup Now!
87+
</span>
88+
</Button>
89+
</div>
5390
</main>
5491
);
5592
}

‎apps/codebility/app/home/applicants/_service/emailAction.ts‎

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const sendMultipleTestReminderEmail = async (emails: string[]) => {
2020
This is a friendly reminder to complete your application process with Codebility.
2121
2222
Steps to complete your application:
23-
1. Visit and Sign In to our and website: https://www.codebility.tech/careers
23+
1. Visit and Sign In to our website: https://www.codebility.tech/auth/sign-in
2424
2. Once Signed In, Click on "Status" in the navigation menu
2525
3. Click "Take the Test" button
2626
4. Read the instructions carefully
@@ -112,7 +112,7 @@ export const sendMultipleTestReminderEmail = async (emails: string[]) => {
112112
<div class="steps">
113113
<h3>Steps to complete your application:</h3>
114114
<ol>
115-
<li>Visit and Sign in to our website: <a href="https://www.codebility.tech/careers">https://www.codebility.tech/careers</a></li>
115+
<li>Visit and Sign in to our website: <a href="https://www.codebility.tech/auth/sign-in">https://www.codebility.tech/auth/sign-in</a></li>
116116
<li>Once Signed In, Click on "Status" in the navigation menu</li>
117117
<li>Click "Take the Test" button</li>
118118
<li>Read the instructions carefully</li>
@@ -179,7 +179,7 @@ export const sendTestReminder = async ({ email, name }: { email: string, name: s
179179
This is a friendly reminder to complete your application process with Codebility.
180180
181181
Steps to complete your application:
182-
1. Visit and Sign in to our and website: https://www.codebility.tech/careers
182+
1. Visit and Sign in to our website: https://www.codebility.tech/auth/sign-in
183183
2. Once Sign in, Click on "Status" in the navigation menu
184184
3. Click "Take the Test" button
185185
4. Read the instructions carefully
@@ -270,7 +270,7 @@ export const sendTestReminder = async ({ email, name }: { email: string, name: s
270270
<div class="steps">
271271
<h3>Steps to complete your application:</h3>
272272
<ol>
273-
<li>Visit and Sign in to our website: <a href="https://www.codebility.tech/careers">https://www.codebility.tech/careers</a></li>
273+
<li>Visit and Sign in to our website: <a href="https://www.codebility.tech/auth/sign-in">https://www.codebility.tech/auth/sign-in</a></li>
274274
<li>Once Signed, In Click on "Status" in the navigation menu</li>
275275
<li>Click "Take the Test" button</li>
276276
<li>Read the instructions carefully</li>
@@ -337,7 +337,7 @@ export const sendMultipleOnboardingReminder = async (emails: string[]) => {
337337
This is a friendly reminder to complete your onboarding process with Codebility.
338338
339339
Steps to complete your onboarding:
340-
1. Visit and Sign in to our and website: https://www.codebility.tech/careers
340+
1. Visit and Sign in to our website: https://www.codebility.tech/auth/sign-in
341341
2. Once Sign in, Click on "Status" in the navigation menu
342342
3. Click the Join Waiting List and Join Discord Server
343343
@@ -433,7 +433,7 @@ export const sendMultipleOnboardingReminder = async (emails: string[]) => {
433433
<div class="steps">
434434
<h3>Steps to complete your onboarding:</h3>
435435
<ol>
436-
<li>Visit and Sign in to our website: <a href="https://www.codebility.tech/careers">https://www.codebility.tech/careers</a></li>
436+
<li>Visit and Sign in to our website: <a href="https://www.codebility.tech/auth/sign-in">https://www.codebility.tech/auth/sign-in</a></li>
437437
<li>Once Signed In, Click on "Status" in the navigation menu</li>
438438
<li>Click the Join Waiting List and Join Discord Server</li>
439439
</ol>
@@ -501,7 +501,7 @@ export const sendOnboardingReminder = async ({ email, name }: { email: string, n
501501
This is a friendly reminder to complete your onboarding process with Codebility.
502502
503503
Steps to complete your onboarding:
504-
1. Visit and Sign in to our and website: https://www.codebility.tech/careers
504+
1. Visit and Sign in to our website: https://www.codebility.tech/auth/sign-in
505505
2. Once Sign in, Click on "Status" in the navigation menu
506506
3. Click the Join Waiting List and Join Discord Server
507507
@@ -597,7 +597,7 @@ export const sendOnboardingReminder = async ({ email, name }: { email: string, n
597597
<div class="steps">
598598
<h3>Steps to complete your onboarding:</h3>
599599
<ol>
600-
<li>Visit and Sign in to our website: <a href="https://www.codebility.tech/careers">https://www.codebility.tech/careers</a></li>
600+
<li>Visit and Sign in to our website: <a href="https://www.codebility.tech/auth/sign-in">https://www.codebility.tech/auth/sign-in</a></li>
601601
<li>Once Signed In, Click on "Status" in the navigation menu</li>
602602
<li>Click the Join Waiting List and Join Discord Server</li>
603603
</ol>
@@ -671,7 +671,7 @@ export const sendMultiplePassedTestEmail = async (Applicant: { email: string, na
671671
For now, please visit our website and check the status of your application.
672672
673673
Steps to check your application status:
674-
1. Visit and Sign in to our website: https://www.codebility.tech/careers
674+
1. Visit and Sign in to our website: https://www.codebility.tech/auth/sign-in
675675
2. Once Signed In, Click on "Status" in the navigation menu
676676
3. Click the Join Waiting List and Join Discord Server
677677
@@ -767,7 +767,7 @@ export const sendMultiplePassedTestEmail = async (Applicant: { email: string, na
767767
<div class="steps">
768768
<h3>Steps to check your application status:</h3>
769769
<ol>
770-
<li>Visit and Sign In our website: <a href="https://www.codebility.tech/careers">https://www.codebility.tech/careers</a></li>
770+
<li>Visit and Sign In our website: <a href="https://www.codebility.tech/auth/sign-in">https://www.codebility.tech/auth/sign-in</a></li>
771771
<li>Once Signed In, Click on "Status" in the navigation menu</li>
772772
<li>Click the Join Waiting List and Join Discord Server</li>
773773
</ol>
@@ -839,7 +839,7 @@ export const sendPassedTestEmail = async ({ email, name }: { email: string, name
839839
For now, please visit our website and check the status of your application.
840840
841841
Steps to check your application status:
842-
1. Visit and Sign In our website: https://www.codebility.tech/careers
842+
1. Visit and Sign In our website: https://www.codebility.tech/auth/sign-in
843843
2. Once Signed In, Click on "Status" in the navigation menu
844844
3. Click the Join Waiting List and Join Discord Server
845845
@@ -935,7 +935,7 @@ export const sendPassedTestEmail = async ({ email, name }: { email: string, name
935935
<div class="steps">
936936
<h3>Steps to check your application status:</h3>
937937
<ol>
938-
<li>Visit and Sign In our website: <a href="https://www.codebility.tech/careers">https://www.codebility.tech/careers</a></li>
938+
<li>Visit and Sign In our website: <a href="https://www.codebility.tech/auth/sign-in">https://www.codebility.tech/auth/sign-in</a></li>
939939
<li>Once Signed In, Click on "Status" in the navigation menu</li>
940940
<li>Click the Join Waiting List and Join Discord Server</li>
941941
</ol>

‎apps/codebility/app/home/feeds/_components/Feed.tsx‎

Lines changed: 79 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,37 @@
11
"use client";
22

3-
import { useEffect, useMemo, useState } from "react";
4-
import DefaultPagination from "@/components/ui/pagination";
3+
import { useEffect, useMemo, useRef, useState } from "react";
54
import { useFeedsStore } from "@/store/feeds-store";
65

76
import Post from "./PostCard";
87

98
interface FeedProp {
109
isAdmin: boolean;
1110
searchQuery?: string;
11+
sortField: "title" | "date" | "upvotes";
12+
sortOrder: "asc" | "desc";
1213
}
1314

14-
export default function Feed({ isAdmin, searchQuery }: FeedProp) {
15+
export default function Feed({
16+
isAdmin,
17+
searchQuery,
18+
sortField,
19+
sortOrder,
20+
}: FeedProp) {
1521
const posts = useFeedsStore((state) => state.posts);
1622
const fetchPosts = useFeedsStore((state) => state.fetchPosts);
1723

18-
const [currentPage, setCurrentPage] = useState(1);
24+
const [visibleCount, setVisibleCount] = useState(6);
25+
const loaderRef = useRef<HTMLDivElement | null>(null);
1926

2027
useEffect(() => {
2128
fetchPosts();
2229
}, [fetchPosts]);
2330

24-
// Filter posts by title or author's first/last name (case-insensitive)
31+
// Filter posts
2532
const filteredPosts = useMemo(() => {
2633
if (!searchQuery || searchQuery.trim() === "") return posts;
27-
2834
const query = searchQuery.toLowerCase();
29-
3035
return posts.filter((post) => {
3136
const titleMatch = post.title?.toLowerCase().includes(query);
3237
const firstNameMatch = post.author_id?.first_name
@@ -35,42 +40,74 @@ export default function Feed({ isAdmin, searchQuery }: FeedProp) {
3540
const lastNameMatch = post.author_id?.last_name
3641
?.toLowerCase()
3742
.includes(query);
38-
3943
return titleMatch || firstNameMatch || lastNameMatch;
4044
});
4145
}, [posts, searchQuery]);
4246

43-
const postsPerPage = 6;
44-
const totalPages = Math.ceil(filteredPosts.length / postsPerPage);
45-
const startIndex = (currentPage - 1) * postsPerPage;
46-
const currentPosts = filteredPosts.slice(
47-
startIndex,
48-
startIndex + postsPerPage,
49-
);
47+
// Sort posts
48+
const sortedPosts = useMemo(() => {
49+
const sorted = [...filteredPosts];
50+
sorted.sort((a, b) => {
51+
let aVal: string | number = "";
52+
let bVal: string | number = "";
53+
54+
switch (sortField) {
55+
case "title":
56+
aVal = a.title?.toLowerCase() || "";
57+
bVal = b.title?.toLowerCase() || "";
58+
break;
59+
case "date":
60+
aVal = new Date(a.created_at).getTime();
61+
bVal = new Date(b.created_at).getTime();
62+
break;
63+
case "upvotes":
64+
aVal = a.upvote_count ?? 0;
65+
bVal = b.upvote_count ?? 0;
66+
break;
67+
}
68+
69+
if (aVal < bVal) return sortOrder === "asc" ? -1 : 1;
70+
if (aVal > bVal) return sortOrder === "asc" ? 1 : -1;
71+
return 0;
72+
});
73+
return sorted;
74+
}, [filteredPosts, sortField, sortOrder]);
75+
76+
const visiblePosts = sortedPosts.slice(0, visibleCount);
5077

51-
// Reset to first page when search changes
5278
useEffect(() => {
53-
setCurrentPage(1);
79+
setVisibleCount(6);
5480
}, [searchQuery]);
5581

82+
// Infinite scroll
83+
useEffect(() => {
84+
const observer = new IntersectionObserver(
85+
(entries) => {
86+
const entry = entries[0];
87+
if (entry?.isIntersecting) {
88+
setVisibleCount((prev) => Math.min(prev + 6, sortedPosts.length));
89+
}
90+
},
91+
{ threshold: 1 },
92+
);
93+
94+
const currentLoader = loaderRef.current;
95+
if (currentLoader) observer.observe(currentLoader);
96+
return () => {
97+
if (currentLoader) observer.unobserve(currentLoader);
98+
};
99+
}, [sortedPosts.length]);
56100
const handleDeletePost = (deletedPostId: string | number) => {
57101
useFeedsStore.setState((state) => ({
58102
posts: state.posts.filter((post) => post.id !== deletedPostId),
59103
}));
60104
};
61105

62-
const handleNextPage = () => {
63-
if (currentPage < totalPages) setCurrentPage((p) => p + 1);
64-
};
65-
66-
const handlePreviousPage = () => {
67-
if (currentPage > 1) setCurrentPage((p) => p - 1);
68-
};
69-
70106
return (
71107
<>
108+
{/* Posts grid */}
72109
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
73-
{currentPosts.map((post) => (
110+
{visiblePosts.map((post) => (
74111
<Post
75112
key={post.id}
76113
post={post}
@@ -80,14 +117,22 @@ export default function Feed({ isAdmin, searchQuery }: FeedProp) {
80117
))}
81118
</div>
82119

83-
{filteredPosts.length > 0 && (
84-
<DefaultPagination
85-
totalPages={totalPages}
86-
currentPage={currentPage}
87-
handleNextPage={handleNextPage}
88-
handlePreviousPage={handlePreviousPage}
89-
setCurrentPage={setCurrentPage}
90-
/>
120+
{/* Loader */}
121+
{visibleCount < sortedPosts.length && (
122+
<div
123+
ref={loaderRef}
124+
className="mt-6 flex justify-center text-sm text-gray-500"
125+
>
126+
<div className="flex items-center gap-2">
127+
<div className="h-4 w-4 animate-spin rounded-full border-2 border-gray-400 border-t-transparent" />
128+
Loading more posts...
129+
</div>
130+
</div>
131+
)}
132+
133+
{/* Empty state */}
134+
{sortedPosts.length === 0 && (
135+
<div className="mt-8 text-center text-gray-500">No posts found.</div>
91136
)}
92137
</>
93138
);

0 commit comments

Comments
 (0)