Skip to content

LevinMedia/portfolio

Repository files navigation

🎨 Levin Media Portfolio

Built with Next.js, Supabase, and a whole lot of coffee. Deployed on Vercel. This is my personal portfolio site, but I've open sourced it so anyone can check it out or tinker! πŸŽ‰ PRs welcome :D

✨ Features

  • 🎭 Theme System - Light/Dark mode with custom color preferences
  • πŸ“ Guestbook - MySpace-style guestbook with Milkdown WYSIWYG editor
  • πŸ–ΌοΈ Image Uploads - Drag & drop images with Supabase storage
  • πŸ”’ Admin Panel - Secure admin interface for content management
  • πŸ“Š Work History - Dynamic work history with company logos
  • 🎨 Design System - Custom component library with Storybook
  • πŸ“± Responsive - Mobile-first design with beautiful grid backgrounds

πŸš€ Quick Start

Prerequisites

  • Node.js 18+ and npm
  • A Supabase account (free tier works great!)

1. Clone the Repository

git clone https://github.com/LevinMedia/portfolio.git
cd portfolio
npm install

2. Set Up Supabase

  1. Create a free account at supabase.com
  2. Create a new project
  3. Go to Project Settings β†’ API
  4. Copy your project URL and keys

3. Create Environment Variables

Create a .env.local file in the root directory:

# Supabase Configuration
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key

# Auth cookie for private featured works (min 16 chars). Required for private-access users to see private selected works.
AUTH_SECRET=your_secret_at_least_16_characters

⚠️ Important: Never commit .env.local to git! It's already in .gitignore.

4. Set Up Database

  1. In your Supabase project, go to SQL Editor
  2. Copy the contents of db/prod_functions.sql
  3. Paste and run the SQL script

This creates all necessary tables, functions, and security policies:

  • admin_users - Admin authentication
  • setup_state - One-time setup tracking
  • howdy_content - Homepage content
  • work_companies & work_positions - Work history
  • guestbook_entries - Guestbook messages

5. Set Up Storage

  1. In Supabase, go to Storage
  2. Create a new bucket called media
  3. Set it to Public (for serving images)

6. Run the Development Server

npm run dev

Open http://localhost:3000 and you're ready! πŸŽ‰

7. Initial Admin Setup

On first run, visit /admin - you'll be redirected to a one-time secure setup page where you create your admin credentials.

πŸ“ Project Structure

β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ app/
β”‚   β”‚   β”œβ”€β”€ admin/           # Admin dashboard & content management
β”‚   β”‚   β”œβ”€β”€ api/             # API routes for data & auth
β”‚   β”‚   β”œβ”€β”€ components/      # Reusable React components
β”‚   β”‚   └── site-settings/   # Theme & design system controls
β”‚   └── stories/             # Storybook component documentation
β”œβ”€β”€ db/
β”‚   └── prod_functions.sql   # Database schema & functions
└── public/                  # Static assets (images, logos)

🎨 Key Technologies

πŸ› οΈ Available Scripts

npm run dev          # Start development server
npm run build        # Build for production
npm run start        # Start production server
npm run lint         # Run ESLint
npm run storybook    # Start Storybook
npm run build-storybook  # Build Storybook

🎭 Theme Customization

The site uses CSS variables for theming. Edit src/app/globals.css to customize:

:root {
  --primary: #C614E1;      /* Brand primary */
  --secondary: #ec4899;    /* Secondary accent */
  --accent: #0891b2;       /* Tertiary accent */
  /* ... more theme variables */
}

Access theme controls at /site-settings (linked from the Howdy component).

πŸ–ΌοΈ Image Management

Images are stored in Supabase Storage under the media bucket:

  • images/ - General images
  • company-logos/ - Work history logos
  • guestbook/ - Guestbook uploads

The image uploader includes cropping functionality powered by react-easy-crop.

πŸ” Admin Features

The admin panel (/admin) includes:

  • Howdy Content - Edit homepage greeting
  • Work History - Manage companies and positions
  • Guestbook - Moderate messages (approve/reject/delete)
  • Site Settings - Theme and design system controls

πŸ“ Guestbook

The guestbook features:

  • Rich text editing with Milkdown (slash commands, formatting)
  • Image uploads with drag & drop
  • Social links (LinkedIn, Threads, Twitter, Instagram)
  • Admin moderation system
  • Markdown rendering with GFM support

πŸ§ͺ Development Tips

Running Storybook

npm run storybook

View components in isolation at http://localhost:6006

Database Migrations

When updating the database schema, edit db/prod_functions.sql and run it in the Supabase SQL Editor.

Pro tip: The script uses DROP POLICY IF EXISTS and CREATE OR REPLACE FUNCTION for safe re-runs.

Environment Variables

For production deployment (Vercel, etc.), set these environment variables in your hosting platform:

  • NEXT_PUBLIC_SUPABASE_URL
  • NEXT_PUBLIC_SUPABASE_ANON_KEY
  • SUPABASE_SERVICE_ROLE_KEY

🀝 Contributing

PRs are welcome! πŸŽ‰

Whether it's:

  • πŸ› Bug fixes
  • ✨ New features
  • πŸ“ Documentation improvements
  • 🎨 Design enhancements

Feel free to fork, experiment, and submit a pull request!

Development Workflow

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

πŸ“„ License

This project is licensed under the GNU General Public License v2.0 or later.

See the LICENSE file for details.

TL;DR: You're free to use, modify, and distribute this code. If you distribute modified versions, you must also make your source code available under the GPL v2.

πŸ’¬ Questions?

Feel free to open an issue if you have questions or run into problems!

About

Resources

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors