A modern, responsive photography portfolio web application built with React 19, TypeScript, and Firebase. Featuring dynamic album management, image uploads via Backblaze B2, comprehensive admin controls, and a beautiful user interface.
- Dynamic Portfolio: Showcase photography albums with public/private visibility control
- Public Albums Page: Browse all public albums with hero images and image counts
- Responsive Design: Mobile-first UI built with Tailwind CSS 4 and shadcn/ui components
- Dark Mode: System-aware theme with manual toggle
- Active Page Indication: Clear navigation with highlighted active pages
- Complete Album Management: Create, rename, delete albums, and toggle visibility
- Image Upload System: Multi-image upload with progress tracking and thumbnail generation
- Hero Image Selection: Set custom hero images for albums and homepage
- Content Management:
- Edit homepage hero title and subtitle
- Manage contact information (email, phone, location, Instagram)
- Update pricing packages with descriptions and prices
- QR Code Generation: Share albums easily with generated QR codes
- Bulk Operations: Download and delete multiple images at once
- Secure Authentication: Firebase Auth with protected admin routes
- Firestore Integration: Real-time data sync for albums and images
- Backblaze B2 Storage: Scalable cloud storage with Cloudflare Worker proxy
- Image Optimization: Automatic thumbnail generation for fast loading
- Full-Screen Viewer: Browse images with navigation controls
- Error Handling: Graceful error states with retry functionality
- GPL-3.0 Copyleft: Free and open-source software
- Node.js 18+ or Bun
- Firebase project with Firestore and Authentication enabled
- Backblaze B2 account for image storage
- Cloudflare Workers account for image proxy
-
Clone the repository
git clone https://github.com/LensIllumination/Client-Website.git cd Client-Website -
Install dependencies
npm install # or with bun (recommended) bun install -
Configure Firebase
Create a
.env.localfile in the project root:VITE_FIREBASE_API_KEY=your_api_key VITE_FIREBASE_AUTH_DOMAIN=your_project.firebaseapp.com VITE_FIREBASE_PROJECT_ID=your_project_id VITE_FIREBASE_STORAGE_BUCKET=your_project.appspot.com VITE_FIREBASE_MESSAGING_SENDER_ID=your_sender_id VITE_FIREBASE_APP_ID=your_app_id
-
Set up Backblaze B2
- Create a B2 bucket
- Generate application keys
- Configure CORS settings to allow your domain
- Deploy the Cloudflare Worker (see
worker.jsandwrangler.toml)
-
Initialize Firestore
Create these collections and documents:
settings/ βββ hero (homepage content) βββ contact (contact information) βββ pricing (pricing packages) albums/ (created via admin dashboard) images/ (created via admin dashboard) -
Configure Firebase Authentication
- Enable Email/Password authentication
- Add admin user(s)
- Update Firestore security rules with admin UIDs
-
Start development server
npm run dev # or bun run devVisit
http://localhost:5173 -
Build for production
npm run build # or bun run build
βββ public/
β βββ 404.html # Custom 404 page
βββ src/
β βββ components/
β β βββ ui/ # shadcn/ui components (button, card, dialog, etc.)
β β βββ AdminFab.tsx # Floating admin button
β β βββ Footer.tsx # Site footer with navigation and auth
β β βββ GalleryImage.tsx # Image component with loading states
β β βββ ImageErrorPanel.tsx # Error handling for images
β β βββ Navbar.tsx # Navigation with active page highlighting
β β βββ ProtectedRoute.tsx # Auth guard for admin routes
β βββ hooks/
β β βββ useAuth.ts # Authentication hook
β βββ lib/
β β βββ LoadAlbum.ts # Album loading utilities
β β βββ utils.ts # Utility functions
β βββ AdminDashboard.tsx # Complete admin interface
β βββ AdminSignIn.tsx # Authentication page
β βββ AdminUploader.tsx # Image upload interface
β βββ AlbumView.tsx # Public album viewer
β βββ Contact.tsx # Contact page
β βββ Home.tsx # Homepage with hero
β βββ NotFound.tsx # 404 page
β βββ Pricing.tsx # Pricing packages page
β βββ PublicAlbums.tsx # Public albums gallery
β βββ firebase.ts # Firebase configuration
β βββ index.css # Global styles
β βββ main.tsx # App entry point with routing
βββ worker.js # Cloudflare Worker for B2 proxy
βββ wrangler.toml # Cloudflare Worker config
βββ vite.config.ts # Vite configuration
βββ tailwind.config.ts # Tailwind CSS configuration
βββ tsconfig.json # TypeScript configuration
βββ vercel.json # Vercel deployment config
- Frontend: React 19, TypeScript 5, Vite 6
- Styling: Tailwind CSS 4, shadcn/ui, Lucide React icons
- Database: Firebase Firestore (NoSQL)
- Authentication: Firebase Authentication
- Storage: Backblaze B2 with Cloudflare Workers proxy
- Routing: React Router v7
- Image Processing: Browser-based thumbnail generation
- QR Codes: qrcode.react
- Deployment: Vercel (frontend) + Cloudflare Workers (proxy)
- Build Tool: Vite with SWC compiler
- Package Manager: npm or Bun
- Create new albums with custom names
- Rename existing albums
- Delete albums (with confirmation)
- Toggle public/private visibility
- Set hero images for albums
- View album statistics (image count, creation date)
- Bulk album operations
- Multi-Image Upload: Upload multiple images simultaneously with drag-and-drop
- Progress Tracking: Real-time upload progress for each image
- Automatic Thumbnails: Browser-based thumbnail generation for faster loading
- Image Metadata: Track file names, upload dates, and storage paths
- Bulk Delete: Select and delete multiple images at once
- Download Options: Download original or thumbnail versions
- Full-Screen Viewer: Navigate through album images with keyboard shortcuts
- Error Handling: Graceful handling of upload failures with retry options
-
Homepage Hero:
- Edit hero title and subtitle
- Upload custom hero background image
- Preview changes before saving
-
Contact Information:
- Email address
- Phone number
- Physical location
- Instagram handle
-
Pricing Packages:
- Add/edit/remove pricing tiers
- Set package name, description, and price
- Reorder packages
- Protected Routes: Secure admin-only areas with authentication
- Admin FAB: Floating action button for quick admin access
- Active Page Highlighting: Clear indication of current page in navigation
- Mobile Responsive: Full admin functionality on all devices
- Dark Mode Support: Complete theme consistency across admin interface
Configure your Firestore security rules to allow public reads and admin-only writes:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Helper function to check if user is admin
function isAdmin() {
return request.auth != null &&
request.auth.uid in [
"ADMIN_UID_1", // Replace with actual admin UIDs
"ADMIN_UID_2"
];
}
// Allow public reads for all documents
match /{document=**} {
allow read: if true;
}
// Settings collection (hero, contact, pricing)
match /settings/{docId} {
allow write: if isAdmin();
}
// Images collection
match /images/{imageId} {
allow create, update, delete: if isAdmin();
}
// Albums collection
match /albums/{albumId} {
allow create, update, delete: if isAdmin();
}
}
}Important: Replace ADMIN_UID_1 and ADMIN_UID_2 with your actual Firebase user UIDs. You can find these in the Firebase Console under Authentication > Users.
# Development
npm run dev # Start Vite dev server (http://localhost:5173)
bun run dev # Start with Bun runtime
# Production
npm run build # TypeScript check + production build
bun run build # Build with Bun
npm run preview # Preview production build locally
# Code Quality
npm run lint # Run ESLint on all TypeScript files
# Deployment (if using gh-pages)
npm run predeploy # Build before deploying
npm run deploy # Deploy to GitHub Pages-
Push to GitHub
git push origin main
-
Import in Vercel
- Go to vercel.com
- Import your GitHub repository
- Vercel will auto-detect the Vite configuration
-
Configure Environment Variables
Add these in Vercel project settings:
VITE_FIREBASE_API_KEY VITE_FIREBASE_AUTH_DOMAIN VITE_FIREBASE_PROJECT_ID VITE_FIREBASE_STORAGE_BUCKET VITE_FIREBASE_MESSAGING_SENDER_ID VITE_FIREBASE_APP_ID -
Deploy
- Vercel will automatically deploy on push
- Custom domain configuration available in settings
The included worker.js proxies requests to Backblaze B2:
-
Install Wrangler
npm install -g wrangler
-
Configure
wrangler.tomlname = "b2-proxy" main = "worker.js" compatibility_date = "2024-01-01" [vars] B2_ENDPOINT = "https://your-bucket.s3.us-west-001.backblazeb2.com"
-
Add Secrets
wrangler secret put B2_ACCESS_KEY_ID wrangler secret put B2_SECRET_ACCESS_KEY
-
Deploy
wrangler deploy
For other hosting providers:
npm run buildDeploy the dist/ folder to your static hosting service (Netlify, GitHub Pages, Firebase Hosting, etc.).
- Hero section with customizable title, subtitle, and background image
- Call-to-action buttons for Albums and Contact
- Responsive design with mobile-optimized layout
- Grid display of all public albums
- Hero images with image counts
- Click to view full album
- Loading states and empty states
- Full album gallery with responsive grid
- Click to view images in fullscreen
- Keyboard navigation (β β) in fullscreen viewer
- Download options for individual images
- Dynamic pricing packages loaded from Firestore
- Card-based layout with hover effects
- Direct contact button for inquiries
- Contact information cards (Email, Phone, Location, Instagram)
- Direct action buttons (email, call, maps, follow)
- Admin edit button for authenticated users
-
Album Management Section:
- Create/rename/delete albums
- Toggle public/private visibility
- Set hero images
- Navigate to album details
-
Hero Content Section:
- Edit homepage hero title and subtitle
- Upload and preview hero background image
-
Contact Management Section:
- Update email, phone, location, Instagram
-
Pricing Management Section:
- Add/edit/delete pricing packages
- Drag to reorder (if implemented)
- Drag-and-drop image upload
- Multi-file selection
- Real-time upload progress
- Image thumbnails with delete options
- Set album hero image
- Download images individually or in bulk
- Email/password authentication
- Error handling and validation
- Redirect after successful login
- Protected Routes: Admin pages require authentication
- Firebase Auth: Secure email/password authentication
- Firestore Rules: Database-level security with admin-only writes
- Environment Variables: Sensitive credentials stored securely
- CORS Configuration: Proper CORS setup for B2 storage
- No API Keys in Code: All sensitive data in environment variables
# Install dependencies
bun install
# Start dev server with hot reload
bun run dev- Create component in
src/ - Add route in
src/main.tsx - Update navigation in
src/components/Navbar.tsx - Update navigation in
src/components/Footer.tsx
# shadcn/ui components can be added with:
npx shadcn@latest add [component-name]Never commit .env.local to version control. Add it to .gitignore.
The project uses strict TypeScript. Run npm run build to check for type errors.
This project is licensed under the GNU General Public License v3.0 - see LICENSE file for details.
This is a copyleft license - any modifications or derivative works must also be distributed under GPL-3.0.
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Please ensure your code follows the existing style and passes all type checks.
For inquiries, visit the Contact Page or email contect@lensillumination.ca
Jacob Orr
- GitHub: @Jquob
- Website: lensillumination.ca
Live Site: lensillumination.ca
Repository: LensIllumination/Client-Website
License: GPL-3.0-only