add people

This commit is contained in:
sasha-astiadi 2025-07-22 14:34:15 +02:00
parent f42f90782f
commit 5437cae055
7 changed files with 401 additions and 0 deletions

143
docs/adding-people.md Normal file
View File

@ -0,0 +1,143 @@
# Adding New People to the Team Page
This guide explains how to add new team members to the website. The system is designed to automatically display people based on existing component files.
## Current System
Currently, only **Adnan Fatayerji** appears on the people page because only his component exists in `src/components/people/People_Adnan.tsx`.
## How to Add a New Person
To add a new team member, follow these steps:
### 1. Create the Person's Component
Create a new file: `src/components/people/People_[Name].tsx`
Example for John Doe:
```typescript
import { PersonTemplate } from '@/components/PersonTemplate'
export const data = [
{
name: 'John Doe',
role: 'Software Engineer',
imageUrl: '/images/people/john_doe/john_doe.jpg',
xUrl: '#',
linkedinUrl: 'https://www.linkedin.com/in/johndoe/',
},
]
const biography = `
<p class="text-lg/7">
John is a passionate software engineer with expertise in modern web technologies.
He specializes in building scalable applications and has a keen interest in user experience design.
</p>
<p class="mt-5">
With over 5 years of experience in the tech industry, John has worked on various projects
ranging from startups to enterprise applications. He believes in writing clean, maintainable code
and is always eager to learn new technologies.
</p>
`
export function People_John() {
return <PersonTemplate personData={data[0]} biography={biography} />
}
```
### 2. Create the Person's Page Route
Create a directory and page: `src/app/people/John_Doe/page.tsx`
```typescript
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_John } from '@/components/people/People_John'
export default function JohnDoePage() {
return (
<>
<Header_darkbg />
<main>
<People_John />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}
```
### 3. Update the People Data Registry
Add the new person's data import to `src/lib/peopleData.ts`:
```typescript
// Add import
import { data as johnData } from '@/components/people/People_John'
// Add to getAllPeopleData function
export function getAllPeopleData(): PersonData[] {
const allPeopleData: PersonData[] = []
try {
allPeopleData.push(...adnanData)
allPeopleData.push(...johnData) // Add this line
} catch (error) {
console.error('Error loading people data:', error)
}
return allPeopleData
}
```
### 4. Add Person's Images
Add the person's images to: `public/images/people/john_doe/`
- `john_doe.jpg` (or .png, .jpeg)
## Template System Benefits
The system now uses a **centralized template** (`PersonTemplate.tsx`) for all individual people pages. This means:
- **Global Styling**: Change image size, layout, or styling in `PersonTemplate.tsx` and it applies to ALL people's profiles
- **Consistency**: All people pages have the same structure and design
- **Easy Maintenance**: Update the template once instead of editing each person's component individually
- **Responsive Design**: Template handles all responsive breakpoints uniformly
### Customizing the Template
To change styling for all people pages (like image size), edit `src/components/PersonTemplate.tsx`:
```typescript
// Example: To change image aspect ratio for all people
<img
alt={personData.name}
src={personData.imageUrl}
width={1184}
height={1376}
className="aspect-square w-full rounded-lg object-cover shadow-lg lg:aspect-auto" // Changed from aspect-12/7 to aspect-square
/>
```
## Important Notes
- **Data Structure**: Each person component must export a `data` array with the exact structure shown above
- **Biography Format**: Use HTML string for rich text formatting in the biography
- **Template Usage**: All person components should use `PersonTemplate` for consistency
- **Naming Convention**:
- Component files: `People_[FirstName].tsx`
- Page directories: `[First_Last]/page.tsx`
- Image directories: `first_last/`
- **Automatic Display**: Once all steps are completed, the person will automatically appear on the main people page
- **URL Structure**: Individual pages will be accessible at `/people/First_Last`
## Current Status
- ✅ Adnan Fatayerji - Complete (component, page, images)
- ❌ All other team members - Need to be added following the steps above
The system is designed to be scalable - each new person added will automatically appear on the team page without needing to modify the main PeopleHero component.

View File

