Skip to content

Commit dbb38e6

Browse files
committed
opportunities
1 parent 86e5652 commit dbb38e6

10 files changed

Lines changed: 368 additions & 202 deletions

src/app/join-us/[id]/not-found.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import Link from "next/link";
2+
import Navbar from "@/components/Navbar";
3+
import Footer from "@/components/Footer";
4+
import { navigationItems } from "@/constants/navigation";
5+
6+
export default function NotFound() {
7+
return (
8+
<div className="relative min-h-screen bg-white">
9+
<Navbar items={navigationItems} activeItem="/join-us" />
10+
<div className="pt-20 flex flex-col items-center justify-center min-h-[60vh] gap-8 px-4">
11+
<h1 className="font-sans text-4xl md:text-6xl font-normal leading-[1.1] tracking-[-0.96px] text-[#001f33]">
12+
Position Not Found
13+
</h1>
14+
<p className="font-sans text-lg md:text-xl font-normal leading-[1.4] text-[#001f33] text-center max-w-[600px]">
15+
The position you are looking for does not exist or is no longer
16+
available.
17+
</p>
18+
<Link
19+
href="/join-us"
20+
className="bg-[#001f33] text-white px-8 py-4 font-mono text-sm font-normal leading-[1.1] uppercase hover:bg-opacity-90 transition-colors"
21+
>
22+
Back to Positions
23+
</Link>
24+
</div>
25+
<Footer />
26+
</div>
27+
);
28+
}
29+

