Skip to content

Commit 2bcc275

Browse files
Zeff01claude
andcommitted
feat: add developer profiles to /codevs page and improve UX
- Add CodevsProfiles component to show current developers for inspiration - Create dedicated ProfilesContainer with intern-focused messaging - Separate /hire-a-codev from /codevs for different user journeys - Update navigation to point to correct routes - Improve Work with Us section with proper CTAs and updated copy - Fix carousel image loading with error handling and fallbacks - Update hero messaging for intern recruitment focus 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 0b72b1c commit 2bcc275

30 files changed

Lines changed: 1571 additions & 40 deletions

‎apps/codebility/app/(marketing)/_components/MarketingNavigation.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const NAV_ITEMS = [
3939
{ id: "1", title: "Our Services", path: "/services" },
4040
{ id: "2", title: "About Us", path: "/#whychooseus" },
4141
{ id: "3", title: "Book a Call", path: "/bookacall" },
42-
{ id: "4", title: "Hire a CoDevs", path: "/codevs" },
42+
{ id: "4", title: "Hire a CoDevs", path: "/hire-a-codev" },
4343
] as const;
4444

4545
const getMenuItems = (

‎apps/codebility/app/(marketing)/_components/MarketingSidenavMenu.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const links = [
88
{ href: "/services", label: "Our Services" },
99
{ href: "/#whychooseus", label: "About Us" },
1010
{ href: "/bookacall", label: "Book a Call" },
11-
{ href: "/codevs", label: "Hire Codevs" },
11+
{ href: "/hire-a-codev", label: "Hire Codevs" },
1212
{ href: "/careers", label: "Careers" },
1313
];
1414

‎apps/codebility/app/(marketing)/_components/landing/LandingWorkWithUs.tsx‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,21 @@ const WORK_WITH_US_CARDS = [
2929
description:
3030
"Accelerate delivery with vetted engineers, designers, and strategists who integrate seamlessly with your roadmap.",
3131
link: {
32-
href: "/codevs",
33-
text: "Meet CoDevs",
32+
href: "/hire-a-codev",
33+
text: "Hire CoDevs",
3434
},
3535
},
3636
{
3737
id: "developer-journey",
38-
title: "Developer Journey",
38+
title: "Be a Codev",
3939
image: {
4040
src: "/assets/images/index/image.png",
41-
alt: "developer learning journey phases",
41+
alt: "become a codev developer",
4242
},
4343
description:
4444
"Join our community, sharpen your craft through real projects, and unlock career opportunities across the globe.",
4545
link: {
46-
href: "/auth/onboarding",
46+
href: "/codevs",
4747
text: "Join the Program",
4848
},
4949
},
@@ -58,7 +58,7 @@ const WORK_WITH_US_CARDS = [
5858
"Explore exciting career opportunities with our team. Join a dynamic environment where innovation meets collaboration.",
5959
link: {
6060
href: "/careers",
61-
text: "Apply as a Dev",
61+
text: "View Careers",
6262
},
6363
},
6464
];

