Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
4c43860
feat: major site refactor with CMS, galleries, and featured work impr…
leanderrj Jan 18, 2026
8f7a216
docs: update README with current tech stack and features
leanderrj Jan 18, 2026
c8d3774
fix: UI improvements based on feedback
leanderrj Jan 19, 2026
4d5826a
fix: logo visibility and prevent selection during flower drag
leanderrj Jan 19, 2026
cc1cbb4
fix: about page background slideshow, mobile logo, smaller flowers
leanderrj Jan 19, 2026
05eb7b3
fix: normalize tags to handle comma-separated strings
leanderrj Jan 19, 2026
1831973
fix: add Netlify redirects for /admin CMS route
leanderrj Jan 19, 2026
c60560f
Merge pull request #17 from leviszaboo/main
leviszaboo Jan 22, 2026
9ebed14
Merge pull request #16 from leanderrj/main
leviszaboo Jan 22, 2026
84b987a
add auth base_url to cms config
leviszaboo Jan 22, 2026
6e6990e
Update admin/config.yml
leviszaboo Jan 22, 2026
bcb95bc
Update config.yml
leviszaboo Jan 22, 2026
20d2e3f
fix: smoother animations, fonts, colors, remove unused stuff
leviszaboo Jan 22, 2026
4beed87
fix: mobile menu, menu button
leviszaboo Jan 28, 2026
2d69c3f
refactor: typescript + project cleanup
leviszaboo Jan 28, 2026
85b026d
fix: final ts conversions
leviszaboo Jan 28, 2026
45482a9
fix: build issue
leviszaboo Jan 28, 2026
3042305
Update Galleries “alpha-x-junya-watanabe”
leanderrj Feb 5, 2026
41953f7
Update Galleries “alpha-x-junya-watanabe”
leanderrj Feb 5, 2026
703b5a8
Update Galleries “alpha-x-junya-watanabe”
leanderrj Feb 5, 2026
c7fc0af
Update Galleries “alpha-x-junya-watanabe”
leanderrj Feb 5, 2026
6b7ee62
Update Pages “about”
leanderrj Feb 5, 2026
b967104
feat: add PagesCMS configuration
leanderrj Feb 13, 2026
5abf003
feat: configure static export for Cloudflare Pages
leanderrj Feb 13, 2026
9a5ec18
feat: configure Next.js for Cloudflare Pages SSR
leanderrj Feb 13, 2026
ea8e0cf
feat: add Wrangler config for Cloudflare Workers
leanderrj Feb 13, 2026
768592f
feat: add OpenNext Cloudflare config
leanderrj Feb 13, 2026
52248ed
feat: add Cloudflare deps (@opennextjs/cloudflare, wrangler)
leanderrj Feb 13, 2026
b6bdd18
chore: add Cloudflare build artifacts to gitignore
leanderrj Feb 13, 2026
0f27716
Merge pull request #1 from leme-leme/feat/pagescms
leanderrj Feb 13, 2026
5f156e3
fix: bump next to ~15.2.9 for @opennextjs/cloudflare compat
leanderrj Feb 13, 2026
acb4ad6
fix: remove stale package-lock.json for Cloudflare Pages build
leanderrj Feb 13, 2026
432754c
chore: rename wrangler config
leanderrj Feb 13, 2026
9fb7703
fix: wrangler.json with pages_build_output_dir for CF Pages SSR
leanderrj Feb 13, 2026
754b538
fix: remove reserved ASSETS binding, simplify wrangler for Pages
leanderrj Feb 13, 2026
aab8e8d
chore: switch to wrangler.jsonc per OpenNext docs
leanderrj Feb 13, 2026
e866caf
fix: restore wrangler.jsonc per OpenNext docs (worker + assets)
leanderrj Feb 13, 2026
a26e28d
chore: switch to wrangler.toml for Pages compat
leanderrj Feb 13, 2026
3e181a0
fix: wrangler.toml with pages_build_output_dir (no ASSETS binding)
leanderrj Feb 13, 2026
b0d06ea
fix: Pages-only wrangler.toml (no main/assets, only pages_build_outpu…
leanderrj Feb 13, 2026
f47d9a7
chore: remove Pages wrangler.toml, use Worker wrangler.jsonc
leanderrj Feb 13, 2026
ce94fb1
fix: restore Worker wrangler.jsonc for OpenNext deploy
leanderrj Feb 13, 2026
edfca6a
ci: add GitHub Actions deploy to Cloudflare Workers
leanderrj Feb 13, 2026
e069d88
fix: move env vars to job level for wrangler auth
leanderrj Feb 13, 2026
4f0e633
ci: add workflow_dispatch trigger
leanderrj Feb 13, 2026
f85f128
fix: replace fs-based content loading with static JSON imports
Feb 14, 2026
3779229
fix: add route pattern for luigi.leme.work custom domain
Feb 14, 2026
869efea
fix: set assets not_found_handling to none for SSR fallback
Feb 14, 2026
7424902
refactor: reorganize CMS content by page with semantic frontend matching
claude Feb 14, 2026
1ed86a7
chore: add package-lock.json
claude Feb 14, 2026
f3d6465
feat: normalize Featured Work with portfolio grid, Work selects, and …
claude Feb 14, 2026
58c729b
fix: merge conflicts
leviszaboo Feb 14, 2026
20506b1
fix: lint issues
leviszaboo Feb 14, 2026
22bdb60
Update Galleries “la-cassette-runway”
Luigisimiani Apr 21, 2026
63dbbc7
Update Galleries “la-cassette-runway”
Luigisimiani Apr 21, 2026
10b2152
Update Contact “contact-info”
Luigisimiani Apr 21, 2026
ec1ac47
Update Galleries “la-cassette-runway”
Luigisimiani Apr 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Email Service (Resend)
RESEND_API_KEY=re_your_resend_api_key
USER_EMAIL=your@email.com
10 changes: 9 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
{
"extends": "next/core-web-vitals"
"extends": ["next/core-web-vitals", "next/typescript"],
"rules": {
"@typescript-eslint/no-unused-vars": [
"warn",
{ "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }
],
"@typescript-eslint/no-empty-object-type": "off"
},
"ignorePatterns": [".next/", "node_modules/", "public/"]
}
30 changes: 30 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: CI

on:
push:
branches: [main, master]
pull_request:
branches: [main, master]

jobs:
lint-and-build:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Build
run: npm run build
1 change: 0 additions & 1 deletion .husky/pre-commit

This file was deleted.

149 changes: 149 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Luigi Simiani Photography Portfolio

## Project Overview

Next.js 16 photography portfolio with React 19, TypeScript, Framer Motion animations, and Decap CMS for content management.

## Tech Stack

- **Framework**: Next.js 16 (App Router, Turbopack)
- **Language**: TypeScript (strict mode)
- **Styling**: Tailwind CSS + custom CSS
- **Animation**: Framer Motion 12
- **CMS**: Decap CMS (content in `/content/`)
- **UI Components**: shadcn/ui + 8-bit themed components

## Project Structure

```
├── app/
│ ├── (routes)/ # Page routes
│ │ ├── page.tsx # Home
│ │ ├── about-me/
│ │ ├── contact/
│ │ └── gallery/[id]/
│ ├── components/ # App-specific components
│ │ ├── about-me/
│ │ ├── contact/
│ │ ├── gallery/
│ │ ├── header/
│ │ ├── home/
│ │ ├── menu/
│ │ └── work/
│ ├── context/ # React Context (UIContext)
│ ├── actions/ # Server actions
│ ├── types/ # TypeScript types
│ └── styles/ # Global CSS
├── components/ui/ # Reusable UI primitives (shadcn)
├── lib/ # Utilities
│ ├── animations.ts # All Framer Motion configs
│ ├── constants.ts # App constants
│ ├── content.ts # CMS content fetching
│ ├── getBase64.ts # Image blur placeholders
│ └── utils.ts # cn() utility
├── content/ # CMS JSON content
│ ├── gallery/ # Gallery data
│ └── settings/ # Site settings
└── public/ # Static assets
```

## File Naming Conventions

| Type | Convention | Example |
|------|------------|---------|
| React components | PascalCase `.tsx` | `Header.tsx` |
| Utilities | camelCase `.ts` | `utils.ts` |
| Constants | camelCase `.ts` | `constants.ts` |
| Folders | kebab-case | `about-me/` |
| Route pages | `page.tsx` | `app/(routes)/gallery/page.tsx` |

## Import Order

1. React/Next.js (`react`, `next/*`)
2. External libraries (`framer-motion`, `rooks`, etc.)
3. `@/lib/*` utilities
4. `@/app/components/*`
5. `@/components/ui/*`
6. Relative imports (`./`, `../`)

## Component Patterns

### Props Interface
```tsx
interface ButtonProps {
text: string;
onClick?: () => void;
}

export default function Button({ text, onClick }: ButtonProps) {
// ...
}
```

### Conditional Classes
```tsx
import { cn } from "@/lib/utils";

<div className={cn("base-class", isActive && "active-class")} />
```

### Animations
Import from centralized animations file:
```tsx
import { fadeIn, carouselAnimationProps } from "@/lib/animations";

<motion.div {...fadeIn}>
```

### Context Usage
```tsx
import { useUI } from "@/app/context/UIContext";

const { isMenuVisible, toggleMenu } = useUI();
```

## Adding Content

### New Gallery
1. Add JSON file to `/content/gallery/[id].json`
2. Include required fields: `id`, `title`, `imageUrls`
3. Optional: `subTitle`, `description`, `coverPhoto`, `tags`

### Gallery JSON Schema
```json
{
"id": "gallery-slug",
"title": "Gallery Title",
"subTitle": "Optional subtitle",
"description": "Optional description",
"coverPhoto": "/images/galleries/cover.jpg",
"imageUrls": ["/images/galleries/img1.jpg"],
"tags": ["featured", "fashion"]
}
```

## Key Files

| File | Purpose |
|------|---------|
| `app/context/UIContext.tsx` | Global UI state (menu visibility) |
| `lib/animations.ts` | All Framer Motion animation configs |
| `lib/content.ts` | CMS content fetching functions |
| `app/types/content.ts` | TypeScript types for content |

## Environment Variables

```bash
# .env.local
RESEND_API_KEY= # For contact form emails
GALLERY_PASSWORD= # For protected galleries
```

## Commands

```bash
npm run dev # Development server
npm run build # Production build
npm run start # Start production server
npm run lint # Run ESLint
```
98 changes: 96 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,100 @@
# luigisimiani.com

Portfolio website of Amsterdam based photographer Luigi Simiani. Built with Next.js, Firebase and ImageKit.

Portfolio website for Amsterdam-based photographer Luigi Simiani. Built with Next.js 16, React 19, and Tailwind CSS.

<img width="1432" height="718" alt="Screenshot 2025-11-23 at 12 46 06" src="https://github.com/user-attachments/assets/8b633bea-3dda-4484-b7e8-a018448b1a05" />

## Tech Stack

- **Framework**: Next.js 16 (App Router, Turbopack)
- **Runtime**: React 19
- **CMS**: Decap CMS (formerly Netlify CMS)
- **Styling**: Tailwind CSS
- **Animations**: Framer Motion 12
- **Language**: TypeScript (strict mode)

## Features

- **Password-protected galleries** with secret flower bypass (5 clicks)
- **Downloadable galleries** - client-side ZIP export
- **Hero slideshow** with gallery references
- **Featured work section** with 2-column grid, quotes, and full-width hero items
- **Gallery tags** for layout control and utility functions
- **8-bit themed UI** components for contact form

## Project Structure

```
├── app/
│ ├── (routes)/ # Page routes
│ ├── components/ # App-specific components
│ │ ├── about-me/ # About page components
│ │ ├── contact/ # Contact form components
│ │ ├── gallery/ # Gallery view components
│ │ ├── header/ # Site header
│ │ ├── home/ # Homepage components
│ │ ├── menu/ # Navigation menu
│ │ └── work/ # Gallery/carousel components
│ ├── context/ # React Context
│ └── types/ # TypeScript types
├── components/ui/ # Reusable UI primitives (shadcn)
├── lib/ # Utilities & animations
├── content/ # CMS JSON content
└── public/ # Static assets
```

## Gallery Tags

| Tag | Effect |
|-----|--------|
| `hero` | Full-width in Featured Work |
| `hidden` | Excluded from gallery listings |
| `downloadable` | Adds ZIP download button |
| `protect` | Disables flower bypass on password page |

## Content Structure

```
content/
├── gallery/ # Gallery JSON files
├── bio.json # About page content
├── contact.json # Contact info
├── featured.json # Hero slideshow galleries
├── quotes.json # Featured work quotes
└── settings.json # Homepage settings
```

## Development

```bash
# Install dependencies
npm install

# Run development server
npm run dev

# Production build
npm run build

# Run CMS locally
npx decap-server
```

Visit `http://localhost:3000` for the site and `http://localhost:3000/admin` for the CMS.

## Environment Variables

Copy `.env.example` to `.env.local` and configure:

```
RESEND_API_KEY=your-resend-api-key
GALLERY_PASSWORD=your-gallery-password
```

## Documentation

See [CLAUDE.md](./CLAUDE.md) for detailed developer documentation including:
- File naming conventions
- Import order guidelines
- Component patterns
- How to add new content
29 changes: 19 additions & 10 deletions app/(routes)/about-me/page.jsx → app/(routes)/about-me/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import AboutMePage from "@/app/components/AboutMe/AboutMePage";
import Menu from "@/app/components/Menu/Menu/Menu";
import MemorizePosition from "@/app/components/Work/MemorizePosition";
import { fetchDocs } from "@/app/service/fetchDocs";
import { fetchFeaturedBlurDataUrls } from "@/app/utils/getBase64";
import AboutMePage from "@/app/components/about-me/AboutMePage";
import Menu from "@/app/components/menu/Menu";
import MemorizePosition from "@/app/components/work/MemorizePosition";
import { fetchDocs } from "@/lib/content";
import { fetchFeaturedBlurDataUrls } from "@/lib/getBase64";

export const revalidate = 0;
// Use ISR with 1 hour revalidation
export const revalidate = 3600;

export const metadata = {
title: "About Me",
Expand All @@ -18,11 +19,19 @@ export const metadata = {
},
};

interface Paragraph {
value: string;
highlightWords?: string[];
}

interface Source {
url: string;
base64?: string;
}

export default async function AboutMe() {
const paragraphRef = `${process.env.NEXT_PUBLIC_USER_UID}/about-me/paragraphs`;
const sourcesRef = `${process.env.NEXT_PUBLIC_USER_UID}/featured/about-me`;
const paragraphs = await fetchDocs(paragraphRef);
const sources = await fetchDocs(sourcesRef);
const paragraphs = (await fetchDocs("about/bio")) as unknown as Paragraph[];
const sources = (await fetchDocs("about/film-strip")) as unknown as Source[];

const blurData = await fetchFeaturedBlurDataUrls(sources);

Expand Down
14 changes: 7 additions & 7 deletions app/(routes)/contact/page.jsx → app/(routes)/contact/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import ContactPage from "@/app/components/Contact/ContactPage";
import Menu from "@/app/components/Menu/Menu/Menu";
import MemorizePosition from "@/app/components/Work/MemorizePosition";
import { fetchDoc } from "@/app/service/fetchDocs";
import ContactPage from "@/app/components/contact/ContactPage";
import Menu from "@/app/components/menu/Menu";
import MemorizePosition from "@/app/components/work/MemorizePosition";
import { fetchDoc } from "@/lib/content";

export const revalidate = 0;
// Use ISR with 1 hour revalidation
export const revalidate = 3600;

export const metadata = {
title: "Contact",
Expand All @@ -18,8 +19,7 @@ export const metadata = {
};

export default async function Contact() {
const ref = `${process.env.NEXT_PUBLIC_USER_UID}/contact-info`;
const contactInfo = await fetchDoc(ref);
const contactInfo = await fetchDoc("contact");

return (
<>
Expand Down
7 changes: 0 additions & 7 deletions app/(routes)/digital/[project]/loading.jsx

This file was deleted.

Loading
Loading