Skip to content

Commit

Permalink
Add content and styling
Browse files Browse the repository at this point in the history
  • Loading branch information
jonaschlegel committed Sep 30, 2024
1 parent c6b6eb0 commit 5c971da
Show file tree
Hide file tree
Showing 86 changed files with 3,642 additions and 186 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: CI
on: push

jobs:
ci:
name: CI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
cache: 'pnpm'
- run: pnpm install
- run: pnpm eslint . --max-warnings 0
- run: pnpm tsc
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
],
// Enable Next.js TypeScript plugin
// https://nextjs.org/docs/app/building-your-application/configuring/typescript#typescript-plugin
"typescript.tsdk": "node_modules/typescript/lib"
"typescript.tsdk": "node_modules/typescript/lib",
"cSpell.words": ["Jona"]
}
97 changes: 76 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,91 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
# Business Portfolio Website

## Getting Started
This repository contains the code for the business portfolio website [jonaschlegel.com](https://jonaschlegel.com), showcasing Jona Schlegel’s projects in archaeological science communication, illustration, and web development.

First, run the development server:
## Technologies Used

- **Framework**: [Next.js 13+](https://nextjs.org/)
- **Package Manager**: [pnpm](https://pnpm.io/)
- **Styling**: [Tailwind CSS](https://tailwindcss.com/)
- **TypeScript**: For type safety and improved developer experience.
- **ESLint**: [UpLeveled ESLint Config](https://github.com/upleveled/eslint-config-upleveled) for linting and code quality.

## Setup and Installation

To set up the project locally, follow these steps:

1. **Clone the repository**:

```bash
git clone https://github.com/<your-username>/business-portfolio.git
cd business-portfolio
```

2. **Install dependencies using pnpm**:

```bash
pnpm install
```

3. **Start the development server**:

```bash
pnpm dev
```

The site will be running locally at `http://localhost:3000`.

## Project Setup Command

The project was created using the following command:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
pnpm create next-app@canary . --app --no-turbo --no-src-dir --no-eslint --import-alias @/* --tailwind
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
This command configures the project with:

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
- No turbo pack.
- No `src` directory.
- No ESLint (configured separately with [UpLeveled ESLint](https://github.com/upleveled/eslint-config-upleveled)).
- Tailwind CSS for styling.
- TypeScript support.
- Import alias for simplified path imports (`@/`).

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Folder Structure

## Learn More
```plaintext
.
├── .vscode/ # VSCode settings and configurations
├── app/ # Next.js app directory
├── node_modules/ # Project dependencies
├── public/ # Public assets
├── .gitignore # Files to ignore in git
├── eslint.config.js # ESLint configuration (UpLeveled)
├── next-env.d.ts # TypeScript environment file
├── next.config.ts # Next.js configuration
├── package.json # Project dependencies and scripts
├── pnpm-lock.yaml # Lockfile for pnpm dependencies
├── postcss.config.mjs # PostCSS configuration
├── prettier.config.js # Prettier configuration
├── README.md # Project documentation
├── stylelint.config.js # Stylelint configuration
├── tailwind.config.ts # Tailwind CSS configuration
├── tsconfig.json # TypeScript configuration
```

## Linting and Formatting

To learn more about Next.js, take a look at the following resources:
The project uses the [UpLeveled ESLint config](https://github.com/upleveled/eslint-config-upleveled) for consistent code formatting and linting. You can run linting using the following command:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
```bash
pnpm lint
```

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deployment

## Deploy on Vercel
The website is deployed using [Vercel](https://vercel.com/). You can find the production site at [jonaschlegel.com](https://jonaschlegel.com).

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
## License

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
75 changes: 75 additions & 0 deletions app/Tracking.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import Script from 'next/script.js';

const googleAnalyticsTrackingId = 'G-6S9J34MPR3';

export default function Tracking() {
return (
<>
<Script
id="_next-ga-init"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag() {
window.dataLayer.push(arguments);
}
gtag('consent', 'default', {
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
analytics_storage: 'denied',
functionality_storage: 'denied',
personalization_storage: 'denied',
security_storage: 'granted',
wait_for_update: 2000,
});
gtag('set', 'ads_data_redaction', true);
gtag('set', 'url_passthrough', true);
gtag('js', new Date());
gtag('config', '${googleAnalyticsTrackingId}');
`,
}}
/>
<Script
id="_next-ga"
src={`https://www.googletagmanager.com/gtag/js?id=${googleAnalyticsTrackingId}`}
/>
<Script
id="fb-pixel"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
!function(f,b,e,v,n,t,s) {
if(f.fbq) return;
n = f.fbq = function() {
n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments);
};
if(!f._fbq) f._fbq = n;
n.push = n;
n.loaded = !0;
n.version = '2.0';
n.queue = [];
t = b.createElement(e);
t.async = !0;
t.src = v;
s = b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s);
}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', '812703364405783');
fbq('track', 'PageView');
`,
}}
/>
<Script
src="https://scripts.simpleanalyticscdn.com/latest.js"
strategy="afterInteractive"
defer
/>
<Script
id="cookieyes"
src="https://cdn-cookieyes.com/client_data/f49132772cc1d9f89dfe9534/script.js"
/>
</>
);
}
36 changes: 36 additions & 0 deletions app/components/Banner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import Image from 'next/image';
import type { FC } from 'react';
import { bannerData, jonaImage } from '../data/content';
import BannerText from './BannerText';
import ButtonSecondary from './ButtonSecondary';

const Banner: FC = () => {
return (
<div className="mb-8 bg-yellow-100 py-3 text-neutral-950">
<div className="container mx-auto px-4">
<div className="flex flex-wrap items-center justify-between gap-4 lg:gap-8">
<div className="flex-1">
{bannerData.map((bannerText) => (
<BannerText key={`banner-${bannerText}`}>{bannerText}</BannerText>
))}
<div className="mt-4 text-center">
<ButtonSecondary
className="bg-primary-accent font-semibold text-black border-none"
pdfUrl="/data/Portfolio_JonaSchlegel.pdf"
>
Download my Portfolio
</ButtonSecondary>
</div>
</div>
<div className="shrink-0">
<div className="relative aspect-square size-32 overflow-hidden rounded-full lg:size-48">
<Image src={jonaImage} alt="jona" fill className="object-cover" />
</div>
</div>
</div>
</div>
</div>
);
};

export default Banner;
11 changes: 11 additions & 0 deletions app/components/BannerText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { FC, ReactNode } from 'react';

const BannerText: FC<{ children: ReactNode }> = ({ children }) => {
return (
<p className="text-center font-merriweather text-xs tracking-tighter text-neutral-950 lg:text-xl">
{children}
</p>
);
};

export default BannerText;
81 changes: 81 additions & 0 deletions app/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
'use client';

import type { Route } from 'next';
import Link from 'next/link';
import type { ButtonHTMLAttributes, FC } from 'react';
import React from 'react';

export interface ButtonProps {
className?: string;
translate?: string;
sizeClass?: string;
fontSize?: string;
loading?: boolean;
disabled?: boolean;
type?: ButtonHTMLAttributes<HTMLButtonElement>['type'];
href?: Route;
onClick?: () => void;
children?: React.ReactNode;
}

const Button: FC<ButtonProps> = ({
className = 'text-neutral-200 disabled:cursor-not-allowed',
translate = '',
sizeClass = 'py-3 px-4 mt-5 mb-5 sm:py-2 sm:px-6',
fontSize = 'text-sm sm:text-base font-medium',
disabled = false,
href,
children,
type,
loading,
onClick = () => {},
}) => {
const CLASSES = `relative inline-flex items-center justify-center rounded-full transition-colors ${fontSize} ${sizeClass} ${translate} ${className}`;

const renderLoading = () => {
return (
<svg
className="-ml-1 mr-3 size-5 animate-spin"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="3"
/>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
);
};

if (href) {
return (
<Link href={href} className={CLASSES} onClick={onClick}>
{children || `This is Link`}
</Link>
);
}

return (
<button
disabled={disabled || loading}
className={CLASSES}
onClick={onClick}
type={type}
>
{loading && renderLoading()}
{children || `This is Button`}
</button>
);
};

export default Button;
45 changes: 45 additions & 0 deletions app/components/ButtonPrimary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use client';

import React from 'react';
import type { ButtonProps } from './Button';
import Button from './Button';

declare global {
interface Window {
Calendly: {
initPopupWidget: (options: { url: string }) => void;
};
}
}

export interface ButtonPrimaryProps extends ButtonProps {
email?: string;
calendlyEventSlug?: string;
}

const ButtonPrimary: React.FC<ButtonPrimaryProps> = ({
className = '',
email,
calendlyEventSlug,
...args
}) => {
const handleClick = () => {
if (email) {
window.location.href = `mailto:${email}`;
} else if (calendlyEventSlug) {
window.Calendly.initPopupWidget({
url: `https://calendly.com/${calendlyEventSlug}?primary_color=ff3367`,
});
}
};

return (
<Button
className={`rounded-full bg-primary-accent font-semibold text-black ${className}`}
{...args}
onClick={handleClick}
/>
);
};

export default ButtonPrimary;
Loading

0 comments on commit 5c971da

Please sign in to comment.