‎apps/codebility/app/(marketing)/_shared/CodevsCarousel.tsx‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,17 @@ const EmblaCarousel: React.FC<PropType> = (props) => {
7272
alt="Project Image"
7373
width={1000}
7474
height={500}
75-
75+
onError={(e) => {
76+
console.error('Failed to load image:', src);
77+
// Replace with fallback image
78+
const target = e.target as HTMLImageElement;
79+
target.src = '/assets/images/index/projects-large.jpg';
80+
}}
7681
className={`${
7782
index === activeIndex
78-
? "lg:h-[35rem]"
79-
: "lg:mt-14 lg:h-[25rem] "
80-
} border-black-800 bg-black-600 h-[20rem] w-full rounded-lg border object-cover p-4`}
83+
? "lg:h-[30rem]"
84+
: "lg:mt-10 lg:h-[22rem] "
85+
} border-black-800 bg-black-600 h-[18rem] w-full rounded-lg border object-cover p-4`}
8186
/>
8287
</div>
8388
</div>

‎apps/codebility/app/(marketing)/codevs/_components/CodevsHero.tsx‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,25 +29,25 @@ export default function Hero() {
2929
></div>
3030
<OrbitingCirclesBackground />
3131
<div className="z-10 flex w-full flex-1 flex-col items-center justify-center gap-3 p-4 text-center text-white">
32-
<p className="text-sm lg:text-xl">Find Your Next Developer</p>
32+
<p className="text-sm lg:text-xl">Join Our Community</p>
3333
<div className="relative">
3434
<h1
3535
className={`absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-5xl uppercase tracking-widest opacity-5 md:text-6xl lg:text-9xl ${rowdies.className}`}
3636
>
3737
Codebility
3838
</h1>
3939
<h2 className="text-xl font-semibold lg:text-5xl">
40-
Hire Talented Codevs
40+
Become a CoDevs
4141
</h2>
4242
</div>
4343
<p className="text-xs md:text-sm lg:text-2xl">
44-
Connect with skilled developers ready to bring your projects to life
44+
Start your journey as a developer and grow with real-world projects
4545
</p>
4646
<div className="mx-auto mt-6 flex w-full justify-center">
47-
<Link href="#codevs">
47+
<Link href={pathsConfig.auth.onboarding}>
4848
<Button className="from-customTeal to-customViolet-100 h-12 rounded-full bg-gradient-to-r via-customBlue-100 p-0.5 hover:bg-gradient-to-br md:w-40">
4949
<span className="bg-black-500 flex h-full w-full items-center justify-center rounded-full text-sm text-white lg:text-lg">
50-
Hire Codevs
50+
Join Now
5151
</span>
5252
</Button>
5353
</Link>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Suspense } from "react";
2+
import { UsersSkeleton } from "@/components/ui/skeleton/UsersSkeleton";
3+
import { getCodevs } from "@/lib/server/codev.service";
4+
import { Codev } from "@/types/home/codev";
5+
import {
6+
getPrioritizedAndFilteredCodevs,
7+
} from "@/utils/codev-priority";
8+
9+
import Section from "../../_shared/CodevsSection";
10+
import CodevList from "../../profiles/_components/CodevList";
11+
import CodevsProfilesContainer from "./CodevsProfilesContainer";
12+
13+
export const dynamic = "force-dynamic";
14+
export const revalidate = 0;
15+
16+
export default async function CodevsProfiles() {
17+
const [{ data: allCodevs, error }] = await Promise.all([
18+
getCodevs({ filters: { role_id: 10 } }), // show only Codevs with role_id 10 which is Codev
19+
]);
20+
21+
if (error) {
22+
throw new Error("Failed to fetch profiles data");
23+
}
24+
25+
const codevsArray: Codev[] = Array.isArray(allCodevs) ? allCodevs : [];
26+
27+
// Use the utility function with filterAdminAndFailed set to true
28+
const sortedCodevs = getPrioritizedAndFilteredCodevs(
29+
codevsArray,
30+
{ activeStatus: ["active"] },
31+
true,
32+
);
33+
34+
return (
35+
<Section
36+
id="codevs-profiles"
37+
className="from-black-500 relative w-full bg-gradient-to-b"
38+
>
39+
<div className="bg-code-pattern absolute inset-0 bg-repeat opacity-5"></div>
40+
<div className="relative flex flex-col gap-8">
41+
<CodevsProfilesContainer />
42+
<Suspense fallback={<UsersSkeleton />}>
43+
<CodevList codevs={sortedCodevs} />
44+
</Suspense>
45+
</div>
46+
</Section>
47+
);
48+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"use client";
2+
3+
import { fadeInOutDownToUp } from "@/components/FramerAnimation/Framer";
4+
import H2 from "@/components/shared/home/H2";
5+
import Heading3 from "@/components/shared/home/Heading3";
6+
import IntroText from "@/components/shared/home/IntroText";
7+
import { motion } from "framer-motion";
8+
9+
export default function CodevsProfilesContainer() {
10+
return (
11+
<div className="flex flex-col gap-4">
12+
<motion.div
13+
variants={fadeInOutDownToUp}
14+
initial="hidden"
15+
whileInView="visible"
16+
className="mx-auto"
17+
>
18+
<H2 className="text-white">Meet Our CoDevs</H2>
19+
</motion.div>
20+
<motion.div
21+
variants={fadeInOutDownToUp}
22+
initial="hidden"
23+
whileInView="visible"
24+
className="text-gray mx-auto max-w-[650px] text-center"
25+
>
26+
<Heading3>See Who You'll Be Working With</Heading3>
27+
<IntroText>
28+
Get inspired by our current CoDevs who started their journey just like you.
29+
These talented developers have grown their skills through real-world projects
30+
and are now contributing to amazing products worldwide.
31+
</IntroText>
32+
</motion.div>
33+
</div>
34+
);
35+
}

‎apps/codebility/app/(marketing)/codevs/_components/CodevsProject.tsx‎

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -64,23 +64,46 @@ export default async function ProjectSection() {
6464

6565
if (activeProjects && activeProjects.length > 0) {
6666
for (const project of activeProjects) {
67-
if (project.main_image) {
68-
let imageUrl = project.main_image;
67+
let imageUrl = '';
68+
69+
if (project.main_image && project.main_image.trim()) {
70+
imageUrl = project.main_image.trim();
6971

70-
// Transform URL if needed
71-
if (imageUrl.startsWith("public")) {
72+
// Transform URL if needed - handle various URL formats
73+
if (imageUrl.startsWith("public/")) {
74+
// Handle public/ prefix
75+
imageUrl = `${process.env.NEXT_PUBLIC_SUPABASE_URL}/storage/v1/object/public/services-image/${imageUrl}`;
76+
} else if (imageUrl.startsWith("public")) {
77+
// Handle public prefix without slash
78+
imageUrl = `${process.env.NEXT_PUBLIC_SUPABASE_URL}/storage/v1/object/public/services-image/${imageUrl}`;
79+
} else if (!imageUrl.startsWith("http")) {
80+
// If it's a relative path, construct the full URL
7281
imageUrl = `${process.env.NEXT_PUBLIC_SUPABASE_URL}/storage/v1/object/public/services-image/${imageUrl}`;
7382
}
7483

75-
// Store both the image URL and project data
76-
projectsWithImages.push({
77-
...project,
78-
imageUrl,
79-
});
80-
81-
// Add to slides for the carousel
82-
slides.push(imageUrl);
84+
// Validate the URL before adding to carousel
85+
try {
86+
new URL(imageUrl);
87+
console.log(`✅ Valid image URL for project ${project.name}:`, imageUrl);
88+
} catch (error) {
89+
console.warn(`❌ Invalid image URL for project ${project.name}:`, imageUrl);
90+
// Use fallback image for invalid URLs
91+
imageUrl = '/assets/images/index/projects-large.jpg';
92+
}
93+
} else {
94+
console.warn(`⚠️ No image found for project ${project.name}, using fallback`);
95+
// Use fallback image for projects without images
96+
imageUrl = '/assets/images/index/projects-large.jpg';
8397
}
98+
99+
// Store both the image URL and project data
100+
projectsWithImages.push({
101+
...project,
102+
imageUrl,
103+
});
104+
105+
// Add to slides for the carousel
106+
slides.push(imageUrl);
84107
}
85108
}
86109

0 commit comments

Comments
 (0)