@ -0,0 +1,19 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { People_Adnan } from '@/components/people/People_Adnan'
export default function AdnanFatayerjiPage() {
return (
<>
<Header_darkbg />
<main>
<People_Adnan />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

32
src/app/people/page.tsx Normal file
View File

@ -0,0 +1,32 @@
import { CallToAction } from '@/components/CallToAction'
import { Faqs } from '@/components/Faqs'
import { Footer } from '@/components/Footer'
import { Header_darkbg } from '@/components/Header_darkbg'
import { HomeAbout } from '@/components/HomeAbout'
import { Hero } from '@/components/Hero'
import { Pricing } from '@/components/Pricing'
import { PrimaryFeatures } from '@/components/PrimaryFeatures'
import { SecondaryFeatures } from '@/components/SecondaryFeatures'
import { Testimonials } from '@/components/Testimonials'
import { HomePrinciples } from '@/components/HomePrinciples'
import { HomeMilestones } from '@/components/HomeMilestones'
import { HomeVentures } from '@/components/HomeVentures'
import { Quote } from '@/components/Quote'
import { AboutHero } from '@/components/AboutHero'
import { AboutMission } from '@/components/AboutMission'
import { AboutExperience } from '@/components/AboutExperience'
import { PeopleHero } from '@/components/PeopleHero'
export default function People() {
return (
<>
<Header_darkbg />
<main>
<PeopleHero />
<CallToAction />
<Faqs />
</main>
<Footer />
</>
)
}

View File

@ -0,0 +1,65 @@
import Link from 'next/link'
import { getAllPeopleData, PersonData } from '@/lib/peopleData'
// Function to convert name to URL slug
function nameToSlug(name: string): string {
return name.replace(/\s+/g, '_')
}
export function PeopleHero() {
const people = getAllPeopleData()
return (
<div className="bg-white py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mx-auto max-w-2xl lg:mx-0">
<h2 className="text-4xl font-semibold tracking-tight text-pretty text-gray-900 sm:text-5xl">Our team</h2>
<p className="mt-6 text-lg/8 text-gray-600">
We're a dynamic group of individuals who are passionate about what we do and dedicated to delivering the
best results for our clients.
</p>
</div>
<ul
role="list"
className="mx-auto mt-20 grid max-w-2xl grid-cols-1 gap-x-8 gap-y-16 sm:grid-cols-2 lg:mx-0 lg:max-w-none lg:grid-cols-4"
>
{people.map((person) => (
<li key={person.name}>
<img
alt=""
src={person.imageUrl}
className="aspect-1/1 w-full rounded-2xl object-cover outline-1 -outline-offset-1 outline-black/5"
/>
<Link href={`/people/${nameToSlug(person.name)}`}>
<h3 className="mt-6 text-lg/8 font-semibold tracking-tight text-gray-900 hover:text-indigo-600 cursor-pointer">{person.name}</h3>
</Link>
<p className="text-base/7 text-gray-600">{person.role}</p>
<ul role="list" className="mt-6 flex gap-x-6">
<li>
<a href={person.xUrl} className="text-gray-400 hover:text-gray-500">
<span className="sr-only">X</span>
<svg fill="currentColor" viewBox="0 0 20 20" aria-hidden="true" className="size-5">
<path d="M11.4678 8.77491L17.2961 2H15.915L10.8543 7.88256L6.81232 2H2.15039L8.26263 10.8955L2.15039 18H3.53159L8.87581 11.7878L13.1444 18H17.8063L11.4675 8.77491H11.4678ZM9.57608 10.9738L8.95678 10.0881L4.02925 3.03974H6.15068L10.1273 8.72795L10.7466 9.61374L15.9156 17.0075H13.7942L9.57608 10.9742V10.9738Z" />
</svg>
</a>
</li>
<li>
<a href={person.linkedinUrl} className="text-gray-400 hover:text-gray-500">
<span className="sr-only">LinkedIn</span>
<svg fill="currentColor" viewBox="0 0 20 20" aria-hidden="true" className="size-5">
<path
d="M16.338 16.338H13.67V12.16c0-.995-.017-2.277-1.387-2.277-1.39 0-1.601 1.086-1.601 2.207v4.248H8.014v-8.59h2.559v1.174h.037c.356-.675 1.227-1.387 2.526-1.387 2.703 0 3.203 1.778 3.203 4.092v4.711zM5.005 6.575a1.548 1.548 0 11-.003-3.096 1.548 1.548 0 01.003 3.096zm-1.337 9.763H6.34v-8.59H3.667v8.59zM17.668 1H2.328C1.595 1 1 1.581 1 2.298v15.403C1 18.418 1.595 19 2.328 19h15.34c.734 0 1.332-.582 1.332-1.299V2.298C19 1.581 18.402 1 17.668 1z"
clipRule="evenodd"
fillRule="evenodd"
/>
</svg>
</a>
</li>
</ul>
</li>
))}
</ul>
</div>
</div>
)
}

View File

@ -0,0 +1,87 @@
import { CameraIcon } from '@heroicons/react/20/solid'
import { PersonData } from '@/lib/peopleData'
interface PersonTemplateProps {
personData: PersonData
biography: string
}
export function PersonTemplate({ personData, biography }: PersonTemplateProps) {
return (
<div className="overflow-hidden bg-white">
<div className="relative mx-auto max-w-7xl px-6 py-16 lg:px-8">
<div className="absolute top-0 bottom-0 left-3/4 hidden w-screen bg-gray-50 lg:block" />
<div className="mx-auto max-w-prose text-base lg:grid lg:max-w-none lg:grid-cols-2 lg:gap-8">
<div>
<h2 className="text-lg font-semibold text-indigo-600">Team Member</h2>
<h3 className="mt-2 text-3xl/8 font-bold tracking-tight text-gray-900 sm:text-4xl">Meet {personData.name}</h3>
</div>
</div>
<div className="mt-8 lg:grid lg:grid-cols-2 lg:gap-8">
<div className="relative lg:col-start-2 lg:row-start-1">
<svg
fill="none"
width={404}
height={384}
viewBox="0 0 404 384"
aria-hidden="true"
className="absolute top-0 right-0 -mt-20 -mr-20 hidden lg:block"
>
<defs>
<pattern
x={0}
y={0}
id="de316486-4a29-4312-bdfc-fbce2132a2c1"
width={20}
height={20}
patternUnits="userSpaceOnUse"
>
<rect x={0} y={0} fill="currentColor" width={4} height={4} className="text-gray-200" />
</pattern>
</defs>
<rect fill="url(#de316486-4a29-4312-bdfc-fbce2132a2c1)" width={404} height={384} />
</svg>
<div className="relative mx-auto max-w-prose text-base lg:max-w-none">
<figure>
<img
alt={personData.name}
src={personData.imageUrl}
width={1184}
height={1376}
className="aspect-12/7 w-full rounded-lg object-cover shadow-lg lg:aspect-auto"
/>
<figcaption className="mt-3 flex text-sm text-gray-500">
<CameraIcon aria-hidden="true" className="size-5 flex-none text-gray-400" />
<span className="ml-2">Professional Photo</span>
</figcaption>
</figure>
</div>
</div>
<div className="mt-8 lg:mt-0">
<div className="mx-auto text-base/7 text-gray-500">
<p className="text-lg/7 font-semibold text-gray-900 mb-4">{personData.role}</p>
<div
className="prose prose-gray max-w-none"
dangerouslySetInnerHTML={{ __html: biography }}
/>
{/* Social Links */}
{personData.linkedinUrl && personData.linkedinUrl !== '#' && (
<div className="mt-8">
<a
href={personData.linkedinUrl}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Connect on LinkedIn
</a>
</div>
)}
</div>
</div>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,24 @@
import { PersonTemplate } from '@/components/PersonTemplate'
export const data = [
{
name: 'Adnan Fatayerji',
role: 'Co-Founder, CEO',
imageUrl: '/images/people/adnan_fatayerji/adnan_fatayerji.jpg',
xUrl: '#',
linkedinUrl: 'https://www.linkedin.com/in/adnansf/',
},
]
const biography = `
<p class="text-lg/7">
Adnan is a systems thinker and venture architect with a lifelong focus on reimagining how digital systems serve people. With a background spanning economics, strategy, and frontier technologies, his work bridges innovation and impactbringing visionary ideas into scalable, grounded execution.
</p>
<p class="mt-5">
He has spent over a decade exploring how infrastructure, governance, and technology can evolve to empower communities, not just corporations. At OurWorld, Adnan leads strategy, growth, and ecosystem development, guiding ventures that are rooted in sovereignty, collective intelligence, and long-term resilience.
</p>
`
export function People_Adnan() {
return <PersonTemplate personData={data[0]} biography={biography} />
}

31
src/lib/peopleData.ts Normal file
View File

@ -0,0 +1,31 @@
// This file dynamically imports all people data from components
// When new people components are added, they will automatically appear
export interface PersonData {
name: string
role: string
imageUrl: string
xUrl: string
linkedinUrl: string
}
// Import all existing people data
import { data as adnanData } from '@/components/people/People_Adnan'
// Function to get all people data
export function getAllPeopleData(): PersonData[] {
const allPeopleData: PersonData[] = []
// Add data from all existing people components
try {
allPeopleData.push(...adnanData)
} catch (error) {
console.error('Error loading Adnan data:', error)
}
// When new people components are created, add their imports here:
// import { data as newPersonData } from '@/components/people/People_NewPerson'
// allPeopleData.push(...newPersonData)
return allPeopleData
}