src/app/join-us/[id]/page.tsx

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import Navbar from "@/components/Navbar";
2+
import Section from "@/components/Section";
3+
import Footer from "@/components/Footer";
4+
import Image from "next/image";
5+
import Link from "next/link";
6+
import { notFound } from "next/navigation";
7+
import { opportunities } from "@/data/opportunities";
8+
import { navigationItems } from "@/constants/navigation";
9+
10+
export async function generateStaticParams() {
11+
return opportunities.map((opportunity) => ({
12+
id: opportunity.id,
13+
}));
14+
}
15+
16+
interface OpportunityPageProps {
17+
params: Promise<{ id: string }>;
18+
}
19+
20+
export default async function OpportunityPage({ params }: OpportunityPageProps) {
21+
const { id } = await params;
22+
const opportunity = opportunities.find((o) => o.id === id);
23+
24+
if (!opportunity) {
25+
notFound();
26+
}
27+
28+
return (
29+
<div className="relative min-h-screen bg-white">
30+
<Navbar items={navigationItems} activeItem="/join-us" />
31+
32+
<div className="pt-20">
33+
<Section className="py-12 md:py-20">
34+
<div className="flex flex-col gap-8 md:gap-12 items-start max-w-[1200px] mx-auto">
35+
<div className="flex flex-col gap-4 md:gap-[19px] items-start leading-[1.1] text-[#001f33] w-full">
36+
<p className="font-mono text-base md:text-lg font-normal uppercase">
37+
{opportunity.subtitle}
38+
</p>
39+
<h1 className="font-sans text-3xl md:text-[48px] font-normal leading-[1.1] tracking-[-0.96px]">
40+
{opportunity.title}
41+
</h1>
42+
<p className="font-sans text-base md:text-xl font-normal leading-[1.4] tracking-[-0.2px] max-w-[800px]">
43+
{opportunity.description}
44+
</p>
45+
</div>
46+
47+
<div className="flex flex-col md:flex-row gap-10 w-full">
48+
<div className="flex flex-col gap-6 w-full md:w-2/3">
49+
{opportunity.detailedDescription && (
50+
<p className="font-sans text-base md:text-lg font-normal leading-[1.6] tracking-[-0.16px] text-[#001f33]">
51+
{opportunity.detailedDescription}
52+
</p>
53+
)}
54+
55+
{opportunity.responsibilities && opportunity.responsibilities.length > 0 && (
56+
<div className="flex flex-col gap-3">
57+
<h2 className="font-sans text-xl md:text-2xl font-normal leading-[1.2] tracking-[-0.4px] text-[#001f33]">
58+
Responsibilities
59+
</h2>
60+
<ul className="list-disc pl-5 space-y-2">
61+
{opportunity.responsibilities.map((item, index) => (
62+
<li
63+
key={index}
64+
className="font-sans text-base md:text-lg font-normal leading-[1.6] text-[#001f33]"
65+
>
66+
{item}
67+
</li>
68+
))}
69+
</ul>
70+
</div>
71+
)}
72+
73+
{opportunity.requirements && opportunity.requirements.length > 0 && (
74+
<div className="flex flex-col gap-3">
75+
<h2 className="font-sans text-xl md:text-2xl font-normal leading-[1.2] tracking-[-0.4px] text-[#001f33]">
76+
Requirements
77+
</h2>
78+
<ul className="list-disc pl-5 space-y-2">
79+
{opportunity.requirements.map((item, index) => (
80+
<li
81+
key={index}
82+
className="font-sans text-base md:text-lg font-normal leading-[1.6] text-[#001f33]"
83+
>
84+
{item}
85+
</li>
86+
))}
87+
</ul>
88+
</div>
89+
)}
90+
91+
{opportunity.benefits && opportunity.benefits.length > 0 && (
92+
<div className="flex flex-col gap-3">
93+
<h2 className="font-sans text-xl md:text-2xl font-normal leading-[1.2] tracking-[-0.4px] text-[#001f33]">
94+
Benefits
95+
</h2>
96+
<ul className="list-disc pl-5 space-y-2">
97+
{opportunity.benefits.map((item, index) => (
98+
<li
99+
key={index}
100+
className="font-sans text-base md:text-lg font-normal leading-[1.6] text-[#001f33]"
101+
>
102+
{item}
103+
</li>
104+
))}
105+
</ul>
106+
</div>
107+
)}
108+
</div>
109+
110+
<div className="flex flex-col gap-6 w-full md:w-1/3">
111+
<div className="relative w-full h-[260px] md:h-[280px]">
112+
<Image
113+
src={opportunity.imageUrl}
114+
alt={opportunity.imageAlt}
115+
fill
116+
className="object-cover"
117+
/>
118+
</div>
119+
120+
<div className="border border-[#a3a3a3] p-5 flex flex-col gap-3">
121+
<h2 className="font-sans text-lg md:text-xl font-normal leading-[1.2] tracking-[-0.2px] text-[#001f33]">
122+
Position details
123+
</h2>
124+
<div className="flex flex-col gap-2 font-sans text-sm md:text-base text-[#001f33]">
125+
{opportunity.type && <p>Type: {opportunity.type}</p>}
126+
{opportunity.employmentType && (
127+
<p>Employment type: {opportunity.employmentType}</p>
128+
)}
129+
{opportunity.workload && <p>Workload: {opportunity.workload}</p>}
130+
{opportunity.contractType && (
131+
<p>Contract type: {opportunity.contractType}</p>
132+
)}
133+
{opportunity.location && <p>Location: {opportunity.location}</p>}
134+
{opportunity.salaryRange && (
135+
<p>Salary range: {opportunity.salaryRange}</p>
136+
)}
137+
{opportunity.deadline && <p>Deadline: {opportunity.deadline}</p>}
138+
{opportunity.contactEmail && (
139+
<p>Contact: {opportunity.contactEmail}</p>
140+
)}
141+
</div>
142+
143+
<div className="mt-2 flex flex-col gap-2">
144+
<Link
145+
href="/contact"
146+
className="border border-[#001f33] text-[#001f33] px-6 py-3 font-mono text-xs md:text-sm font-normal text-center hover:bg-gray-50 transition-colors inline-block"
147+
>
148+
Contact us about this position
149+
</Link>
150+
</div>
151+
</div>
152+
</div>
153+
</div>
154+
</div>
155+
</Section>
156+
157+
<Section className="py-8 md:py-12 border-t border-[#a3a3a3]">
158+
<div className="max-w-[1200px] mx-auto flex justify-between items-center px-6 md:px-0">
159+
<Link
160+
href="/join-us"
161+
className="inline-flex items-center gap-2 font-mono text-xs md:text-sm font-normal text-[#001f33] uppercase hover:opacity-80 transition-opacity"
162+
>
163+
<svg
164+
width="16"
165+
height="16"
166+
viewBox="0 0 16 16"
167+
fill="none"
168+
className="rotate-180"
169+
>
170+
<path
171+
d="M6 12L10 8L6 4"
172+
stroke="currentColor"
173+
strokeWidth="2"
174+
strokeLinecap="round"
175+
strokeLinejoin="round"
176+
/>
177+
</svg>
178+
Back to Positions
179+
</Link>
180+
</div>
181+
</Section>
182+
</div>
183+
184+
<Footer />
185+
</div>
186+
);
187+
}
188+

0 commit comments

Comments
 (0)