Merge pull request 'development' (#1) from development into main
Reviewed-on: #1
192
README.md
@ -1,35 +1,193 @@
|
|||||||
# Salient
|
# 🌐 OurWorld Web (Salient Template)
|
||||||
|
|
||||||
Salient is a [Tailwind Plus](https://tailwindcss.com/plus) site template built using [Tailwind CSS](https://tailwindcss.com) and [Next.js](https://nextjs.org).
|
**OurWorld Web** is a statically-exported site built using the [Salient](https://tailwindcss.com/plus) template with **Next.js**, **Tailwind CSS v4**, and **TypeScript**. It showcases people and ventures in a modular and scalable frontend.
|
||||||
|
|
||||||
## Getting started
|
---
|
||||||
|
|
||||||
To get started with this template, first install the npm dependencies:
|
## 📁 Project Structure
|
||||||
|
|
||||||
```bash
|
```
|
||||||
npm install
|
www_ourworld_new/
|
||||||
|
├── .env.local # Local environment variables
|
||||||
|
├── public/ # Public static assets (images, videos)
|
||||||
|
├── scripts/ # Utility scripts (generate people)
|
||||||
|
├── src/
|
||||||
|
│ ├── app/ # App routes (people, ventures, pages)
|
||||||
|
│ │ ├── people/ # Page routes per person
|
||||||
|
│ │ └── ventures/ # Venture route pages
|
||||||
|
│ ├── components/
|
||||||
|
│ │ └── people/ # Individual people components
|
||||||
|
│ ├── data/ # Temporary or pre-generated data
|
||||||
|
│ ├── lib/ # People data registry (auto-generated)
|
||||||
|
│ ├── styles/ # Tailwind & global CSS
|
||||||
|
│ └── videos/ # Local videos
|
||||||
|
├── docs/ # Markdown docs for onboarding
|
||||||
|
├── out/ # Static export folder after build
|
||||||
|
├── package.json # Scripts and dependencies
|
||||||
|
└── README.md # You are here.
|
||||||
```
|
```
|
||||||
|
|
||||||
Next, run the development server:
|
---
|
||||||
|
|
||||||
|
## ⚙️ Technologies
|
||||||
|
|
||||||
|
* [Next.js](https://nextjs.org/)
|
||||||
|
* [Tailwind CSS](https://tailwindcss.com/)
|
||||||
|
* [React](https://reactjs.org/)
|
||||||
|
* [TypeScript](https://www.typescriptlang.org/)
|
||||||
|
* [Headless UI](https://headlessui.dev/)
|
||||||
|
* [Heroicons](https://heroicons.com/)
|
||||||
|
* [Nodemailer](https://nodemailer.com/) (optional for contact form)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Getting Started
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
git clone https://github.com/your-org/www_ourworld_new.git
|
||||||
|
cd www_ourworld_new
|
||||||
|
npm install
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, open [http://localhost:3000](http://localhost:3000) in your browser to view the website.
|
Visit `http://localhost:3000` to view the site.
|
||||||
|
|
||||||
## Customizing
|
---
|
||||||
|
|
||||||
You can start editing this template by modifying the files in the `/src` folder. The site will auto-update as you edit these files.
|
## ⚙️ Building the Site
|
||||||
|
|
||||||
## License
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
This site template is a commercial product and is licensed under the [Tailwind Plus license](https://tailwindcss.com/plus/license).
|
This will:
|
||||||
|
|
||||||
## Learn more
|
* Run `scripts/generate-people-data.js` to update the people registry
|
||||||
|
* Build a static export into `/out`
|
||||||
|
|
||||||
To learn more about the technologies used in this site template, see the following resources:
|
To preview production:
|
||||||
|
|
||||||
- [Tailwind CSS](https://tailwindcss.com/docs) - the official Tailwind CSS documentation
|
```bash
|
||||||
- [Next.js](https://nextjs.org/docs) - the official Next.js documentation
|
npm run start
|
||||||
- [Headless UI](https://headlessui.dev) - the official Headless UI documentation
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✨ Adding a Person
|
||||||
|
|
||||||
|
1. Create a folder: `public/images/people/jane_doe/`
|
||||||
|
2. Inside it, place:
|
||||||
|
|
||||||
|
* `index.md` (frontmatter + bio)
|
||||||
|
* `jane_doe.jpg` (image)
|
||||||
|
3. Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node scripts/generate-all-people.js
|
||||||
|
npm run generate-people-data
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
**Frontmatter example:**
|
||||||
|
|
||||||
|
```md
|
||||||
|
---
|
||||||
|
title: Jane Doe
|
||||||
|
description: Community Architect
|
||||||
|
extra:
|
||||||
|
socialLinks:
|
||||||
|
LinkedIn: https://linkedin.com/in/janedoe
|
||||||
|
---
|
||||||
|
|
||||||
|
Jane builds regenerative systems that empower local communities...
|
||||||
|
```
|
||||||
|
|
||||||
|
The script will:
|
||||||
|
|
||||||
|
* Generate a component in `src/components/people/People_Jane_Doe.tsx`
|
||||||
|
* Create or update `src/app/people/jane_doe/page.tsx`
|
||||||
|
* Add them to the dynamic people registry
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧠 Anatomy of a Person
|
||||||
|
|
||||||
|
### Component (`People_Jane_Doe.tsx`)
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
export const data = [{
|
||||||
|
name: 'Jane Doe',
|
||||||
|
role: 'Community Architect',
|
||||||
|
imageUrl: '/images/people/jane_doe/jane_doe.jpg',
|
||||||
|
xUrl: '#',
|
||||||
|
linkedinUrl: 'https://linkedin.com/in/janedoe',
|
||||||
|
}]
|
||||||
|
|
||||||
|
const biography = `<p class="text-lg/7">Jane builds regenerative systems...</p>`
|
||||||
|
|
||||||
|
export function People_Jane_Doe() {
|
||||||
|
return <PersonTemplate personData={data[0]} biography={biography} />
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Page (`/people/jane_doe/page.tsx`)
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
export default function JaneDoePage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Jane_Doe />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Scripts
|
||||||
|
|
||||||
|
| Script | Description |
|
||||||
|
| ------------------------------------- | ------------------------------------------ |
|
||||||
|
| `npm run dev` | Start local dev server |
|
||||||
|
| `npm run build` | Export static HTML & run data generation |
|
||||||
|
| `npm run generate-people-data` | Regenerate peopleData.ts from all profiles |
|
||||||
|
| `node scripts/generate-all-people.js` | Parse markdown, images → full pages |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📄 Docs
|
||||||
|
|
||||||
|
* [`docs/adding-people.md`](docs/adding-people.md) – Step-by-step onboarding
|
||||||
|
* [`docs/gmail-setup-guide.md`](docs/gmail-setup-guide.md) – Email auth
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧹 Code Quality
|
||||||
|
|
||||||
|
* ESLint + Prettier + Tailwind Plugin
|
||||||
|
* TypeScript strict mode
|
||||||
|
* Modular file structure
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 License
|
||||||
|
|
||||||
|
This project is based on a **Tailwind Plus** license. You may modify and build on top of it, but redistribution of the template or components is **not permitted**.
|
||||||
|
|
||||||
|
See [`LICENSE.md`](./LICENSE.md) for details.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧠 Learn More
|
||||||
|
|
||||||
|
* [Next.js Documentation](https://nextjs.org/docs)
|
||||||
|
* [Tailwind CSS Documentation](https://tailwindcss.com/docs)
|
||||||
|
* [TypeScript Docs](https://www.typescriptlang.org/docs/)
|
||||||
|
* [Headless UI](https://headlessui.dev/)
|
||||||
|
* [Heroicons](https://heroicons.com/)
|
||||||
|
186
docs/adding-people.md
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
# Adding New People to the Team Page
|
||||||
|
|
||||||
|
This guide explains how to add new team members to the website. The system now features **automated discovery** of people components, so you no longer need to manually update imports!
|
||||||
|
|
||||||
|
## 🚀 Quick Start (Automated Workflow)
|
||||||
|
|
||||||
|
### 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 text-lg/7">
|
||||||
|
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. Add Person's Images
|
||||||
|
|
||||||
|
Add the person's images to: `public/images/people/john_doe/`
|
||||||
|
- `john_doe.jpg` (or .png, .jpeg)
|
||||||
|
|
||||||
|
### 4. 🎉 Automatic Integration
|
||||||
|
|
||||||
|
**That's it!** The person will automatically appear on the people page when you build the project. The people data registry is automatically updated during the build process.
|
||||||
|
|
||||||
|
If you want to test locally during development, you can manually run:
|
||||||
|
```bash
|
||||||
|
npm run generate-people-data
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 How the Automation Works
|
||||||
|
|
||||||
|
The system now includes a Node.js script (`scripts/generate-people-data.js`) that:
|
||||||
|
|
||||||
|
1. **Scans** the `src/components/people/` directory for all `People_*.tsx` files
|
||||||
|
2. **Generates** the necessary imports and exports in `src/lib/peopleData.ts`
|
||||||
|
3. **Creates** both synchronous and asynchronous data loading functions
|
||||||
|
4. **Eliminates** the need for manual import management
|
||||||
|
|
||||||
|
### Available Commands
|
||||||
|
|
||||||
|
- `npm run generate-people-data` - Manually regenerate the people data registry (for development testing)
|
||||||
|
- `npm run dev` - Start the development server
|
||||||
|
- `npm run build` - Build for production (automatically runs people data generation first)
|
||||||
|
|
||||||
|
## 📁 File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── components/people/
|
||||||
|
│ ├── People_Adnan.tsx ✅ Auto-discovered
|
||||||
|
│ ├── People_Kristof.tsx ✅ Auto-discovered
|
||||||
|
│ └── People_John.tsx ✅ Auto-discovered (after running script)
|
||||||
|
├── app/people/
|
||||||
|
│ ├── Adnan_Fatayerji/page.tsx
|
||||||
|
│ ├── kristof_de_spiegeleer/page.tsx
|
||||||
|
│ └── John_Doe/page.tsx
|
||||||
|
├── lib/
|
||||||
|
│ └── peopleData.ts 🤖 Auto-generated
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 Benefits of the New System
|
||||||
|
|
||||||
|
### ✅ **No Manual Imports**
|
||||||
|
- Add a new `People_*.tsx` file
|
||||||
|
- Run `npm run generate-people-data`
|
||||||
|
- Done! No need to edit `peopleData.ts` manually
|
||||||
|
|
||||||
|
### ✅ **Error Prevention**
|
||||||
|
- No more forgotten imports
|
||||||
|
- No more typos in import statements
|
||||||
|
- Automatic error handling for each component
|
||||||
|
|
||||||
|
### ✅ **Scalable**
|
||||||
|
- Add as many people as needed
|
||||||
|
- System automatically discovers all components
|
||||||
|
- Consistent naming convention enforced
|
||||||
|
|
||||||
|
### ✅ **Developer Friendly**
|
||||||
|
- Clear feedback when running the script
|
||||||
|
- Lists all discovered components
|
||||||
|
- Easy to debug and maintain
|
||||||
|
|
||||||
|
## 🔄 Migration from Manual System
|
||||||
|
|
||||||
|
If you have existing people components that were manually imported:
|
||||||
|
|
||||||
|
1. Ensure all components follow the `People_*.tsx` naming convention
|
||||||
|
2. Run `npm run generate-people-data`
|
||||||
|
3. The script will automatically discover and include all existing components
|
||||||
|
|
||||||
|
## 📋 Naming Conventions
|
||||||
|
|
||||||
|
- **Component files**: `People_[FirstName].tsx` (e.g., `People_John.tsx`)
|
||||||
|
- **Page directories**: `[First_Last]/page.tsx` (e.g., `John_Doe/page.tsx`)
|
||||||
|
- **Image directories**: `first_last/` (e.g., `john_doe/`)
|
||||||
|
- **Export function**: `People_[FirstName]` (e.g., `People_John`)
|
||||||
|
|
||||||
|
## 🚨 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
|
||||||
|
- **Automatic Discovery**: Components are discovered by filename pattern `People_*.tsx`
|
||||||
|
- **Script Execution**: Always run `npm run generate-people-data` after adding new people
|
||||||
|
- **URL Structure**: Individual pages will be accessible at `/people/First_Last`
|
||||||
|
|
||||||
|
## 🎨 Template System Benefits
|
||||||
|
|
||||||
|
The system uses a **centralized template** (`PersonTemplate.tsx`) for all individual people pages:
|
||||||
|
|
||||||
|
- **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
|
||||||
|
|
||||||
|
## 📊 Current Status
|
||||||
|
|
||||||
|
- ✅ **Adnan Fatayerji** - Complete (auto-discovered)
|
||||||
|
- ✅ **Kristof De Spiegeleer** - Complete (auto-discovered)
|
||||||
|
- ✅ **Automation System** - Active and ready for new additions
|
||||||
|
|
||||||
|
## 🔮 Future Enhancements
|
||||||
|
|
||||||
|
The automation system can be extended to:
|
||||||
|
- Auto-generate page routes
|
||||||
|
- Validate image file existence
|
||||||
|
- Check for required data fields
|
||||||
|
- Generate TypeScript types from component data
|
||||||
|
- Create development-time file watchers for instant updates
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**🎉 The new automated system makes adding team members faster, safer, and more maintainable than ever before!**
|
134
docs/gmail-setup-guide.md
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
# Gmail Contact Form Setup Guide
|
||||||
|
|
||||||
|
This guide will help you configure your contact form to send emails to your company Gmail inbox.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- A Gmail account for your company
|
||||||
|
- Access to Google Account settings
|
||||||
|
- Basic understanding of environment variables
|
||||||
|
|
||||||
|
## Step 1: Enable 2-Factor Authentication
|
||||||
|
|
||||||
|
1. Go to [Google Account Settings](https://myaccount.google.com/)
|
||||||
|
2. Navigate to **Security** → **2-Step Verification**
|
||||||
|
3. Follow the prompts to enable 2FA if not already enabled
|
||||||
|
4. **Note**: App passwords require 2FA to be enabled
|
||||||
|
|
||||||
|
## Step 2: Generate Gmail App Password
|
||||||
|
|
||||||
|
1. Go to [Google Account Settings](https://myaccount.google.com/)
|
||||||
|
2. Navigate to **Security** → **2-Step Verification** → **App passwords**
|
||||||
|
3. Select **Mail** as the app and **Other** as the device
|
||||||
|
4. Enter "OurWorld Contact Form" as the device name
|
||||||
|
5. Click **Generate**
|
||||||
|
6. **Important**: Copy the 16-character app password immediately (you won't see it again)
|
||||||
|
|
||||||
|
## Step 3: Configure Environment Variables
|
||||||
|
|
||||||
|
1. Open the `.env.local` file in your project root
|
||||||
|
2. Replace the placeholder values with your actual credentials:
|
||||||
|
|
||||||
|
```env
|
||||||
|
# Your Gmail address (the one that will send emails)
|
||||||
|
GMAIL_USER=your-company-email@gmail.com
|
||||||
|
|
||||||
|
# The 16-character app password you generated (no spaces)
|
||||||
|
GMAIL_APP_PASSWORD=abcdabcdabcdabcd
|
||||||
|
|
||||||
|
# Email address where contact form submissions will be sent
|
||||||
|
COMPANY_EMAIL=info@ourworld.tf
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 4: Security Considerations
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
- **Never commit `.env.local` to version control**
|
||||||
|
- The `.env.local` file is already in `.gitignore`
|
||||||
|
- Use different environment variables for production deployment
|
||||||
|
|
||||||
|
### Production Deployment
|
||||||
|
For production (Vercel, Netlify, etc.), add these environment variables in your hosting platform's dashboard:
|
||||||
|
- `GMAIL_USER`
|
||||||
|
- `GMAIL_APP_PASSWORD`
|
||||||
|
- `COMPANY_EMAIL`
|
||||||
|
|
||||||
|
## Step 5: Testing the Setup
|
||||||
|
|
||||||
|
1. Start your development server:
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Navigate to `/contact` page
|
||||||
|
3. Fill out and submit the contact form
|
||||||
|
4. Check your Gmail inbox for the contact form submission
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
**"Authentication failed" error:**
|
||||||
|
- Verify 2FA is enabled on your Google account
|
||||||
|
- Double-check the app password (no spaces, 16 characters)
|
||||||
|
- Ensure you're using the app password, not your regular Gmail password
|
||||||
|
|
||||||
|
**"Invalid credentials" error:**
|
||||||
|
- Verify the `GMAIL_USER` email address is correct
|
||||||
|
- Check that the app password is correctly copied
|
||||||
|
|
||||||
|
**Emails not being received:**
|
||||||
|
- Check your spam/junk folder
|
||||||
|
- Verify the `COMPANY_EMAIL` address is correct
|
||||||
|
- Test with a different recipient email
|
||||||
|
|
||||||
|
**"Module not found" error:**
|
||||||
|
- Ensure nodemailer is installed: `npm install nodemailer @types/nodemailer`
|
||||||
|
|
||||||
|
### Email Format
|
||||||
|
|
||||||
|
The contact form sends emails with:
|
||||||
|
- **Subject**: "New Contact Form Submission from [First Name] [Last Name]"
|
||||||
|
- **From**: Your Gmail address (GMAIL_USER)
|
||||||
|
- **To**: Company email address (COMPANY_EMAIL)
|
||||||
|
- **Content**: Formatted HTML and plain text versions
|
||||||
|
|
||||||
|
## Security Best Practices
|
||||||
|
|
||||||
|
1. **Use App Passwords**: Never use your main Gmail password
|
||||||
|
2. **Limit Access**: Only share credentials with necessary team members
|
||||||
|
3. **Regular Rotation**: Consider rotating app passwords periodically
|
||||||
|
4. **Monitor Usage**: Check Gmail's security activity regularly
|
||||||
|
5. **Environment Separation**: Use different credentials for development/production
|
||||||
|
|
||||||
|
## Alternative Solutions
|
||||||
|
|
||||||
|
If you prefer not to use Gmail directly, consider these alternatives:
|
||||||
|
|
||||||
|
1. **EmailJS**: Client-side email service
|
||||||
|
2. **Formspree**: Form handling service
|
||||||
|
3. **SendGrid**: Professional email API
|
||||||
|
4. **Mailgun**: Email delivery service
|
||||||
|
5. **Resend**: Modern email API
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
If you encounter issues:
|
||||||
|
1. Check the browser console for error messages
|
||||||
|
2. Review the server logs for API errors
|
||||||
|
3. Verify all environment variables are set correctly
|
||||||
|
4. Test with a simple email first
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
project/
|
||||||
|
├── .env.local # Environment variables (not in git)
|
||||||
|
├── src/app/api/contact/ # API route for form submission
|
||||||
|
├── src/components/ContactHero.tsx # Contact form component
|
||||||
|
└── docs/gmail-setup-guide.md # This guide
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Important**: Keep your app password secure and never share it publicly. If compromised, revoke it immediately in your Google Account settings and generate a new one.
|
@ -1,4 +1,13 @@
|
|||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {}
|
const nextConfig = {
|
||||||
|
output: 'export',
|
||||||
|
trailingSlash: true,
|
||||||
|
images: {
|
||||||
|
unoptimized: true,
|
||||||
|
},
|
||||||
|
// Note: API routes in src/app/api/ will not work with static export
|
||||||
|
// The contact form API route will need to be handled separately
|
||||||
|
// Consider using a serverless function or external service for form handling
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = nextConfig
|
module.exports = nextConfig;
|
||||||
|
30
package-lock.json
generated
@ -9,13 +9,16 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^2.1.0",
|
"@headlessui/react": "^2.1.0",
|
||||||
|
"@heroicons/react": "^2.2.0",
|
||||||
"@tailwindcss/forms": "^0.5.3",
|
"@tailwindcss/forms": "^0.5.3",
|
||||||
"@tailwindcss/postcss": "^4.1.7",
|
"@tailwindcss/postcss": "^4.1.7",
|
||||||
"@types/node": "^20.10.8",
|
"@types/node": "^20.10.8",
|
||||||
|
"@types/nodemailer": "^6.4.17",
|
||||||
"@types/react": "^18.2.47",
|
"@types/react": "^18.2.47",
|
||||||
"@types/react-dom": "^18.2.18",
|
"@types/react-dom": "^18.2.18",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"next": "^14.0.4",
|
"next": "^14.0.4",
|
||||||
|
"nodemailer": "^7.0.5",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"tailwindcss": "^4.1.7",
|
"tailwindcss": "^4.1.7",
|
||||||
@ -205,6 +208,15 @@
|
|||||||
"react-dom": "^18"
|
"react-dom": "^18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@heroicons/react": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">= 16 || ^19.0.0-rc"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@humanwhocodes/config-array": {
|
"node_modules/@humanwhocodes/config-array": {
|
||||||
"version": "0.11.14",
|
"version": "0.11.14",
|
||||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
||||||
@ -1340,6 +1352,15 @@
|
|||||||
"undici-types": "~5.26.4"
|
"undici-types": "~5.26.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/nodemailer": {
|
||||||
|
"version": "6.4.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.17.tgz",
|
||||||
|
"integrity": "sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/prop-types": {
|
"node_modules/@types/prop-types": {
|
||||||
"version": "15.7.11",
|
"version": "15.7.11",
|
||||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
|
||||||
@ -3984,6 +4005,15 @@
|
|||||||
"node": "^10 || ^12 || >=14"
|
"node": "^10 || ^12 || >=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/nodemailer": {
|
||||||
|
"version": "7.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.5.tgz",
|
||||||
|
"integrity": "sha512-nsrh2lO3j4GkLLXoeEksAMgAOqxOv6QumNRVQTJwKH4nuiww6iC2y7GyANs9kRAxCexg3+lTWM3PZ91iLlVjfg==",
|
||||||
|
"license": "MIT-0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-assign": {
|
"node_modules/object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
@ -4,20 +4,25 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "npm run generate-people-data && next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint"
|
"lint": "next lint",
|
||||||
|
"generate-people-data": "node scripts/generate-people-data.js",
|
||||||
|
"generate-all-people": "node scripts/generate-all-people.js"
|
||||||
},
|
},
|
||||||
"browserslist": "defaults, not ie <= 11",
|
"browserslist": "defaults, not ie <= 11",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^2.1.0",
|
"@headlessui/react": "^2.1.0",
|
||||||
|
"@heroicons/react": "^2.2.0",
|
||||||
"@tailwindcss/forms": "^0.5.3",
|
"@tailwindcss/forms": "^0.5.3",
|
||||||
"@tailwindcss/postcss": "^4.1.7",
|
"@tailwindcss/postcss": "^4.1.7",
|
||||||
"@types/node": "^20.10.8",
|
"@types/node": "^20.10.8",
|
||||||
|
"@types/nodemailer": "^6.4.17",
|
||||||
"@types/react": "^18.2.47",
|
"@types/react": "^18.2.47",
|
||||||
"@types/react-dom": "^18.2.18",
|
"@types/react-dom": "^18.2.18",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"next": "^14.0.4",
|
"next": "^14.0.4",
|
||||||
|
"nodemailer": "^7.0.5",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"tailwindcss": "^4.1.7",
|
"tailwindcss": "^4.1.7",
|
||||||
|
BIN
public/images/about/exits.png
Normal file
After Width: | Height: | Size: 626 KiB |
BIN
public/images/contact.jpg
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
public/images/contact/contact.jpg
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
public/images/home/authentic.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
public/images/home/kds.png
Normal file
After Width: | Height: | Size: 137 KiB |
BIN
public/images/home/open.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
public/images/home/people.png
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
public/images/home/planet.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
public/images/home/quotes.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
public/images/home/simple.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
public/images/logos/CYBERCITY.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
public/images/logos/HERO.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
public/images/logos/freezone.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
public/images/logos/geomind.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
public/images/logos/indaba.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
public/images/logos/tHREEFOLD.png
Normal file
After Width: | Height: | Size: 13 KiB |
21
public/images/people/Malte_Geierhos/index.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
title: Malte Geierhos
|
||||||
|
weight: 2
|
||||||
|
description: Team Member
|
||||||
|
taxonomies:
|
||||||
|
people: [malte_geierhos]
|
||||||
|
memberships: []
|
||||||
|
categories: []
|
||||||
|
extra:
|
||||||
|
imgPath: malte_geierhos.jpeg
|
||||||
|
organizations: []
|
||||||
|
countries: []
|
||||||
|
cities: []
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: #,
|
||||||
|
websites: #,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
Malte Geierhos is a valued member of our team, bringing expertise and dedication to their role.
|
BIN
public/images/people/Malte_Geierhos/malte_geierhos.jpeg
Normal file
After Width: | Height: | Size: 26 KiB |
8
public/images/people/_index.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
title: "People"
|
||||||
|
paginate_by: 4
|
||||||
|
sort_by: "weight"
|
||||||
|
template: "layouts/people.html"
|
||||||
|
page_template: "partials/personCard.html"
|
||||||
|
insert_anchor_links: "left"
|
||||||
|
---
|
BIN
public/images/people/adnan_fatayerji/adnan_fatayerji.jpg
Normal file
After Width: | Height: | Size: 85 KiB |
20
public/images/people/adnan_fatayerji/index.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
title: Adnan Fateryji
|
||||||
|
weight: 2
|
||||||
|
description: Co-Founder, CEO of TF DMCC
|
||||||
|
taxonomies:
|
||||||
|
people: [adnan fatayerji]
|
||||||
|
memberships: []
|
||||||
|
categories: []
|
||||||
|
extra:
|
||||||
|
imgPath: adnan_fatayerji.jpg
|
||||||
|
organizations: []
|
||||||
|
countries: []
|
||||||
|
cities: []
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/adnansf/,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
Social entrepreneur and ethical technologist with experience advancing sovereign technologies.
|
BIN
public/images/people/alexandre_hannelas/alexandre_hannelas.jpeg
Normal file
After Width: | Height: | Size: 54 KiB |
22
public/images/people/alexandre_hannelas/index.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: Alexandre Hannelas
|
||||||
|
weight: 3
|
||||||
|
description: Finance Manager
|
||||||
|
taxonomies:
|
||||||
|
people: [alexandre_hannelas]
|
||||||
|
memberships: []
|
||||||
|
categories: []
|
||||||
|
extra:
|
||||||
|
imgPath: alexandre_hannelas.jpeg
|
||||||
|
organizations: []
|
||||||
|
countries: [France]
|
||||||
|
cities: [Aix-en-Provence]
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/alexandre-hannelas-741681112/,
|
||||||
|
github: https://github.com/AlexandreHannelas,
|
||||||
|
websites: https://threefold.io/,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
Capable finance specialist with a deep passion in driving lean and innovative business models.
|
BIN
public/images/people/amrit_gayan/amrit_gayan.jpeg
Normal file
After Width: | Height: | Size: 69 KiB |
20
public/images/people/amrit_gayan/index.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
title: Amrit Gayan
|
||||||
|
weight: 4
|
||||||
|
description: Advisor, Finance & Operations
|
||||||
|
taxonomies:
|
||||||
|
people: [amrit_gayan]
|
||||||
|
memberships: []
|
||||||
|
categories: []
|
||||||
|
extra:
|
||||||
|
imgPath: amrit_gayan.jpeg
|
||||||
|
organizations: []
|
||||||
|
countries: []
|
||||||
|
cities: []
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/amrit-gayan-mba-38083462,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
+20 years experience leading award-winning banks. AI and technology enthusiast.
|
BIN
public/images/people/chris_camponovo/chris_camponovo.jpeg
Normal file
After Width: | Height: | Size: 24 KiB |
20
public/images/people/chris_camponovo/index.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
title: Chris Camponovo
|
||||||
|
weight: 2
|
||||||
|
description: Director, Co-Founder of Zanzibar Digital Free Zone
|
||||||
|
taxonomies:
|
||||||
|
people: [chris_camponovo]
|
||||||
|
memberships: []
|
||||||
|
categories: []
|
||||||
|
extra:
|
||||||
|
imgPath: chris_camponovo.jpeg
|
||||||
|
organizations: []
|
||||||
|
countries: []
|
||||||
|
cities: []
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/christopher-camponovo-a8a57515/,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
Former white house lawyer and private equity fund founder that drives multinational asset recovery solutions.
|
BIN
public/images/people/florian_fournier/florian_fournier.jpeg
Normal file
After Width: | Height: | Size: 241 KiB |
21
public/images/people/florian_fournier/index.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
title: Florian Fournier
|
||||||
|
weight: 2
|
||||||
|
description: Co-Founder
|
||||||
|
taxonomies:
|
||||||
|
people: [florian_fournier]
|
||||||
|
memberships: []
|
||||||
|
categories: []
|
||||||
|
extra:
|
||||||
|
imgPath: florian_fournier.jpeg
|
||||||
|
organizations: []
|
||||||
|
countries: [Brazil]
|
||||||
|
cities: [Rio de Janeiro]
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/florianfournier/,
|
||||||
|
websites: https://www.ootopia.org/,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
Experienced social entrepreneur and marketing executive with extensive network in the US and Latam.
|
BIN
public/images/people/gregory_flipo/gregory_flipo.jpg
Normal file
After Width: | Height: | Size: 88 KiB |
21
public/images/people/gregory_flipo/index.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
title: Gregory Flipo
|
||||||
|
weight: 2
|
||||||
|
description: Co-Founder, CEO of Sikana
|
||||||
|
taxonomies:
|
||||||
|
people: [gregory_flipo]
|
||||||
|
memberships: []
|
||||||
|
categories: []
|
||||||
|
extra:
|
||||||
|
imgPath: gregory_flipo.jpg
|
||||||
|
organizations: []
|
||||||
|
countries: []
|
||||||
|
cities: []
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/gregory-flipo-34a9947/,
|
||||||
|
websites: https://threefold.io/,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
Social entrepreneur with 20 years of experience building educational content and platforms used by millions today.
|
BIN
public/images/people/hugo_mathecowitsch/hugo_mathecowitsch.jpeg
Normal file
After Width: | Height: | Size: 117 KiB |
20
public/images/people/hugo_mathecowitsch/index.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
title: Hugo Mathecowitsch
|
||||||
|
weight: 4
|
||||||
|
description: Advisor, Product
|
||||||
|
taxonomies:
|
||||||
|
people: [hugo_mathecowitsch]
|
||||||
|
memberships: []
|
||||||
|
categories: []
|
||||||
|
extra:
|
||||||
|
imgPath: hugo_mathecowitsch.jpeg
|
||||||
|
organizations: []
|
||||||
|
countries: []
|
||||||
|
cities: []
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/hugo-mathecowitsch-9447a3213,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
Launched leading fintech startups in South America. Regenerating public goods.
|
22
public/images/people/jan_de_landtsheer/index.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: Jan De Landtsheer
|
||||||
|
weight: 4
|
||||||
|
description: Advisor, Tech
|
||||||
|
taxonomies:
|
||||||
|
people: [jan_de_landtsheer]
|
||||||
|
memberships: []
|
||||||
|
categories: []
|
||||||
|
extra:
|
||||||
|
imgPath: jan_de_landtsheer.jpeg
|
||||||
|
organizations: []
|
||||||
|
countries: []
|
||||||
|
cities: []
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/jand2/,
|
||||||
|
github: https://github.com/delandtj,
|
||||||
|
websites: https://threefold.tech,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
+30 years experience building deep tech. Co-Founder at ThreeFold.
|
BIN
public/images/people/jan_de_landtsheer/jan_de_landtsheer.jpeg
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
public/images/people/jan_de_landtsheer/jan_de_landtsheer.png
Normal file
After Width: | Height: | Size: 253 KiB |
21
public/images/people/karoline_zizka/index.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
title: Karoline Zizka
|
||||||
|
weight: 2
|
||||||
|
description: HR Coordinator
|
||||||
|
taxonomies:
|
||||||
|
people: [karoline_zizka]
|
||||||
|
memberships: []
|
||||||
|
categories: []
|
||||||
|
extra:
|
||||||
|
imgPath: karoline_zizka.jpeg
|
||||||
|
organizations: []
|
||||||
|
countries: [Austria]
|
||||||
|
cities: [Vienna]
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/karoline-zizka-52a287b6/,
|
||||||
|
websites: https://threefold.io/,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
Passionate about driving people-centric strategies with effective KPIs, supporting venture growth and employee satisfaction.
|
BIN
public/images/people/karoline_zizka/karoline_zizka.jpeg
Normal file
After Width: | Height: | Size: 47 KiB |
22
public/images/people/kristof_de_spiegeleer/index.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: Kristof de Spiegeleer
|
||||||
|
weight: 1
|
||||||
|
description: Co-Founder & CEO
|
||||||
|
taxonomies:
|
||||||
|
people: [kristof_de_spiegeleer]
|
||||||
|
memberships: []
|
||||||
|
categories: []
|
||||||
|
extra:
|
||||||
|
imgPath: kristof_de_spiegeleer.jpeg
|
||||||
|
organizations: []
|
||||||
|
countries: [UAE, Egypt]
|
||||||
|
cities: [Dubai, El Gouna]
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/despiegk/,
|
||||||
|
github: https://github.com/despiegk,
|
||||||
|
websites: https://www.threefold.io, https://www.incubaid.com/,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
Serial entrepreneur with 30 years of experience in Internet and cloud, with + USD 600m in exits.
|
After Width: | Height: | Size: 106 KiB |
21
public/images/people/marion_ravarino/index.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
title: Marion Ravarino
|
||||||
|
weight: 3
|
||||||
|
description: Project Management
|
||||||
|
taxonomies:
|
||||||
|
people: [marion_ravarino]
|
||||||
|
memberships: []
|
||||||
|
categories: [foundation]
|
||||||
|
extra:
|
||||||
|
imgPath: marion_ravarino.jpeg
|
||||||
|
organizations: [threefold_tech]
|
||||||
|
countries: [Germany]
|
||||||
|
cities: []
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/marion-ravarino/,
|
||||||
|
websites: https://threefold.io/,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
Driven by a natural curiosity for new knowledge. OurWorld's mission and values are perfectly aligned with my vision of a better future for all.
|
BIN
public/images/people/marion_ravarino/marion_ravarino.jpeg
Normal file
After Width: | Height: | Size: 79 KiB |
20
public/images/people/michel_coucke/index.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
title: Michel Coucke
|
||||||
|
weight: 1
|
||||||
|
description: Director
|
||||||
|
taxonomies:
|
||||||
|
people: [michel_coucke]
|
||||||
|
memberships: []
|
||||||
|
categories: []
|
||||||
|
extra:
|
||||||
|
imgPath: michel_coucke.jpg
|
||||||
|
organizations: []
|
||||||
|
countries: []
|
||||||
|
cities: []
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/michel-coucke-66112a,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
CEO Lancor, a global executive search firm specializing in board-level search mandates including C-level execs and more.
|
BIN
public/images/people/michel_coucke/michel_coucke.jpg
Normal file
After Width: | Height: | Size: 44 KiB |
20
public/images/people/nousrath_bhugeloo/index.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
title: Nousrath Bhugeloo
|
||||||
|
weight: 1
|
||||||
|
description: Director & Executive Chairperson
|
||||||
|
taxonomies:
|
||||||
|
people: [nousrath_bhugeloo]
|
||||||
|
memberships: []
|
||||||
|
categories: []
|
||||||
|
extra:
|
||||||
|
imgPath: nousrath_bhugeloo.jpg
|
||||||
|
organizations: []
|
||||||
|
countries: []
|
||||||
|
cities: []
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/nousrath-bhugeloo-78078520,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
Advisor to multinationals, funds, family offices and individual entrepreneurs on their strategies and cross border business.
|
BIN
public/images/people/nousrath_bhugeloo/nousrath_bhugeloo.jpg
Normal file
After Width: | Height: | Size: 47 KiB |
21
public/images/people/owen_kemp/index.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
title: Owen Kemp
|
||||||
|
weight: 4
|
||||||
|
description: Advisor, Business Development
|
||||||
|
taxonomies:
|
||||||
|
people: [owen_kemp]
|
||||||
|
memberships: []
|
||||||
|
categories: []
|
||||||
|
extra:
|
||||||
|
imgPath: owen_kemp.jpeg
|
||||||
|
organizations: []
|
||||||
|
countries: [Austria]
|
||||||
|
cities: [Vienna]
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/owen-kemp-b6b5a113/,
|
||||||
|
websites: https://threefold.tech,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
Former HPE VP Innovation & International Relations. Co-Founder at TF9.
|
BIN
public/images/people/owen_kemp/owen_kemp.jpeg
Normal file
After Width: | Height: | Size: 260 KiB |
22
public/images/people/sacha_obeegadoo/index.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: Sacha Obeegadoo
|
||||||
|
weight: 2
|
||||||
|
description: Program Manager
|
||||||
|
taxonomies:
|
||||||
|
people: [sacha_obeegadoo]
|
||||||
|
memberships: []
|
||||||
|
categories: []
|
||||||
|
extra:
|
||||||
|
imgPath: sacha_obeegadoo.jpg
|
||||||
|
organizations: []
|
||||||
|
countries: [Nomad]
|
||||||
|
cities: [Nomad]
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/sachaobeegadoo/,
|
||||||
|
github: https://github.com/Sacha96,
|
||||||
|
websites: https://threefold.io/,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
Business Developer with experience growing companies across various sectors.
|
BIN
public/images/people/sacha_obeegadoo/sacha_obeegadoo.jpg
Normal file
After Width: | Height: | Size: 96 KiB |
22
public/images/people/sam_taggart/index.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: Sam Taggart
|
||||||
|
weight: 4
|
||||||
|
description: Communication & Community
|
||||||
|
taxonomies:
|
||||||
|
people: [sam_taggart]
|
||||||
|
memberships: []
|
||||||
|
categories: []
|
||||||
|
extra:
|
||||||
|
imgPath: sam_taggart.jpg
|
||||||
|
organizations: []
|
||||||
|
countries: [Turkey, USA]
|
||||||
|
cities: [Akyaka, New York, Philadelphia]
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/samtaggart/,
|
||||||
|
github: https://github.com/samtaggart,
|
||||||
|
websites: https://threefold.io/,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
Communication strategist and global citizen, inspired by the beauty of our differences and humbled by our similarities. Driven to drive collective positive change.
|
BIN
public/images/people/sam_taggart/sam_taggart.jpg
Normal file
After Width: | Height: | Size: 475 KiB |
22
public/images/people/sasha_astiadi/index.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: Sasha Astiadi
|
||||||
|
weight: 4
|
||||||
|
description: Web Operations
|
||||||
|
taxonomies:
|
||||||
|
people: [sasha_astiadi]
|
||||||
|
memberships: []
|
||||||
|
categories: []
|
||||||
|
extra:
|
||||||
|
imgPath: sasha_astiadi.png
|
||||||
|
organizations: []
|
||||||
|
countries: [Indonesia, Spain, Germany]
|
||||||
|
cities: [Bali, Barcelona, Berlin]
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/sastiadi,
|
||||||
|
github: https://github.com/sasha-astiadi,
|
||||||
|
websites: https://threefold.io/, https://threefold.tech, https://sashaastiadi.com/,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
A Human-centered product manager, researcher and UX Designer who puts the planet as her main mission.
|
BIN
public/images/people/sasha_astiadi/sasha_astiadi.png
Normal file
After Width: | Height: | Size: 504 KiB |
22
public/images/people/timur_gordon/index.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: Timur Gordon
|
||||||
|
weight: 4
|
||||||
|
description: Software Developer
|
||||||
|
taxonomies:
|
||||||
|
people: [timur_gordon]
|
||||||
|
memberships: []
|
||||||
|
categories: [foundation]
|
||||||
|
extra:
|
||||||
|
imgPath: timur_gordon.jpg
|
||||||
|
organizations: [threefold_foundation]
|
||||||
|
countries: [Turkey, USA]
|
||||||
|
cities: [Istanbul, Zanzibar]
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/timur-gordon/,
|
||||||
|
github: https://github.com/timurgordon,
|
||||||
|
websites: https://threefold.io,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
Software developer who loves nature and believes in using tech to help the planet and people. Believer in open source and collective intelligence.
|
BIN
public/images/people/timur_gordon/timur_gordon.jpg
Normal file
After Width: | Height: | Size: 207 KiB |
21
public/images/people/vianney_spriet/index.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
title: Vianney Spriet
|
||||||
|
weight: 3
|
||||||
|
description: Business Development
|
||||||
|
taxonomies:
|
||||||
|
people: [vianney_spriet]
|
||||||
|
memberships: []
|
||||||
|
categories: []
|
||||||
|
extra:
|
||||||
|
imgPath: vianney_spriet.jpeg
|
||||||
|
organizations: []
|
||||||
|
countries: [France]
|
||||||
|
cities: []
|
||||||
|
private: 0
|
||||||
|
socialLinks: {
|
||||||
|
LinkedIn: https://www.linkedin.com/in/vianney-spriet-09737526/,
|
||||||
|
websites: https://ourworld.tf,
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
Experienced wealth manager, business developer, and former professional skipper with a wide network in Europe.
|
BIN
public/images/people/vianney_spriet/vianney_spriet.jpeg
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
public/images/ventures/cybercity.png
Normal file
After Width: | Height: | Size: 8.2 KiB |
BIN
public/images/ventures/earth.jpg
Normal file
After Width: | Height: | Size: 1.4 MiB |
BIN
public/images/ventures/freezone.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
public/images/ventures/geo_ico.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
public/images/ventures/habari.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
public/images/ventures/hero.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
public/images/ventures/ow_icon.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
public/images/ventures/tf.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
public/videos/ourworld.mp4
Normal file
230
scripts/generate-all-people.js
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const fs = require('fs')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
// Paths
|
||||||
|
const peopleImagesDir = path.join(__dirname, '../public/images/people')
|
||||||
|
const peopleComponentsDir = path.join(__dirname, '../src/components/people')
|
||||||
|
const peopleAppDir = path.join(__dirname, '../src/app/people')
|
||||||
|
|
||||||
|
// Function to parse markdown frontmatter
|
||||||
|
function parseFrontmatter(content) {
|
||||||
|
const frontmatterRegex = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/
|
||||||
|
const match = content.match(frontmatterRegex)
|
||||||
|
|
||||||
|
if (!match) return null
|
||||||
|
|
||||||
|
const frontmatter = {}
|
||||||
|
const body = match[2].trim()
|
||||||
|
|
||||||
|
// Parse YAML-like frontmatter
|
||||||
|
const lines = match[1].split('\n')
|
||||||
|
let currentKey = null
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmed = line.trim()
|
||||||
|
if (!trimmed) continue
|
||||||
|
|
||||||
|
if (trimmed.includes(':') && !trimmed.startsWith(' ')) {
|
||||||
|
const [key, ...valueParts] = trimmed.split(':')
|
||||||
|
const value = valueParts.join(':').trim()
|
||||||
|
currentKey = key.trim()
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
if (value.startsWith('[') && value.endsWith(']')) {
|
||||||
|
// Array value
|
||||||
|
frontmatter[currentKey] = value.slice(1, -1).split(',').map(v => v.trim())
|
||||||
|
} else if (value.startsWith('{') && value.endsWith('}')) {
|
||||||
|
// Object value - parse simple key-value pairs
|
||||||
|
const obj = {}
|
||||||
|
const objContent = value.slice(1, -1)
|
||||||
|
const pairs = objContent.split(',')
|
||||||
|
for (const pair of pairs) {
|
||||||
|
const [k, v] = pair.split(':').map(s => s.trim())
|
||||||
|
if (k && v) {
|
||||||
|
obj[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frontmatter[currentKey] = obj
|
||||||
|
} else {
|
||||||
|
frontmatter[currentKey] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { frontmatter, body }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to convert name to component name
|
||||||
|
function nameToComponentName(name) {
|
||||||
|
return name.replace(/\s+/g, '_').replace(/[^a-zA-Z0-9_]/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to convert name to function name
|
||||||
|
function nameToFunctionName(name) {
|
||||||
|
return 'People_' + nameToComponentName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to generate people component
|
||||||
|
function generatePeopleComponent(personData) {
|
||||||
|
const { name, role, imageUrl, linkedinUrl, description } = personData
|
||||||
|
const functionName = nameToFunctionName(name)
|
||||||
|
|
||||||
|
return `import { PersonTemplate } from '@/components/PersonTemplate'
|
||||||
|
|
||||||
|
export const data = [
|
||||||
|
{
|
||||||
|
name: '${name}',
|
||||||
|
role: '${role}',
|
||||||
|
imageUrl: '${imageUrl}',
|
||||||
|
xUrl: '#',
|
||||||
|
linkedinUrl: '${linkedinUrl || '#'}',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const biography = \`
|
||||||
|
<p class="text-lg/7">
|
||||||
|
${description || `${name} is a valued member of our team, bringing expertise and dedication to their role as ${role}.`}
|
||||||
|
</p>
|
||||||
|
\`
|
||||||
|
|
||||||
|
export function ${functionName}() {
|
||||||
|
return <PersonTemplate personData={data[0]} biography={biography} />
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to generate page component
|
||||||
|
function generatePageComponent(personData) {
|
||||||
|
const { name } = personData
|
||||||
|
const functionName = nameToFunctionName(name)
|
||||||
|
const componentName = nameToComponentName(name)
|
||||||
|
const pageFunctionName = name.replace(/\s+/g, '') + 'Page'
|
||||||
|
|
||||||
|
return `import { CallToAction } from '@/components/CallToAction'
|
||||||
|
import { Faqs } from '@/components/Faqs'
|
||||||
|
import { Footer } from '@/components/Footer'
|
||||||
|
import { Header_darkbg } from '@/components/Header_darkbg'
|
||||||
|
import { ${functionName} } from '@/components/people/People_${componentName}'
|
||||||
|
|
||||||
|
export default function ${pageFunctionName}() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<${functionName} />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main function
|
||||||
|
function main() {
|
||||||
|
console.log('🔍 Scanning for people data...')
|
||||||
|
|
||||||
|
if (!fs.existsSync(peopleImagesDir)) {
|
||||||
|
console.log('❌ People images directory not found')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const peopleDirectories = fs.readdirSync(peopleImagesDir)
|
||||||
|
.filter(dir => {
|
||||||
|
const dirPath = path.join(peopleImagesDir, dir)
|
||||||
|
return fs.statSync(dirPath).isDirectory() && dir !== '_index.md'
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(`✅ Found ${peopleDirectories.length} people directories`)
|
||||||
|
|
||||||
|
let generatedCount = 0
|
||||||
|
let updatedCount = 0
|
||||||
|
|
||||||
|
for (const personDir of peopleDirectories) {
|
||||||
|
try {
|
||||||
|
const indexPath = path.join(peopleImagesDir, personDir, 'index.md')
|
||||||
|
|
||||||
|
if (!fs.existsSync(indexPath)) {
|
||||||
|
console.log(`⚠️ No index.md found for ${personDir}`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = fs.readFileSync(indexPath, 'utf8')
|
||||||
|
const parsed = parseFrontmatter(content)
|
||||||
|
|
||||||
|
if (!parsed) {
|
||||||
|
console.log(`⚠️ Could not parse frontmatter for ${personDir}`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const { frontmatter, body } = parsed
|
||||||
|
|
||||||
|
// Find the actual image file
|
||||||
|
const imageExtensions = ['jpg', 'jpeg', 'png']
|
||||||
|
let actualImagePath = frontmatter.extra?.imgPath
|
||||||
|
|
||||||
|
if (!actualImagePath) {
|
||||||
|
// Try to find the image file with different extensions
|
||||||
|
for (const ext of imageExtensions) {
|
||||||
|
const imagePath = path.join(peopleImagesDir, personDir, `${personDir}.${ext}`)
|
||||||
|
if (fs.existsSync(imagePath)) {
|
||||||
|
actualImagePath = `${personDir}.${ext}`
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback to jpg if no image found
|
||||||
|
actualImagePath = actualImagePath || `${personDir}.jpg`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract person data
|
||||||
|
const personData = {
|
||||||
|
name: frontmatter.title || personDir.replace(/_/g, ' '),
|
||||||
|
role: frontmatter.description || 'Team Member',
|
||||||
|
imageUrl: `/images/people/${personDir}/${actualImagePath}`,
|
||||||
|
linkedinUrl: frontmatter.extra?.socialLinks?.LinkedIn || '#',
|
||||||
|
description: body || `${frontmatter.title} is a valued member of our team.`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate component
|
||||||
|
const componentName = nameToComponentName(personData.name)
|
||||||
|
const componentPath = path.join(peopleComponentsDir, `People_${componentName}.tsx`)
|
||||||
|
|
||||||
|
if (!fs.existsSync(componentPath)) {
|
||||||
|
const componentContent = generatePeopleComponent(personData)
|
||||||
|
fs.writeFileSync(componentPath, componentContent, 'utf8')
|
||||||
|
console.log(`✅ Generated component: People_${componentName}.tsx`)
|
||||||
|
generatedCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update page.tsx
|
||||||
|
const pagePath = path.join(peopleAppDir, personDir, 'page.tsx')
|
||||||
|
|
||||||
|
if (fs.existsSync(pagePath)) {
|
||||||
|
const pageContent = generatePageComponent(personData)
|
||||||
|
fs.writeFileSync(pagePath, pageContent, 'utf8')
|
||||||
|
console.log(`✅ Updated page: ${personDir}/page.tsx`)
|
||||||
|
updatedCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ Error processing ${personDir}:`, error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\n🎉 Generation complete!`)
|
||||||
|
console.log(` - Generated ${generatedCount} new components`)
|
||||||
|
console.log(` - Updated ${updatedCount} page files`)
|
||||||
|
console.log(`\n📝 Run 'npm run generate-people-data' to update the data registry`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the script
|
||||||
|
if (require.main === module) {
|
||||||
|
main()
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { main }
|
127
scripts/generate-people-data.js
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const fs = require('fs')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
// Paths
|
||||||
|
const peopleComponentsDir = path.join(__dirname, '../src/components/people')
|
||||||
|
const peopleDataFile = path.join(__dirname, '../src/lib/peopleData.ts')
|
||||||
|
|
||||||
|
// Function to get all People_*.tsx files
|
||||||
|
function getPeopleComponents() {
|
||||||
|
if (!fs.existsSync(peopleComponentsDir)) {
|
||||||
|
console.log('People components directory not found')
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = fs.readdirSync(peopleComponentsDir)
|
||||||
|
return files
|
||||||
|
.filter(file => file.startsWith('People_') && file.endsWith('.tsx'))
|
||||||
|
.map(file => {
|
||||||
|
const componentName = file.replace('.tsx', '')
|
||||||
|
const importName = componentName.toLowerCase().replace('people_', '')
|
||||||
|
return {
|
||||||
|
fileName: file,
|
||||||
|
componentName,
|
||||||
|
importName,
|
||||||
|
importPath: `@/components/people/${componentName}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to generate the peopleData.ts content
|
||||||
|
function generatePeopleDataContent(components) {
|
||||||
|
const imports = components
|
||||||
|
.map(comp => `import { data as ${comp.importName}Data } from '${comp.importPath}'`)
|
||||||
|
.join('\n')
|
||||||
|
|
||||||
|
const dynamicImports = components
|
||||||
|
.map(comp => ` () => import('${comp.importPath}'),`)
|
||||||
|
.join('\n')
|
||||||
|
|
||||||
|
const syncDataPush = components
|
||||||
|
.map(comp => `
|
||||||
|
try {
|
||||||
|
allPeopleData.push(...${comp.importName}Data)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading ${comp.importName} data:', error)
|
||||||
|
}`)
|
||||||
|
.join('\n')
|
||||||
|
|
||||||
|
return `// This file is auto-generated by scripts/generate-people-data.js
|
||||||
|
// When new people components are added, run: npm run generate-people-data
|
||||||
|
|
||||||
|
export interface PersonData {
|
||||||
|
name: string
|
||||||
|
role: string
|
||||||
|
imageUrl: string
|
||||||
|
xUrl: string
|
||||||
|
linkedinUrl: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to dynamically get all people data
|
||||||
|
export async function getAllPeopleData(): Promise<PersonData[]> {
|
||||||
|
const allPeopleData: PersonData[] = []
|
||||||
|
|
||||||
|
// Auto-generated list of all people components
|
||||||
|
const peopleComponents = [
|
||||||
|
${dynamicImports}
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const importComponent of peopleComponents) {
|
||||||
|
try {
|
||||||
|
const module = await importComponent()
|
||||||
|
if (module.data && Array.isArray(module.data)) {
|
||||||
|
allPeopleData.push(...module.data)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading people data:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allPeopleData
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchronous version using static imports for immediate data access
|
||||||
|
${imports}
|
||||||
|
|
||||||
|
export function getAllPeopleDataSync(): PersonData[] {
|
||||||
|
const allPeopleData: PersonData[] = []
|
||||||
|
${syncDataPush}
|
||||||
|
|
||||||
|
return allPeopleData
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main function
|
||||||
|
function main() {
|
||||||
|
console.log('🔍 Scanning for people components...')
|
||||||
|
|
||||||
|
const components = getPeopleComponents()
|
||||||
|
|
||||||
|
if (components.length === 0) {
|
||||||
|
console.log('❌ No people components found')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`✅ Found ${components.length} people component(s):`)
|
||||||
|
components.forEach(comp => {
|
||||||
|
console.log(` - ${comp.componentName}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('📝 Generating peopleData.ts...')
|
||||||
|
|
||||||
|
const content = generatePeopleDataContent(components)
|
||||||
|
fs.writeFileSync(peopleDataFile, content, 'utf8')
|
||||||
|
|
||||||
|
console.log('✅ Successfully updated src/lib/peopleData.ts')
|
||||||
|
console.log('\n🎉 All people components will now automatically appear on the people page!')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the script
|
||||||
|
if (require.main === module) {
|
||||||
|
main()
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { main, getPeopleComponents, generatePeopleDataContent }
|
34
src/app/about/page.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
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'
|
||||||
|
|
||||||
|
export default function About() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<AboutHero />
|
||||||
|
<AboutMission />
|
||||||
|
<AboutExperience />
|
||||||
|
<Quote />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
80
src/app/api/contact/route.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import { NextRequest, NextResponse } from 'next/server'
|
||||||
|
import nodemailer from 'nodemailer'
|
||||||
|
|
||||||
|
export async function POST(request: NextRequest) {
|
||||||
|
try {
|
||||||
|
const body = await request.json()
|
||||||
|
const { firstName, lastName, email, phoneNumber, message } = body
|
||||||
|
|
||||||
|
// Validate required fields
|
||||||
|
if (!firstName || !lastName || !email || !message) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Missing required fields' },
|
||||||
|
{ status: 400 }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create transporter using Gmail SMTP
|
||||||
|
const transporter = nodemailer.createTransport({
|
||||||
|
service: 'gmail',
|
||||||
|
auth: {
|
||||||
|
user: process.env.GMAIL_USER,
|
||||||
|
pass: process.env.GMAIL_APP_PASSWORD,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Email content
|
||||||
|
const mailOptions = {
|
||||||
|
from: process.env.GMAIL_USER,
|
||||||
|
to: process.env.COMPANY_EMAIL || process.env.GMAIL_USER,
|
||||||
|
subject: `New Contact Form Submission from ${firstName} ${lastName}`,
|
||||||
|
html: `
|
||||||
|
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
|
||||||
|
<h2 style="color: #4f46e5;">New Contact Form Submission</h2>
|
||||||
|
|
||||||
|
<div style="background-color: #f8fafc; padding: 20px; border-radius: 8px; margin: 20px 0;">
|
||||||
|
<h3 style="margin-top: 0; color: #374151;">Contact Information</h3>
|
||||||
|
<p><strong>Name:</strong> ${firstName} ${lastName}</p>
|
||||||
|
<p><strong>Email:</strong> <a href="mailto:${email}">${email}</a></p>
|
||||||
|
${phoneNumber ? `<p><strong>Phone:</strong> ${phoneNumber}</p>` : ''}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="background-color: #f8fafc; padding: 20px; border-radius: 8px; margin: 20px 0;">
|
||||||
|
<h3 style="margin-top: 0; color: #374151;">Message</h3>
|
||||||
|
<p style="white-space: pre-wrap;">${message}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #e5e7eb; color: #6b7280; font-size: 14px;">
|
||||||
|
<p>This email was sent from the OurWorld contact form on ${new Date().toLocaleString()}.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
text: `
|
||||||
|
New Contact Form Submission
|
||||||
|
|
||||||
|
Name: ${firstName} ${lastName}
|
||||||
|
Email: ${email}
|
||||||
|
${phoneNumber ? `Phone: ${phoneNumber}` : ''}
|
||||||
|
|
||||||
|
Message:
|
||||||
|
${message}
|
||||||
|
|
||||||
|
Sent on: ${new Date().toLocaleString()}
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send email
|
||||||
|
await transporter.sendMail(mailOptions)
|
||||||
|
|
||||||
|
return NextResponse.json(
|
||||||
|
{ message: 'Email sent successfully' },
|
||||||
|
{ status: 200 }
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error sending email:', error)
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Failed to send email' },
|
||||||
|
{ status: 500 }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
33
src/app/contact/page.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
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 { ContactHero } from '@/components/ContactHero'
|
||||||
|
|
||||||
|
export default function Contact() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<ContactHero />
|
||||||
|
<Quote />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
import { type Metadata } from 'next'
|
import { type Metadata } from 'next'
|
||||||
import { Inter, Lexend } from 'next/font/google'
|
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
|
|
||||||
import '@/styles/tailwind.css'
|
import '@/styles/tailwind.css'
|
||||||
@ -10,20 +9,13 @@ export const metadata: Metadata = {
|
|||||||
default: 'OurWorld - Accounting made simple for small businesses',
|
default: 'OurWorld - Accounting made simple for small businesses',
|
||||||
},
|
},
|
||||||
description:
|
description:
|
||||||
'Most bookkeeping software is accurate, but hard to use. We make the opposite trade-off, and hope you don’t get audited.',
|
'Most bookkeeping software is accurate, but hard to use. We make the opposite trade-off, and hope you don\'t get audited.',
|
||||||
}
|
}
|
||||||
|
|
||||||
const inter = Inter({
|
// Avenir font configuration with fallbacks
|
||||||
subsets: ['latin'],
|
const avenir = {
|
||||||
display: 'swap',
|
variable: '--font-avenir',
|
||||||
variable: '--font-inter',
|
}
|
||||||
})
|
|
||||||
|
|
||||||
const lexend = Lexend({
|
|
||||||
subsets: ['latin'],
|
|
||||||
display: 'swap',
|
|
||||||
variable: '--font-lexend',
|
|
||||||
})
|
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
children,
|
children,
|
||||||
@ -35,8 +27,7 @@ export default function RootLayout({
|
|||||||
lang="en"
|
lang="en"
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'h-full scroll-smooth bg-white antialiased',
|
'h-full scroll-smooth bg-white antialiased',
|
||||||
inter.variable,
|
avenir.variable,
|
||||||
lexend.variable,
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<body className="flex h-full flex-col">{children}</body>
|
<body className="flex h-full flex-col">{children}</body>
|
||||||
|
@ -1,24 +1,31 @@
|
|||||||
import { CallToAction } from '@/components/CallToAction'
|
import { CallToAction } from '@/components/CallToAction'
|
||||||
import { Faqs } from '@/components/Faqs'
|
import { Faqs } from '@/components/Faqs'
|
||||||
import { Footer } from '@/components/Footer'
|
import { Footer } from '@/components/Footer'
|
||||||
import { Header } from '@/components/Header'
|
import { HomeAbout } from '@/components/HomeAbout'
|
||||||
import { Hero } from '@/components/Hero'
|
import { Hero } from '@/components/Hero'
|
||||||
import { Pricing } from '@/components/Pricing'
|
import { Pricing } from '@/components/Pricing'
|
||||||
import { PrimaryFeatures } from '@/components/PrimaryFeatures'
|
import { PrimaryFeatures } from '@/components/PrimaryFeatures'
|
||||||
import { SecondaryFeatures } from '@/components/SecondaryFeatures'
|
import { SecondaryFeatures } from '@/components/SecondaryFeatures'
|
||||||
import { Testimonials } from '@/components/Testimonials'
|
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 { Header } from '@/components/Header'
|
||||||
|
import { Header_darkbg } from '@/components/Header_darkbg'
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header />
|
<Header_darkbg />
|
||||||
<main>
|
<main>
|
||||||
<Hero />
|
<Hero />
|
||||||
<PrimaryFeatures />
|
<HomeAbout />
|
||||||
<SecondaryFeatures />
|
<HomePrinciples />
|
||||||
|
<HomeMilestones />
|
||||||
|
<HomeVentures />
|
||||||
|
<Quote />
|
||||||
<CallToAction />
|
<CallToAction />
|
||||||
<Testimonials />
|
|
||||||
<Pricing />
|
|
||||||
<Faqs />
|
<Faqs />
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
19
src/app/people/Adnan_Fatayerji/page.tsx
Normal 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_Fateryji } from '@/components/people/People_Adnan_Fateryji'
|
||||||
|
|
||||||
|
export default function AdnanFateryjiPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Adnan_Fateryji />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
19
src/app/people/Malte_Geierhos/page.tsx
Normal 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_Malte_Geierhos } from '@/components/people/People_Malte_Geierhos'
|
||||||
|
|
||||||
|
export default function MalteGeierhosPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Malte_Geierhos />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
19
src/app/people/alexandre_hannelas/page.tsx
Normal 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_Alexandre_Hannelas } from '@/components/people/People_Alexandre_Hannelas'
|
||||||
|
|
||||||
|
export default function AlexandreHannelasPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Alexandre_Hannelas />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
19
src/app/people/amrit_gayan/page.tsx
Normal 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_Amrit_Gayan } from '@/components/people/People_Amrit_Gayan'
|
||||||
|
|
||||||
|
export default function AmritGayanPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Amrit_Gayan />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
19
src/app/people/chris_camponovo/page.tsx
Normal 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_Chris_Camponovo } from '@/components/people/People_Chris_Camponovo'
|
||||||
|
|
||||||
|
export default function ChrisCamponovoPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Chris_Camponovo />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
19
src/app/people/florian_fournier/page.tsx
Normal 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_Florian_Fournier } from '@/components/people/People_Florian_Fournier'
|
||||||
|
|
||||||
|
export default function FlorianFournierPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Florian_Fournier />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
19
src/app/people/gregory_flipo/page.tsx
Normal 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_Gregory_Flipo } from '@/components/people/People_Gregory_Flipo'
|
||||||
|
|
||||||
|
export default function GregoryFlipoPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Gregory_Flipo />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
19
src/app/people/hugo_mathecowitsch/page.tsx
Normal 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_Hugo_Mathecowitsch } from '@/components/people/People_Hugo_Mathecowitsch'
|
||||||
|
|
||||||
|
export default function HugoMathecowitschPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Hugo_Mathecowitsch />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
19
src/app/people/jan_de_landtsheer/page.tsx
Normal 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_Jan_De_Landtsheer } from '@/components/people/People_Jan_De_Landtsheer'
|
||||||
|
|
||||||
|
export default function JanDeLandtsheerPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Jan_De_Landtsheer />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
19
src/app/people/karoline_zizka/page.tsx
Normal 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_Karoline_Zizka } from '@/components/people/People_Karoline_Zizka'
|
||||||
|
|
||||||
|
export default function KarolineZizkaPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Karoline_Zizka />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
19
src/app/people/kristof_de_spiegeleer/page.tsx
Normal 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_Kristof_de_Spiegeleer } from '@/components/people/People_Kristof_de_Spiegeleer'
|
||||||
|
|
||||||
|
export default function KristofdeSpiegeleerPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Kristof_de_Spiegeleer />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
19
src/app/people/marion_ravarino/page.tsx
Normal 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_Marion_Ravarino } from '@/components/people/People_Marion_Ravarino'
|
||||||
|
|
||||||
|
export default function MarionRavarinoPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Marion_Ravarino />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
19
src/app/people/michel_coucke/page.tsx
Normal 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_Michel_Coucke } from '@/components/people/People_Michel_Coucke'
|
||||||
|
|
||||||
|
export default function MichelCouckePage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Michel_Coucke />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
19
src/app/people/nousrath_bhugeloo/page.tsx
Normal 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_Nousrath_Bhugeloo } from '@/components/people/People_Nousrath_Bhugeloo'
|
||||||
|
|
||||||
|
export default function NousrathBhugelooPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Nousrath_Bhugeloo />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
19
src/app/people/owen_kemp/page.tsx
Normal 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_Owen_Kemp } from '@/components/people/People_Owen_Kemp'
|
||||||
|
|
||||||
|
export default function OwenKempPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Owen_Kemp />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
32
src/app/people/page.tsx
Normal 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 />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
19
src/app/people/sacha_obeegadoo/page.tsx
Normal 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_Sacha_Obeegadoo } from '@/components/people/People_Sacha_Obeegadoo'
|
||||||
|
|
||||||
|
export default function SachaObeegadooPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Sacha_Obeegadoo />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
19
src/app/people/sam_taggart/page.tsx
Normal 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_Sam_Taggart } from '@/components/people/People_Sam_Taggart'
|
||||||
|
|
||||||
|
export default function SamTaggartPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Sam_Taggart />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
19
src/app/people/sasha_astiadi/page.tsx
Normal 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_Sasha_Astiadi } from '@/components/people/People_Sasha_Astiadi'
|
||||||
|
|
||||||
|
export default function SashaAstiadiPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Sasha_Astiadi />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
19
src/app/people/timur_gordon/page.tsx
Normal 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_Timur_Gordon } from '@/components/people/People_Timur_Gordon'
|
||||||
|
|
||||||
|
export default function TimurGordonPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header_darkbg />
|
||||||
|
<main>
|
||||||
|
<People_Timur_Gordon />
|
||||||
|
<CallToAction />
|
||||||
|
<Faqs />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|