From 7f48d48b80ab348a41a2e33c26ac8f421586944e Mon Sep 17 00:00:00 2001 From: tanyar09 Date: Fri, 14 Nov 2025 13:33:55 -0500 Subject: [PATCH] feat: Add comprehensive documentation for PunimTag Photo Viewer This commit introduces several new documentation files for the PunimTag Photo Viewer project, including an Executive Summary, Quick Start Guide, Complete Plan, and Architecture Overview. These documents provide a high-level overview, setup instructions, detailed project plans, and architectural diagrams to assist developers and decision-makers. The README has also been updated to include links to these new resources, ensuring easy navigation and access to essential information for users and contributors. --- docs/PHOTO_VIEWER_ARCHITECTURE.md | 780 ++++++++++ docs/PHOTO_VIEWER_EXECUTIVE_SUMMARY.md | 425 +++++ docs/PHOTO_VIEWER_PLAN.md | 1989 ++++++++++++++++++++++++ docs/PHOTO_VIEWER_QUICKSTART.md | 470 ++++++ docs/PHOTO_VIEWER_README.md | 384 +++++ 5 files changed, 4048 insertions(+) create mode 100644 docs/PHOTO_VIEWER_ARCHITECTURE.md create mode 100644 docs/PHOTO_VIEWER_EXECUTIVE_SUMMARY.md create mode 100644 docs/PHOTO_VIEWER_PLAN.md create mode 100644 docs/PHOTO_VIEWER_QUICKSTART.md create mode 100644 docs/PHOTO_VIEWER_README.md diff --git a/docs/PHOTO_VIEWER_ARCHITECTURE.md b/docs/PHOTO_VIEWER_ARCHITECTURE.md new file mode 100644 index 0000000..e1e7b92 --- /dev/null +++ b/docs/PHOTO_VIEWER_ARCHITECTURE.md @@ -0,0 +1,780 @@ +# PunimTag Photo Viewer - Architecture Overview + +## System Architecture Diagram + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ EXISTING PUNIMTAG SYSTEM │ +│ ┌────────────────────┐ ┌─────────────────┐ │ +│ │ Desktop App │ │ Web Admin │ │ +│ │ (Tkinter) │ │ Interface │ │ +│ │ │ │ (React+FastAPI)│ │ +│ └──────────┬─────────┘ └────────┬────────┘ │ +│ │ │ │ +│ │ ┌──────────────────┘ │ +│ │ │ │ +│ ▼ ▼ │ +│ ┌──────────────────────────────────┐ │ +│ │ PostgreSQL Database │ │ +│ │ ┌────────────────────────────┐ │ │ +│ │ │ Tables: │ │ │ +│ │ │ • photos │ │ │ +│ │ │ • faces │ │ │ +│ │ │ • people │ │ │ +│ │ │ • person_encodings │ │ │ +│ │ │ • tags │ │ │ +│ │ │ • phototaglinkage │ │ │ +│ │ │ • photo_favorites │ │ │ +│ │ └────────────────────────────┘ │ │ +│ └──────────────────┬─────────────────┘ │ +└─────────────────────┼────────────────────────────────────────────────────┘ + │ + │ READ-ONLY ACCESS + │ +┌─────────────────────▼──────────────────────────────────────────────────┐ +│ NEW PHOTO VIEWER WEBSITE │ +│ │ +│ ┌────────────────────────────────────────────────────────────────┐ │ +│ │ User's Web Browser │ │ +│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ +│ │ │ Desktop │ │ Tablet │ │ Mobile │ │ │ +│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ +│ └──────────────────────────┬─────────────────────────────────────┘ │ +│ │ HTTPS │ +│ ┌──────────────────────────▼─────────────────────────────────────┐ │ +│ │ Next.js 14 Frontend │ │ +│ │ ┌────────────────────────────────────────────────────────┐ │ │ +│ │ │ React Server Components (App Router) │ │ │ +│ │ │ • Photo Grid (Infinite Scroll) │ │ │ +│ │ │ • Search & Filters │ │ │ +│ │ │ • Lightbox Viewer │ │ │ +│ │ │ • People & Tags Browsers │ │ │ +│ │ └────────────────────────────────────────────────────────┘ │ │ +│ │ ┌────────────────────────────────────────────────────────┐ │ │ +│ │ │ Client Components (Interactive) │ │ │ +│ │ │ • TanStack Query (State Management) │ │ │ +│ │ │ • Framer Motion (Animations) │ │ │ +│ │ │ • React Photo Album (Grid Layout) │ │ │ +│ │ └────────────────────────────────────────────────────────┘ │ │ +│ └──────────────────────────┬─────────────────────────────────────┘ │ +│ │ │ +│ ┌──────────────────────────▼─────────────────────────────────────┐ │ +│ │ Next.js API Routes (Optional) │ │ +│ │ • /api/photos - List and search photos │ │ +│ │ • /api/photos/[id] - Get photo details │ │ +│ │ • /api/people - List people with photo counts │ │ +│ │ • /api/tags - List tags with photo counts │ │ +│ │ • /api/search - Combined search │ │ +│ └──────────────────────────┬─────────────────────────────────────┘ │ +│ │ │ +│ ┌──────────────────────────▼─────────────────────────────────────┐ │ +│ │ Prisma ORM Client │ │ +│ │ • Type-safe database queries │ │ +│ │ • Read-only operations │ │ +│ │ • Connection pooling │ │ +│ └──────────────────────────┬─────────────────────────────────────┘ │ +│ │ │ +│ ┌──────────────────────────▼─────────────────────────────────────┐ │ +│ │ PostgreSQL (Read-Only User) │ │ +│ │ Username: viewer_readonly │ │ +│ │ Permissions: SELECT only on all tables │ │ +│ └─────────────────────────────────────────────────────────────────┘ │ +│ │ +└──────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Data Flow Diagram + +### Photo Gallery Loading + +``` +User Opens Browser + │ + ▼ +┌─────────────────┐ +│ Next.js Server │ +│ (App Router) │ +└────────┬────────┘ + │ + │ 1. Server Component Renders + │ await prisma.photo.findMany() + │ + ▼ +┌─────────────────┐ +│ Prisma Client │ +└────────┬────────┘ + │ + │ 2. SQL Query (Read-Only) + │ SELECT * FROM photos + │ WHERE processed = true + │ ORDER BY date_taken DESC + │ LIMIT 30 + │ + ▼ +┌─────────────────┐ +│ PostgreSQL │ +│ Database │ +└────────┬────────┘ + │ + │ 3. Return Photo Records + │ [{id, path, filename, date_taken, ...}, ...] + │ + ▼ +┌─────────────────┐ +│ Next.js Server │ +│ (Pre-renders) │ +└────────┬────────┘ + │ + │ 4. Generate HTML with Photo Data + │ + ▼ +┌─────────────────┐ +│ User Browser │ +│ (Receives HTML)│ +└────────┬────────┘ + │ + │ 5. Hydrate React Components + │ + ▼ +┌─────────────────┐ +│ Photo Grid │ +│ (Rendered) │ +└────────┬────────┘ + │ + │ 6. Load Images (Lazy) + │ + │ + ▼ +┌─────────────────┐ +│ Next.js Image │ +│ Optimization │ +└────────┬────────┘ + │ + │ 7. Serve Optimized Images + │ (WebP, responsive sizes) + │ + ▼ +┌─────────────────┐ +│ User Sees Photos│ +└─────────────────┘ +``` + +### Search Flow + +``` +User Types in Search + │ + ▼ +┌─────────────────┐ +│ SearchBar │ +│ (Debounced) │ +└────────┬────────┘ + │ 300ms delay + ▼ +┌─────────────────┐ +│ TanStack Query │ +│ (React Query) │ +└────────┬────────┘ + │ + │ Check cache first + │ If not cached: + │ + ▼ +┌─────────────────┐ +│ API Route │ +│ /api/search │ +└────────┬────────┘ + │ + │ Build Query + │ {people: [1,2], tags: [3], dateFrom: '2024-01-01'} + │ + ▼ +┌─────────────────┐ +│ Prisma Query │ +│ Builder │ +└────────┬────────┘ + │ + │ WHERE processed = true + │ AND faces.person_id IN (1,2) + │ AND photo_tags.tag_id IN (3) + │ AND date_taken >= '2024-01-01' + │ + ▼ +┌─────────────────┐ +│ PostgreSQL │ +│ (Indexed) │ +└────────┬────────┘ + │ + │ Return Matching Photos + │ + ▼ +┌─────────────────┐ +│ TanStack Query │ +│ (Cache Result) │ +└────────┬────────┘ + │ + │ staleTime: 5 min + │ cacheTime: 10 min + │ + ▼ +┌─────────────────┐ +│ Photo Grid │ +│ (Update UI) │ +└─────────────────┘ +``` + +--- + +## Component Hierarchy + +``` +App Layout +├── Header +│ ├── Logo +│ ├── Navigation +│ │ ├── Home Link +│ │ ├── People Link +│ │ ├── Tags Link +│ │ └── Timeline Link +│ ├── SearchButton +│ └── UserMenu +│ ├── Login/Logout +│ └── Settings +│ +├── Main Content (Page) +│ │ +│ ├── HomePage (/) +│ │ ├── Hero Section +│ │ ├── QuickFilters +│ │ └── PhotoGrid +│ │ ├── PhotoCard (x30) +│ │ │ ├── Image (Next.js) +│ │ │ ├── Overlay +│ │ │ └── Actions +│ │ └── InfiniteScroll Trigger +│ │ +│ ├── SearchPage (/search) +│ │ ├── SearchBar +│ │ ├── FilterPanel +│ │ │ ├── PeopleFilter +│ │ │ ├── DateRangePicker +│ │ │ └── TagFilter +│ │ └── SearchResults +│ │ └── PhotoGrid +│ │ +│ ├── PhotoDetailPage (/photo/[id]) +│ │ ├── Lightbox +│ │ │ ├── FullSizeImage +│ │ │ ├── NavigationButtons +│ │ │ └── CloseButton +│ │ └── MetadataPanel +│ │ ├── DateTaken +│ │ ├── PeopleList +│ │ ├── TagList +│ │ └── Actions +│ │ +│ ├── PeoplePage (/people) +│ │ ├── PeopleGrid +│ │ │ └── PersonCard (x many) +│ │ │ ├── RepresentativePhoto +│ │ │ ├── PersonName +│ │ │ └── PhotoCount +│ │ └── SearchBar +│ │ +│ └── TagsPage (/tags) +│ ├── TagCloud +│ │ └── TagBadge (x many) +│ │ ├── TagName +│ │ └── PhotoCount +│ └── SearchBar +│ +└── Footer + ├── Copyright + ├── Links + └── Version +``` + +--- + +## Technology Stack Layers + +``` +┌──────────────────────────────────────────────────────┐ +│ Presentation Layer │ +│ ┌────────────────────────────────────────────────┐ │ +│ │ React Server Components + Client Components │ │ +│ │ • shadcn/ui (UI Components) │ │ +│ │ • Tailwind CSS (Styling) │ │ +│ │ • Framer Motion (Animations) │ │ +│ │ • Lucide React (Icons) │ │ +│ └────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────┐ +│ Application Layer │ +│ ┌────────────────────────────────────────────────┐ │ +│ │ Next.js 14 (App Router) │ │ +│ │ • Server-Side Rendering │ │ +│ │ • API Routes │ │ +│ │ • Image Optimization │ │ +│ │ • Route Handlers │ │ +│ └────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────┐ +│ State Management Layer │ +│ ┌────────────────────────────────────────────────┐ │ +│ │ TanStack Query (React Query) │ │ +│ │ • Server State Caching │ │ +│ │ • Automatic Refetching │ │ +│ │ • Optimistic Updates │ │ +│ │ • Infinite Scroll │ │ +│ └────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────┐ +│ Data Access Layer │ +│ ┌────────────────────────────────────────────────┐ │ +│ │ Prisma ORM │ │ +│ │ • Type-Safe Queries │ │ +│ │ • Connection Pooling │ │ +│ │ • Query Builder │ │ +│ │ • Migration Management │ │ +│ └────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────┐ +│ Database Layer │ +│ ┌────────────────────────────────────────────────┐ │ +│ │ PostgreSQL │ │ +│ │ • Relational Database │ │ +│ │ • ACID Transactions │ │ +│ │ • Indices for Performance │ │ +│ │ • Read-Only User (viewer_readonly) │ │ +│ └────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────┘ +``` + +--- + +## Deployment Architecture + +### Option 1: Vercel (Recommended) + +``` +┌──────────────────────────────────────────────────────────┐ +│ Vercel Edge Network │ +│ ┌────────────────────────────────────────────────────┐ │ +│ │ Global CDN (Automatic) │ │ +│ │ • Caches static assets │ │ +│ │ │ - HTML, CSS, JS │ │ +│ │ │ - Optimized images │ │ +│ │ └─ Serves from nearest edge location │ │ +│ └────────────────────┬───────────────────────────────┘ │ +│ │ │ +│ ┌────────────────────▼───────────────────────────────┐ │ +│ │ Next.js Server (Serverless Functions) │ │ +│ │ • API Routes │ │ +│ │ │ - /api/photos │ │ +│ │ │ - /api/search │ │ +│ │ │ - /api/people │ │ +│ │ └─ Server Components rendering │ │ +│ └────────────────────┬───────────────────────────────┘ │ +└────────────────────────┼──────────────────────────────────┘ + │ + │ Database Connection + │ (Secure, encrypted) + │ +┌────────────────────────▼──────────────────────────────────┐ +│ Your PostgreSQL Server │ +│ • On your local network OR │ +│ • Hosted database (Railway, Supabase, etc.) │ +│ • Connection via DATABASE_URL env variable │ +└───────────────────────────────────────────────────────────┘ +``` + +### Option 2: Self-Hosted (Docker) + +``` +┌──────────────────────────────────────────────────────────┐ +│ Your Server / VPS │ +│ ┌────────────────────────────────────────────────────┐ │ +│ │ Caddy (Reverse Proxy + Auto HTTPS) │ │ +│ │ • Automatic SSL certificates │ │ +│ │ • HTTPS redirection │ │ +│ │ • Static file serving │ │ +│ └────────────────────┬───────────────────────────────┘ │ +│ │ │ +│ ┌────────────────────▼───────────────────────────────┐ │ +│ │ Docker Container: punimtag-viewer │ │ +│ │ ┌──────────────────────────────────────────────┐ │ │ +│ │ │ Next.js Production Server │ │ │ +│ │ │ • Node.js 20 │ │ │ +│ │ │ • Optimized build │ │ │ +│ │ │ • Port 3000 │ │ │ +│ │ └────────────────┬─────────────────────────────┘ │ │ +│ └───────────────────┼────────────────────────────────┘ │ +│ │ │ +│ ┌───────────────────▼────────────────────────────────┐ │ +│ │ Docker Container: PostgreSQL (Optional) │ │ +│ │ • Or connect to existing PunimTag database │ │ +│ └────────────────────────────────────────────────────┘ │ +└───────────────────────────────────────────────────────────┘ +``` + +--- + +## Security Architecture + +``` +┌──────────────────────────────────────────────────────────┐ +│ User Browser │ +└───────────────────────┬──────────────────────────────────┘ + │ HTTPS Only + │ (TLS 1.3) + ▼ +┌──────────────────────────────────────────────────────────┐ +│ Authentication Layer │ +│ ┌────────────────────────────────────────────────────┐ │ +│ │ NextAuth.js (Optional) │ │ +│ │ • JWT Sessions │ │ +│ │ • OAuth Providers (Google, etc.) │ │ +│ │ • OR Simple Password Protection │ │ +│ └────────────────────┬───────────────────────────────┘ │ +└────────────────────────┼──────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────┐ +│ Authorization Layer │ +│ ┌────────────────────────────────────────────────────┐ │ +│ │ Read-Only Access │ │ +│ │ • No write operations allowed │ │ +│ │ • No photo uploads │ │ +│ │ • No deletion │ │ +│ │ • Favorites only write operation (per user) │ │ +│ └────────────────────┬───────────────────────────────┘ │ +└────────────────────────┼──────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────┐ +│ API Security Layer │ +│ ┌────────────────────────────────────────────────────┐ │ +│ │ • Rate Limiting (10 req/10s per IP) │ │ +│ │ • Input Validation (Zod schemas) │ │ +│ │ • SQL Injection Prevention (Prisma ORM) │ │ +│ │ • XSS Prevention (React auto-escaping) │ │ +│ │ • CSRF Protection (SameSite cookies) │ │ +│ └────────────────────┬───────────────────────────────┘ │ +└────────────────────────┼──────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────┐ +│ Database Security │ +│ ┌────────────────────────────────────────────────────┐ │ +│ │ Read-Only PostgreSQL User │ │ +│ │ • Username: viewer_readonly │ │ +│ │ • SELECT permission only │ │ +│ │ • No INSERT, UPDATE, DELETE │ │ +│ │ • Connection over encrypted channel │ │ +│ └────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────┘ +``` + +--- + +## Performance Optimization Strategy + +``` +┌──────────────────────────────────────────────────────────┐ +│ Browser Layer │ +│ ┌────────────────────────────────────────────────────┐ │ +│ │ Browser Caching │ │ +│ │ • Static assets: 1 year │ │ +│ │ • Images: 1 month │ │ +│ │ • API responses: 5 minutes │ │ +│ └────────────────────────────────────────────────────┘ │ +└───────────────────────┬──────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────┐ +│ React Query Layer │ +│ ┌────────────────────────────────────────────────────┐ │ +│ │ In-Memory Cache │ │ +│ │ • Query results cached │ │ +│ │ • staleTime: 5 minutes │ │ +│ │ • cacheTime: 10 minutes │ │ +│ │ • Automatic background refetching │ │ +│ └────────────────────────────────────────────────────┘ │ +└───────────────────────┬──────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────┐ +│ Next.js Layer │ +│ ┌────────────────────────────────────────────────────┐ │ +│ │ Image Optimization │ │ +│ │ • Automatic WebP/AVIF conversion │ │ +│ │ • Responsive images (srcset) │ │ +│ │ • Lazy loading │ │ +│ │ • Blur placeholders │ │ +│ ├────────────────────────────────────────────────────┤ │ +│ │ Code Splitting │ │ +│ │ • Route-based splitting │ │ +│ │ • Dynamic imports │ │ +│ │ • Component lazy loading │ │ +│ ├────────────────────────────────────────────────────┤ │ +│ │ Server-Side Rendering │ │ +│ │ • Initial render on server │ │ +│ │ • Faster first contentful paint │ │ +│ └────────────────────────────────────────────────────┘ │ +└───────────────────────┬──────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────┐ +│ Database Layer │ +│ ┌────────────────────────────────────────────────────┐ │ +│ │ Prisma Connection Pooling │ │ +│ │ • Reuse database connections │ │ +│ │ • Max connections: 10 │ │ +│ ├────────────────────────────────────────────────────┤ │ +│ │ PostgreSQL Indices │ │ +│ │ • idx_photos_date_taken │ │ +│ │ • idx_photos_processed │ │ +│ │ • idx_faces_person_id │ │ +│ │ • idx_photo_tags_tag │ │ +│ ├────────────────────────────────────────────────────┤ │ +│ │ Query Optimization │ │ +│ │ • Cursor-based pagination │ │ +│ │ • SELECT only needed columns │ │ +│ │ • Minimize JOINs │ │ +│ └────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────┘ +``` + +--- + +## Comparison: Admin Interface vs Photo Viewer + +| Aspect | Admin Interface | Photo Viewer | +|--------|----------------|--------------| +| **Purpose** | Manage & Process | View & Browse | +| **Tech Stack** | React + FastAPI | Next.js 14 | +| **Rendering** | Client-Side (SPA) | Server + Client (SSR) | +| **State** | React Context | TanStack Query | +| **Database Access** | Full (R/W) via API | Read-Only via Prisma | +| **Photo Upload** | ✅ Yes | ❌ No | +| **Face Processing** | ✅ Yes | ❌ No | +| **Search** | ✅ Advanced | ✅ User-friendly | +| **Performance** | Good | Excellent | +| **Mobile UX** | Functional | Optimized | +| **Auth** | Required | Optional | +| **Deployment** | Backend + Frontend | Single Next.js App | +| **Target Users** | Admin (1-2) | All Users (5-50+) | +| **Complexity** | High | Low | + +--- + +## Database Read Patterns + +### Admin Interface (Write-Heavy) + +``` +┌─────────────────────────────────────┐ +│ Photo Upload │ +│ INSERT INTO photos │ +│ (Heavy write operations) │ +└─────────────────────────────────────┘ + +┌─────────────────────────────────────┐ +│ Face Processing │ +│ INSERT INTO faces │ +│ UPDATE photos SET processed │ +│ (Batch operations) │ +└─────────────────────────────────────┘ + +┌─────────────────────────────────────┐ +│ Person Identification │ +│ UPDATE faces SET person_id │ +│ INSERT INTO person_encodings │ +└─────────────────────────────────────┘ +``` + +### Photo Viewer (Read-Heavy) + +``` +┌─────────────────────────────────────┐ +│ Browse Photos │ +│ SELECT * FROM photos │ +│ WHERE processed = true │ +│ ORDER BY date_taken DESC │ +│ LIMIT 30 OFFSET X │ +└─────────────────────────────────────┘ + +┌─────────────────────────────────────┐ +│ Search by Person │ +│ SELECT p.* FROM photos p │ +│ JOIN faces f ON p.id = f.photo_id │ +│ WHERE f.person_id = X │ +└─────────────────────────────────────┘ + +┌─────────────────────────────────────┐ +│ Get Photo Details │ +│ SELECT * FROM photos WHERE id = X │ +│ + JOIN faces + people + tags │ +└─────────────────────────────────────┘ +``` + +--- + +## Image Serving Strategy + +``` +┌──────────────────────────────────────────────────────────┐ +│ Original Photo Files │ +│ Location: /path/to/photos/2024/01/IMG_1234.jpg │ +│ Size: 4000x3000, 8MB │ +└───────────────────────┬──────────────────────────────────┘ + │ + │ Referenced by path in DB + │ +┌───────────────────────▼──────────────────────────────────┐ +│ Next.js Image Component │ +│ │ +└───────────────────────┬──────────────────────────────────┘ + │ + │ Optimization Request + │ +┌───────────────────────▼──────────────────────────────────┐ +│ Next.js Image Optimization │ +│ ┌────────────────────────────────────────────────────┐ │ +│ │ 1. Load original image │ │ +│ │ 2. Resize to requested dimensions │ │ +│ │ 3. Convert to WebP/AVIF (browser support) │ │ +│ │ 4. Apply quality compression (85%) │ │ +│ │ 5. Generate blur placeholder (base64) │ │ +│ │ 6. Cache optimized image │ │ +│ └────────────────────────────────────────────────────┘ │ +└───────────────────────┬──────────────────────────────────┘ + │ + │ Serve Optimized + │ +┌───────────────────────▼──────────────────────────────────┐ +│ Browser Display │ +│ Sizes: │ +│ • Thumbnail: 300x200 (~30KB WebP) │ +│ • Medium: 800x600 (~150KB WebP) │ +│ • Large: 1920x1440 (~500KB WebP) │ +│ • Full: 4000x3000 (~2MB original, if download enabled) │ +└──────────────────────────────────────────────────────────┘ +``` + +--- + +## Responsive Design Strategy + +### Mobile First Approach + +``` +┌─────────────────────────────────────────────────────────┐ +│ Mobile (<640px) │ +│ ┌───────────────────────────────────────────────────┐ │ +│ │ [☰] PunimTag [🔍] │ │ +│ ├───────────────────────────────────────────────────┤ │ +│ │ ┌──────────┐ ┌──────────┐ │ │ +│ │ │ Photo │ │ Photo │ 2 columns │ │ +│ │ └──────────┘ └──────────┘ │ │ +│ │ ┌──────────┐ ┌──────────┐ │ │ +│ │ │ Photo │ │ Photo │ Compact │ │ +│ │ └──────────┘ └──────────┘ │ │ +│ └───────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────┘ + +┌─────────────────────────────────────────────────────────┐ +│ Tablet (640px - 1024px) │ +│ ┌───────────────────────────────────────────────────┐ │ +│ │ [Logo] Home | People | Tags [Search] [👤] │ │ +│ ├───────────────────────────────────────────────────┤ │ +│ │ ┌─────┐ ┌─────┐ ┌─────┐ │ │ +│ │ │Photo│ │Photo│ │Photo│ 3-4 columns │ │ +│ │ └─────┘ └─────┘ └─────┘ │ │ +│ │ ┌─────┐ ┌─────┐ ┌─────┐ │ │ +│ │ │Photo│ │Photo│ │Photo│ Medium size │ │ +│ │ └─────┘ └─────┘ └─────┘ │ │ +│ └───────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────┘ + +┌─────────────────────────────────────────────────────────┐ +│ Desktop (>1024px) │ +│ ┌───────────────────────────────────────────────────┐ │ +│ │ [Logo] Home | People | Tags [Search Bar] [👤] │ │ +│ ├──────┬────────────────────────────────────────────┤ │ +│ │Filter│ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ │ │ +│ │ │ │Ph│ │Ph│ │Ph│ │Ph│ │Ph│ 5-6 columns │ │ +│ │People│ └──┘ └──┘ └──┘ └──┘ └──┘ │ │ +│ │☐ John│ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ │ │ +│ │☐ Jane│ │Ph│ │Ph│ │Ph│ │Ph│ │Ph│ Large grid │ │ +│ │ │ └──┘ └──┘ └──┘ └──┘ └──┘ │ │ +│ │Tags │ │ │ +│ │☐ Fam │ │ │ +│ └──────┴────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────┘ +``` + +--- + +## Error Handling Flow + +``` +User Request + │ + ▼ +┌──────────────────┐ +│ Try Operation │ +└────────┬─────────┘ + │ + ├─ Success ────────────────┐ + │ ▼ + │ ┌─────────────────┐ + │ │ Return Data │ + │ │ Update UI │ + │ └─────────────────┘ + │ + └─ Error ──────────────────┐ + ▼ + ┌──────────────────┐ + │ Catch Error │ + └────────┬─────────┘ + │ + ┌───────────────┼───────────────┐ + │ │ │ + ▼ ▼ ▼ + ┌───────────────┐ ┌─────────────┐ ┌────────────┐ + │ Network Error │ │ 404 Not │ │ 500 Server │ + │ (Offline) │ │ Found │ │ Error │ + └───────┬───────┘ └──────┬──────┘ └─────┬──────┘ + │ │ │ + │ │ │ + └────────────────┼──────────────┘ + ▼ + ┌──────────────────┐ + │ Log Error │ + │ (Sentry) │ + └────────┬─────────┘ + ▼ + ┌──────────────────┐ + │ Show Error UI │ + │ • Toast │ + │ • Error Page │ + │ • Retry Button │ + └──────────────────┘ +``` + +--- + +This architecture ensures a clean separation between the admin interface (for management) and the photo viewer (for browsing), while sharing the same database and maintaining data consistency. + diff --git a/docs/PHOTO_VIEWER_EXECUTIVE_SUMMARY.md b/docs/PHOTO_VIEWER_EXECUTIVE_SUMMARY.md new file mode 100644 index 0000000..66e5015 --- /dev/null +++ b/docs/PHOTO_VIEWER_EXECUTIVE_SUMMARY.md @@ -0,0 +1,425 @@ +# PunimTag Photo Viewer - Executive Summary + +## 📋 Overview + +This document provides a high-level summary of the plan to create a new photo viewing website for PunimTag. This summary is designed for decision-makers and provides key recommendations. + +--- + +## 🎯 What Are We Building? + +A **modern, fast, beautiful photo gallery website** that allows family members to browse and search photos from your PunimTag database without needing admin access. + +### Key Features +- ✅ Browse all photos in a beautiful grid layout +- ✅ Search by people, dates, and tags +- ✅ View full-size photos with metadata +- ✅ Mobile-optimized responsive design +- ✅ Fast loading with modern optimizations +- ✅ Optional authentication for privacy + +### What It's NOT +- ❌ Not a replacement for the admin interface +- ❌ No photo uploads (read-only) +- ❌ No face processing or management +- ❌ No bulk operations + +--- + +## 💡 Why Do We Need This? + +### Current Situation +You have a powerful admin interface for managing photos, but it's: +- Too complex for casual family browsing +- Requires admin credentials +- Focused on management, not viewing +- Not optimized for mobile devices + +### The Solution +A separate, simplified photo viewer that: +- Focuses solely on viewing and searching +- Easy for anyone in the family to use +- Optimized for browsing experience +- Beautiful, modern design +- Works great on all devices + +--- + +## 🏗️ Technical Approach + +### Recommended Technology Stack + +| Component | Technology | Why? | +|-----------|-----------|------| +| **Framework** | Next.js 14 | Best-in-class for performance, SEO, and developer experience | +| **UI Library** | shadcn/ui + Tailwind | Modern, beautiful, customizable components | +| **Database** | Existing PostgreSQL | Reuse your database with read-only access | +| **ORM** | Prisma | Type-safe database queries, excellent PostgreSQL support | +| **State** | TanStack Query | Perfect for server data caching and infinite scroll | +| **Images** | Next.js Image | Automatic optimization, WebP conversion, lazy loading | +| **Hosting** | Vercel (recommended) | Zero-config deployment, global CDN, free tier available | + +### Architecture Overview + +``` +User Browser → Next.js Frontend → Prisma ORM → PostgreSQL Database + (Read-Only User) +``` + +--- + +## 📊 Decision Matrix + +### 🌟 Recommended Approach: Next.js with Vercel + +**Pros:** +- ⭐ Fastest time to market (6-8 weeks) +- ⭐ Best performance (Lighthouse score >90) +- ⭐ Easiest deployment (zero config) +- ⭐ Free hosting tier available +- ⭐ Automatic image optimization +- ⭐ Built-in SEO and accessibility + +**Cons:** +- 💰 Can be expensive for high traffic (but free tier is generous) +- 🌐 Requires database to be network-accessible (or use hosted DB) + +**Best For:** Most users, especially if you want the best UX and fastest development + +--- + +### Alternative 1: Self-Hosted Docker + +**Pros:** +- 💰 Lower ongoing costs (after initial setup) +- 🔒 Full control over hosting +- 🏠 Can run on local network +- 🔐 More privacy (no third-party hosting) + +**Cons:** +- 🛠️ Requires DevOps knowledge +- ⏰ Longer setup time +- 📈 Manual scaling +- 🔧 More maintenance + +**Best For:** Technical users who want full control and have server management experience + +--- + +### Alternative 2: Astro + React Islands + +**Pros:** +- ⚡ Even faster than Next.js +- 💾 Lower bandwidth usage +- 🎨 Great for content-heavy sites + +**Cons:** +- 📚 Smaller ecosystem than Next.js +- 🆕 Less mature (but stable) +- 🔧 More manual configuration + +**Best For:** Users who prioritize absolute maximum performance + +--- + +## 💰 Cost Analysis + +### Development Costs + +| Approach | Timeline | Estimated Cost | +|----------|----------|---------------| +| **DIY (Self-Development)** | 12 weeks | Your time (200-300 hours) | +| **Freelancer** | 8-12 weeks | $18,000 - $28,000 | +| **Agency** | 6-10 weeks | $30,000 - $50,000 | + +### Monthly Hosting Costs + +| Option | Cost/Month | Best For | +|--------|------------|----------| +| **Vercel (Free Tier)** | $0 | Small families (<100 users) | +| **Vercel (Pro)** | $20 | Medium usage (100-1000 users) | +| **Railway** | $5-20 | Hobby projects | +| **VPS (DigitalOcean)** | $12-24 | Self-hosted, full control | +| **Home Server** | $0* | After hardware cost, local network | + +**Recommendation:** Start with Vercel Free Tier, upgrade if needed + +--- + +## ⏱️ Timeline + +### Fast Track (6 weeks) + +| Week | Phase | Deliverables | +|------|-------|--------------| +| 1-2 | Foundation | Project setup, database connection, basic layout | +| 3-4 | Core Features | Photo grid, lightbox, search by people/tags | +| 5 | Polish | Responsive design, animations, performance | +| 6 | Launch | Deploy, test, documentation | + +### Standard Track (12 weeks) + +| Week | Phase | Deliverables | +|------|-------|--------------| +| 1-2 | Foundation | Setup + authentication | +| 3-4 | Core Features | Photo browsing | +| 5-6 | Search | Advanced search and filters | +| 7-8 | Polish | Optimization and UX | +| 9-10 | Advanced | Favorites, sharing, timeline | +| 11-12 | Launch | Testing, deployment, docs | + +**Recommendation:** Standard track for best quality and features + +--- + +## 🎨 Design Approach + +### Modern, Clean, Fast + +- **Inspiration:** Google Photos, iCloud Photos +- **Style:** Minimalist, photo-first design +- **Colors:** Modern blues and clean whites/darks +- **Typography:** Inter font family (clean, readable) +- **Layout:** Responsive grid (masonry or justified) +- **Animations:** Subtle, smooth, meaningful + +### Mobile First +- Works beautifully on phones and tablets +- Touch-optimized interactions +- Fast loading on cellular networks +- Responsive images for all screen sizes + +--- + +## 🔒 Security Considerations + +### Database Security +- ✅ Read-only PostgreSQL user (no write access) +- ✅ Separate credentials from admin interface +- ✅ Encrypted database connections +- ✅ Network isolation (if self-hosted) + +### Application Security +- ✅ Optional authentication (NextAuth.js) +- ✅ Rate limiting to prevent abuse +- ✅ Input validation on all queries +- ✅ XSS and CSRF protection (built-in) +- ✅ HTTPS only (automatic with Vercel/Caddy) + +### Privacy Options +- ✅ Strip GPS data from EXIF +- ✅ Configurable download permissions +- ✅ Per-user favorites (privacy-preserving) +- ✅ Optional authentication requirement + +--- + +## 📈 Success Metrics + +### Technical Goals +- 🎯 Lighthouse Performance Score: >90 +- 🎯 Page Load Time: <2 seconds +- 🎯 Image Load Time: <1 second +- 🎯 API Response Time: <200ms +- 🎯 Mobile Experience: Excellent + +### User Experience Goals +- 🎯 User Adoption: 90% of family members +- 🎯 Session Duration: >5 minutes +- 🎯 Return Visits: >60% weekly +- 🎯 User Satisfaction: >4.5/5 stars +- 🎯 Mobile Usage: >50% of traffic + +--- + +## 🚦 Go/No-Go Criteria + +### ✅ Proceed If: +- You have a PostgreSQL database with photos +- You want to share photos with family/friends +- You have budget for development OR time to DIY +- You value user experience and modern design +- You want mobile-optimized photo browsing + +### ❌ Don't Proceed If: +- Your database has <100 photos (not worth it yet) +- You're happy with current admin interface for viewing +- Budget constraints (<$500 for hosting/development) +- You don't care about mobile experience +- You need write operations (upload, edit, delete) + +--- + +## 🎬 Next Steps + +### Option A: Hire Developer/Agency +1. ✅ Approve this plan +2. ✅ Choose technology stack (recommend: Next.js + Vercel) +3. ✅ Set budget and timeline +4. ✅ Find and hire developer/agency +5. ✅ Provide database access (read-only user) +6. ✅ Review progress weekly +7. ✅ Test and provide feedback +8. ✅ Launch and train users + +### Option B: DIY Development +1. ✅ Review technical requirements +2. ✅ Follow Quick Start Guide (`docs/PHOTO_VIEWER_QUICKSTART.md`) +3. ✅ Set up development environment +4. ✅ Follow phase-by-phase plan (`docs/PHOTO_VIEWER_PLAN.md`) +5. ✅ Build incrementally (one feature at a time) +6. ✅ Test with real users frequently +7. ✅ Deploy when stable +8. ✅ Iterate based on feedback + +### Option C: Hybrid Approach +1. ✅ Hire freelancer for initial setup (Weeks 1-4) +2. ✅ Learn and customize yourself (Weeks 5+) +3. ✅ Freelancer available for support +4. ✅ Best of both worlds + +**Recommendation:** Option A or C for fastest, highest-quality results + +--- + +## ❓ Frequently Asked Questions + +### Q: Can I keep using the admin interface? +**A:** Yes! The photo viewer is a separate application. Your admin interface continues working exactly as before. + +### Q: Will this modify my database? +**A:** No. The photo viewer uses a read-only database user. It can't modify your photos, faces, or people data. The only exception is the optional favorites feature, which adds per-user favorites to a separate table. + +### Q: Do I need to migrate data? +**A:** No. The photo viewer reads directly from your existing PunimTag PostgreSQL database. No data migration needed. + +### Q: Can I customize the design? +**A:** Absolutely! The design is fully customizable. You can change colors, fonts, layouts, and components to match your preferences. + +### Q: What if I have 100,000 photos? +**A:** The architecture is designed to scale. With proper indexing and pagination, it can handle hundreds of thousands of photos. Performance may require some optimization for very large databases. + +### Q: Can users upload photos? +**A:** Not in the initial design (read-only). However, this can be added as a future enhancement if needed. The admin interface remains the primary way to add photos. + +### Q: Is it mobile-friendly? +**A:** Yes! Mobile-first design with responsive layouts, touch-optimized interactions, and fast loading on cellular networks. + +### Q: What about authentication? +**A:** Optional. You can: +- Make it completely public (no login) +- Add simple password protection +- Use OAuth (Google, Facebook) +- Use email/password authentication + +Choose based on your privacy needs. + +### Q: Can I host it on my home server? +**A:** Yes! You can self-host using Docker. The plan includes instructions for both cloud (Vercel) and self-hosted (Docker) deployment. + +### Q: What if my database is not network-accessible? +**A:** For Vercel hosting, you'll need network access. For self-hosted, you can run it on the same network as your database. + +### Q: How do I update it when new photos are added? +**A:** It's automatic! The viewer reads from the live database, so new photos appear immediately after they're processed in the admin interface. + +--- + +## 🎯 Recommendation Summary + +### For Most Users: Next.js + Vercel +- ⭐ Best performance and user experience +- ⭐ Fastest development (6-8 weeks) +- ⭐ Easiest deployment and maintenance +- ⭐ Free hosting tier available +- ⭐ Proven, mature ecosystem + +### Cost: $0-20/month hosting + $18K-28K development (or DIY) + +### Timeline: 6-12 weeks depending on approach + +### Next Action: Review full plan, make go/no-go decision, allocate budget + +--- + +## 📚 Documentation Index + +| Document | Purpose | Audience | +|----------|---------|----------| +| **This Document** | Executive summary and decision guide | Decision makers | +| `PHOTO_VIEWER_PLAN.md` | Complete detailed plan (20+ pages) | Developers, project managers | +| `PHOTO_VIEWER_QUICKSTART.md` | Quick setup guide (5 minutes to start) | Developers | +| `PHOTO_VIEWER_ARCHITECTURE.md` | Technical architecture and diagrams | Developers, architects | + +--- + +## ✅ Action Items + +### For You (Decision Maker) +- [ ] Review this executive summary +- [ ] Read the full detailed plan if needed +- [ ] Make go/no-go decision +- [ ] Allocate budget (development + hosting) +- [ ] Choose deployment approach (Vercel vs self-hosted) +- [ ] Decide on authentication requirement +- [ ] Approve timeline and milestones + +### For Developer/Agent +- [ ] Read all documentation +- [ ] Set up development environment +- [ ] Create read-only database user +- [ ] Initialize Next.js project +- [ ] Follow Phase 1 tasks +- [ ] Provide weekly progress updates + +--- + +## 🎊 Expected Outcome + +After completion, you'll have: + +✅ **A beautiful, modern photo gallery** that family members love to use + +✅ **Fast, responsive browsing** on all devices (desktop, tablet, mobile) + +✅ **Powerful search capabilities** (people, dates, tags) + +✅ **Read-only access** that keeps your database safe + +✅ **Separate from admin interface** so you can manage photos independently + +✅ **Scalable architecture** that grows with your photo collection + +✅ **Low maintenance** with automatic updates and optimizations + +--- + +## 📧 Questions? + +If you have questions or need clarification on any part of this plan: + +1. **Review the detailed plan** (`PHOTO_VIEWER_PLAN.md`) +2. **Check the architecture** (`PHOTO_VIEWER_ARCHITECTURE.md`) +3. **Try the quick start** (`PHOTO_VIEWER_QUICKSTART.md`) +4. **Consult Next.js docs** (https://nextjs.org/docs) + +--- + +**Ready to proceed?** 🚀 + +Choose your path: +- **Option A:** Hire a developer and provide them with this plan +- **Option B:** Build it yourself using the Quick Start Guide +- **Option C:** Hybrid approach (developer + your customizations) + +**This is a well-planned project with clear deliverables, proven technologies, and realistic timelines.** + +Let's build something amazing! 🎉 + +--- + +**Document Version:** 1.0 +**Last Updated:** November 14, 2025 +**Status:** Ready for Decision +**Recommended Action:** Proceed with Next.js + Vercel approach + diff --git a/docs/PHOTO_VIEWER_PLAN.md b/docs/PHOTO_VIEWER_PLAN.md new file mode 100644 index 0000000..21e7672 --- /dev/null +++ b/docs/PHOTO_VIEWER_PLAN.md @@ -0,0 +1,1989 @@ +# PunimTag Photo Viewer - Frontend Website Plan + +## Executive Summary + +This document outlines the detailed plan for creating a new, lightweight, public-facing photo viewing website that connects to the existing PunimTag PostgreSQL database. This website will be separate from the admin interface and focused on providing a beautiful, fast, and intuitive photo browsing experience. + +--- + +## 1. Project Overview + +### 1.1 Purpose +Create a read-only photo gallery website that allows users to: +- Browse photos from the PunimTag database +- Search by people names, dates taken, and tags +- View high-quality photos with smooth navigation +- Experience a modern, responsive design + +### 1.2 Key Differentiators from Admin Interface +| Feature | Admin Interface | Photo Viewer | +|---------|----------------|--------------| +| Purpose | Photo management & AI processing | Photo viewing & browsing | +| Authentication | Required | Optional (configurable) | +| Operations | Read/Write (CRUD) | Read-only | +| Target Users | Administrators | End users/Family members | +| Complexity | Feature-rich, complex | Simple, elegant | +| Performance | Batch operations | Optimized for browsing | + +--- + +## 2. Technology Stack + +### 2.1 Frontend Framework +**Next.js 14+ (App Router)** +- **Why Next.js?** + - Built-in SSR/SSG for excellent performance + - Image optimization out of the box + - File-based routing + - API routes for backend proxy + - Excellent SEO support + - Great TypeScript support + +**Alternative: Astro 4+ with React Islands** +- Even faster page loads +- Partial hydration +- Great for content-heavy sites +- Zero JS by default + +**Recommendation: Next.js 14** (more mature ecosystem, better for dynamic data) + +### 2.2 UI Framework & Styling +**shadcn/ui + Tailwind CSS** +- Modern, accessible components +- Highly customizable +- Built on Radix UI primitives +- Consistent design system +- Copy-paste components (no npm bloat) + +**Additional UI Libraries:** +- **Framer Motion** - Smooth animations and transitions +- **Lucide React** - Beautiful, consistent icons +- **React Photo Album** - Optimized photo grid layouts +- **Yet Another React Lightbox** - Photo viewing/slideshow + +### 2.3 State Management +**TanStack Query (React Query) v5** +- Perfect for server state management +- Built-in caching and invalidation +- Loading and error states +- Infinite scroll support +- Optimistic updates + +**Zustand** (optional, for client state) +- Lightweight alternative to Redux +- Simple API +- Only if needed for complex client state + +### 2.4 Backend Architecture + +#### Option A: Direct Database Access (Recommended for simplicity) +``` +Next.js App → Prisma ORM → PostgreSQL Database +``` + +**Prisma ORM** +- Type-safe database client +- Auto-generated types from schema +- Excellent PostgreSQL support +- Read-only mode for safety + +#### Option B: Backend Proxy (Recommended for security) +``` +Next.js App → Lightweight API Layer → PostgreSQL Database +``` + +**Lightweight API Options:** +1. **Next.js API Routes** - Built-in, simple +2. **tRPC** - End-to-end typesafe APIs +3. **Separate FastAPI microservice** - Reuse existing backend code + +**Recommendation: Next.js API Routes with Prisma** (simplest, most integrated) + +### 2.5 Database Strategy +**Read-Only PostgreSQL Access** +- Connect to existing PunimTag PostgreSQL database +- Read-only database user for security +- Use connection pooling (PgBouncer or Prisma Accelerate) +- No schema changes needed + +--- + +## 3. Architecture Design + +### 3.1 Project Structure +``` +punimtag-viewer/ +├── src/ +│ ├── app/ # Next.js 14 App Router +│ │ ├── (marketing)/ # Public pages (landing, about) +│ │ ├── (viewer)/ # Main photo viewer pages +│ │ │ ├── layout.tsx # Viewer layout with nav +│ │ │ ├── page.tsx # Home/Gallery grid +│ │ │ ├── photo/[id]/ # Individual photo view +│ │ │ ├── people/ # Browse by people +│ │ │ ├── tags/ # Browse by tags +│ │ │ └── search/ # Search results +│ │ ├── api/ # API routes (if needed) +│ │ └── layout.tsx # Root layout +│ ├── components/ # React components +│ │ ├── ui/ # shadcn/ui components +│ │ ├── gallery/ # Gallery-specific components +│ │ │ ├── PhotoGrid.tsx +│ │ │ ├── PhotoCard.tsx +│ │ │ ├── Lightbox.tsx +│ │ │ └── InfiniteScroll.tsx +│ │ ├── search/ # Search components +│ │ │ ├── SearchBar.tsx +│ │ │ ├── FilterPanel.tsx +│ │ │ └── DateRangePicker.tsx +│ │ └── layout/ # Layout components +│ │ ├── Header.tsx +│ │ ├── Sidebar.tsx +│ │ └── Footer.tsx +│ ├── lib/ # Utilities and configs +│ │ ├── db.ts # Prisma client +│ │ ├── queries.ts # Database queries +│ │ ├── utils.ts # Helper functions +│ │ └── constants.ts # Constants +│ ├── hooks/ # Custom React hooks +│ │ ├── usePhotos.ts +│ │ ├── usePeople.ts +│ │ ├── useTags.ts +│ │ └── useInfiniteScroll.ts +│ └── types/ # TypeScript types +│ └── database.ts +├── prisma/ +│ └── schema.prisma # Prisma schema (read-only) +├── public/ +│ └── assets/ # Static assets +├── .env.local # Environment variables +├── next.config.js +├── tailwind.config.ts +├── package.json +└── README.md +``` + +### 3.2 Data Flow + +``` +User Browser + ↓ +Next.js Frontend (React Components) + ↓ +TanStack Query (Caching & State) + ↓ +Next.js API Routes (Optional Proxy) + ↓ +Prisma ORM (Type-safe queries) + ↓ +PostgreSQL Database (Read-Only) + ↓ +Return Photo Data + URLs + ↓ +Next.js Image Optimization + ↓ +Optimized Images to User +``` + +### 3.3 Image Serving Strategy + +#### Option A: Direct File Access (Fastest) +- Next.js serves images directly from file system +- Use Next.js Image component for optimization +- Configure `images.domains` for external sources +- Requires access to original photo files + +#### Option B: API Proxy (More Flexible) +- API endpoint serves images from database-referenced paths +- Supports image resizing and caching +- Can add watermarks, metadata stripping +- Slightly slower but more controlled + +**Recommendation: Option A** (Direct file access with Next.js Image optimization) + +--- + +## 4. Core Features & Pages + +### 4.1 Home/Gallery Page +**URL:** `/` + +**Features:** +- Infinite scroll photo grid (masonry or justified layout) +- Lazy loading with blur placeholders +- Quick filters: All, Favorites, Recent +- Photo count badge +- Smooth animations on load + +**Components:** +- `PhotoGrid` - Main grid container +- `PhotoCard` - Individual photo card with hover effects +- `InfiniteScroll` - Load more on scroll +- `FilterBar` - Quick filter chips + +**Performance:** +- Initial load: 30 photos +- Lazy load: 30 photos per page +- Virtual scrolling for 10,000+ photos + +### 4.2 Search Page +**URL:** `/search` + +**Search Criteria:** +1. **People Search** + - Autocomplete dropdown + - Multiple people selection + - "Match all" vs "Match any" toggle + +2. **Date Search** + - Date range picker + - Preset ranges (Today, This week, This month, This year) + - Custom date range + +3. **Tag Search** + - Tag cloud with counts + - Multi-select tags + - Hierarchical tags (if implemented) + +4. **Combined Search** + - All filters work together (AND logic) + - Clear filters button + - Search result count + +**Components:** +- `SearchBar` - Main search input +- `PeopleFilter` - People selection +- `DateRangePicker` - Date picker +- `TagFilter` - Tag selection +- `SearchResults` - Results grid + +### 4.3 Photo Detail Page +**URL:** `/photo/[id]` + +**Features:** +- Full-size photo viewer +- Photo metadata panel + - Date taken + - Dimensions + - Tagged people (with links) + - Tags (with links) +- Previous/Next navigation +- Keyboard shortcuts (←/→ arrows, Esc) +- Download button (optional, configurable) +- Share button (copy link) + +**Components:** +- `Lightbox` - Full-screen photo viewer +- `MetadataPanel` - Photo information +- `PeopleList` - Tagged people +- `TagList` - Photo tags + +### 4.4 People Browser +**URL:** `/people` + +**Features:** +- Grid of people with representative photo +- Person name and photo count +- Sort by: Name, Photo count, Recent +- Search/filter people + +**Detail Page:** `/people/[id]` +- Person's name and details +- Grid of all photos featuring this person +- Timeline view option +- Photo count + +### 4.5 Tags Browser +**URL:** `/tags` + +**Features:** +- Tag cloud or list view +- Photo count per tag +- Color-coded tags (if applicable) +- Search tags + +**Detail Page:** `/tags/[id]` +- All photos with this tag +- Related tags +- Tag description (if available) + +### 4.6 Timeline View (Optional) +**URL:** `/timeline` + +**Features:** +- Chronological photo layout +- Group by year/month/day +- Timeline scrubber +- Jump to date + +--- + +## 5. UI/UX Design + +### 5.1 Design System + +**Color Palette (Modern & Clean):** +``` +Primary: + - Light: #3b82f6 (Blue 500) + - Dark: #60a5fa (Blue 400) + +Secondary: + - Light: #8b5cf6 (Violet 500) + - Dark: #a78bfa (Violet 400) + +Neutral: + - Background Light: #ffffff + - Background Dark: #0f172a (Slate 900) + - Surface Light: #f8fafc (Slate 50) + - Surface Dark: #1e293b (Slate 800) + +Text: + - Primary Light: #0f172a (Slate 900) + - Primary Dark: #f8fafc (Slate 50) + - Secondary Light: #64748b (Slate 500) + - Secondary Dark: #94a3b8 (Slate 400) +``` + +**Typography:** +``` +Font Family: + - Headings: Inter (Google Fonts) + - Body: Inter + - Mono: JetBrains Mono (for metadata) + +Font Sizes: + - Display: 4rem (64px) + - H1: 3rem (48px) + - H2: 2.25rem (36px) + - H3: 1.875rem (30px) + - H4: 1.5rem (24px) + - Body: 1rem (16px) + - Small: 0.875rem (14px) + - Tiny: 0.75rem (12px) +``` + +**Spacing Scale:** +- 4, 8, 12, 16, 20, 24, 32, 40, 48, 64, 80, 96 (px) + +**Border Radius:** +- Small: 4px +- Medium: 8px +- Large: 12px +- XLarge: 16px + +### 5.2 Layout & Navigation + +**Header (Desktop):** +``` +┌────────────────────────────────────────────────────┐ +│ [Logo] PunimTag [Search] [User Menu] │ +│ Home | People | Tags | Timeline │ +└────────────────────────────────────────────────────┘ +``` + +**Header (Mobile):** +``` +┌─────────────────────────────┐ +│ [☰] PunimTag [Search] │ +└─────────────────────────────┘ +``` + +**Sidebar Filter Panel (Desktop):** +``` +┌──────────────┐ +│ Filters │ +│ │ +│ People │ +│ □ John │ +│ □ Jane │ +│ │ +│ Date Range │ +│ [Picker] │ +│ │ +│ Tags │ +│ □ Family │ +│ □ Vacation │ +│ │ +│ [Apply] │ +└──────────────┘ +``` + +### 5.3 Responsive Design + +**Breakpoints:** +- Mobile: < 640px (1 column) +- Tablet: 640px - 1024px (2-3 columns) +- Desktop: > 1024px (4-5 columns) +- Wide: > 1536px (5-6 columns) + +**Photo Grid:** +- Mobile: 1-2 columns +- Tablet: 3-4 columns +- Desktop: 4-5 columns +- Wide: 6+ columns + +### 5.4 Animation & Transitions + +**Principles:** +- Subtle and smooth (no jarring animations) +- Fast transitions (150-300ms) +- Meaningful motion (guide user attention) + +**Key Animations:** +1. Photo Grid + - Fade in on load + - Scale on hover + - Smooth masonry layout shifts + +2. Page Transitions + - Fade between pages + - Slide in sidebars + +3. Lightbox + - Zoom in from thumbnail + - Smooth previous/next transitions + +**Framer Motion Variants:** +```typescript +const photoCardVariants = { + hidden: { opacity: 0, y: 20 }, + visible: { + opacity: 1, + y: 0, + transition: { duration: 0.3 } + }, + hover: { + scale: 1.05, + transition: { duration: 0.2 } + } +} +``` + +--- + +## 6. Performance Optimization + +### 6.1 Image Optimization + +**Next.js Image Component:** +```typescript +{photo.filename} +``` + +**Strategies:** +1. **Responsive Images** + - Generate multiple sizes (thumbnail, medium, large, full) + - Serve appropriate size based on viewport + - Use `srcset` and `sizes` attributes + +2. **Blur Placeholder** + - Generate blur hash on backend + - Show blur while loading + - Smooth transition to full image + +3. **Lazy Loading** + - Load images as they enter viewport + - Prefetch next page of images + - Intersection Observer API + +4. **CDN Integration (Optional)** + - Cloudflare Images + - Vercel Image Optimization + - imgix or Cloudinary + +### 6.2 Data Fetching Optimization + +**React Query Configuration:** +```typescript +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 5 * 60 * 1000, // 5 minutes + cacheTime: 10 * 60 * 1000, // 10 minutes + refetchOnWindowFocus: false, + retry: 1, + }, + }, +}); +``` + +**Strategies:** +1. **Prefetching** + - Prefetch next page on current page load + - Prefetch photo details on hover + +2. **Infinite Scroll** + - Use React Query's `useInfiniteQuery` + - Load 30 photos per page + - Keep 3 pages in memory + +3. **Debouncing** + - Debounce search inputs (300ms) + - Debounce filter changes + +4. **Parallel Queries** + - Fetch photos and metadata simultaneously + - Batch API requests + +### 6.3 Database Optimization + +**Indexing Strategy:** +```sql +-- Existing indices (already in PunimTag) +CREATE INDEX idx_photos_date_taken ON photos(date_taken); +CREATE INDEX idx_photos_processed ON photos(processed); +CREATE INDEX idx_faces_person_id ON faces(person_id); +CREATE INDEX idx_faces_photo_id ON faces(photo_id); +CREATE INDEX idx_photo_tags_tag ON phototaglinkage(tag_id); +CREATE INDEX idx_photo_tags_photo ON phototaglinkage(photo_id); + +-- Additional indices for viewer +CREATE INDEX idx_photos_date_added_desc ON photos(date_added DESC); +CREATE INDEX idx_people_names ON people(first_name, last_name); +CREATE INDEX idx_tags_name ON tags(tag_name); +``` + +**Query Optimization:** +1. **Pagination** + - Use cursor-based pagination for infinite scroll + - Avoid OFFSET for large datasets + +2. **Joins** + - Minimize JOIN operations + - Use SELECT only needed columns + - Aggregate queries for counts + +3. **Connection Pooling** + - Use PgBouncer or Prisma connection pooling + - Limit max connections + +### 6.4 Caching Strategy + +**Multi-Layer Caching:** + +1. **Browser Cache** + - Cache static assets (1 year) + - Cache images (1 month) + - Cache API responses (5 minutes) + +2. **React Query Cache** + - Cache query results in memory + - Automatic invalidation + - Background refetching + +3. **Next.js Cache** + - Static page generation (ISR) + - API route caching + - Image optimization cache + +4. **Database Cache (Optional)** + - Redis for frequently accessed data + - Cache photo counts, tag lists + - Cache search results + +### 6.5 Performance Targets + +| Metric | Target | Measurement | +|--------|--------|-------------| +| First Contentful Paint | < 1.5s | Lighthouse | +| Largest Contentful Paint | < 2.5s | Lighthouse | +| Time to Interactive | < 3.5s | Lighthouse | +| Cumulative Layout Shift | < 0.1 | Lighthouse | +| API Response Time | < 200ms | Server logs | +| Photo Load Time | < 1s | Network tab | +| Infinite Scroll Delay | < 500ms | User testing | + +--- + +## 7. Search Implementation + +### 7.1 Search API Endpoints + +**Existing PunimTag API:** `/api/photos` +```typescript +// Search photos by multiple criteria +GET /api/photos?search_type=name&person_name=John&page=1&page_size=30 +GET /api/photos?search_type=date&date_from=2024-01-01&date_to=2024-12-31 +GET /api/photos?search_type=tags&tag_names=family,vacation&match_all=false +``` + +**New Viewer API (Recommended):** +```typescript +// Combined search endpoint +GET /api/viewer/photos?people=1,2&tags=3,4&date_from=2024-01-01&page=1 + +// Get all people with photo counts +GET /api/viewer/people + +// Get all tags with photo counts +GET /api/viewer/tags + +// Get photo by ID with full metadata +GET /api/viewer/photos/:id +``` + +### 7.2 Search UI Components + +**SearchBar Component:** +```typescript +interface SearchBarProps { + onSearch: (query: string) => void; + placeholder?: string; +} + +// Features: +// - Debounced input (300ms) +// - Clear button +// - Search icon +// - Keyboard shortcuts (Cmd+K, Ctrl+K) +``` + +**FilterPanel Component:** +```typescript +interface FilterPanelProps { + people: Person[]; + tags: Tag[]; + dateRange: DateRange; + onFilterChange: (filters: Filters) => void; +} + +// Features: +// - Collapsible sections +// - Multi-select with checkboxes +// - Date range picker +// - Clear all filters +// - Active filter count badge +``` + +**PeopleAutocomplete:** +```typescript +interface PeopleAutocompleteProps { + onSelect: (people: Person[]) => void; + multiple?: boolean; +} + +// Features: +// - Fuzzy search +// - Avatar thumbnails +// - Photo count badges +// - Keyboard navigation +``` + +### 7.3 Search Query Building + +**Prisma Query Example:** +```typescript +async function searchPhotos(filters: SearchFilters) { + const where = { + processed: true, + AND: [ + // Date filter + filters.dateFrom ? { date_taken: { gte: filters.dateFrom } } : {}, + filters.dateTo ? { date_taken: { lte: filters.dateTo } } : {}, + + // People filter (photo has face with person_id in list) + filters.people?.length ? { + faces: { + some: { + person_id: { in: filters.people } + } + } + } : {}, + + // Tags filter + filters.tags?.length ? { + photo_tags: { + some: { + tag_id: { in: filters.tags } + } + } + } : {}, + ] + }; + + return await prisma.photo.findMany({ + where, + include: { + faces: { + include: { person: true } + }, + photo_tags: { + include: { tag: true } + } + }, + orderBy: { date_taken: 'desc' }, + take: 30, + skip: (page - 1) * 30, + }); +} +``` + +### 7.4 Search Performance + +**Optimization Techniques:** +1. **Full-Text Search (Future Enhancement)** + ```sql + -- Add tsvector column for fast text search + ALTER TABLE photos ADD COLUMN search_vector tsvector; + CREATE INDEX idx_photos_search ON photos USING gin(search_vector); + ``` + +2. **Materialized Views (For Aggregate Counts)** + ```sql + -- Precompute photo counts per person + CREATE MATERIALIZED VIEW person_photo_counts AS + SELECT person_id, COUNT(*) as photo_count + FROM faces + WHERE person_id IS NOT NULL + GROUP BY person_id; + ``` + +3. **Elasticsearch Integration (Advanced)** + - Index photos in Elasticsearch + - Fast full-text search + - Faceted search + - Relevance scoring + +--- + +## 8. Security & Access Control + +### 8.1 Authentication (Optional but Recommended) + +**Auth Options:** + +1. **Simple Password Protection** + - Single shared password + - Session-based auth + - Cookie storage + +2. **Email/Password Auth** + - User registration + - Password hashing (bcrypt) + - JWT tokens + +3. **OAuth/Social Login** + - Google, Facebook, GitHub + - NextAuth.js integration + - Easy for users + +4. **Family Sharing Model** + - Invite codes + - Family groups + - Role-based access + +**Recommendation:** Start with NextAuth.js for flexibility + +**NextAuth.js Configuration:** +```typescript +// app/api/auth/[...nextauth]/route.ts +import NextAuth from "next-auth"; +import CredentialsProvider from "next-auth/providers/credentials"; +import GoogleProvider from "next-auth/providers/google"; + +export const authOptions = { + providers: [ + GoogleProvider({ + clientId: process.env.GOOGLE_ID, + clientSecret: process.env.GOOGLE_SECRET, + }), + CredentialsProvider({ + // Simple password or email/password + credentials: { + password: { label: "Password", type: "password" } + }, + authorize: async (credentials) => { + // Verify password + if (credentials.password === process.env.VIEWER_PASSWORD) { + return { id: "viewer", name: "Family Member" }; + } + return null; + } + }) + ], + pages: { + signIn: '/login', + }, + session: { + strategy: "jwt", + } +}; +``` + +### 8.2 Read-Only Database Access + +**PostgreSQL Read-Only User:** +```sql +-- Create read-only user +CREATE USER viewer_readonly WITH PASSWORD 'secure_password_here'; + +-- Grant connect permission +GRANT CONNECT ON DATABASE punimtag TO viewer_readonly; + +-- Grant usage on schema +GRANT USAGE ON SCHEMA public TO viewer_readonly; + +-- Grant SELECT on all tables +GRANT SELECT ON ALL TABLES IN SCHEMA public TO viewer_readonly; + +-- Grant SELECT on future tables +ALTER DEFAULT PRIVILEGES IN SCHEMA public +GRANT SELECT ON TABLES TO viewer_readonly; + +-- Ensure no write permissions +REVOKE INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public FROM viewer_readonly; +``` + +**Prisma Configuration:** +```typescript +// prisma/schema.prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL_READONLY") +} + +// .env.local +DATABASE_URL_READONLY="postgresql://viewer_readonly:secure_password@localhost:5432/punimtag" +``` + +### 8.3 API Security + +**Rate Limiting:** +```typescript +// middleware.ts +import { Ratelimit } from "@upstash/ratelimit"; +import { Redis } from "@upstash/redis"; + +const ratelimit = new Ratelimit({ + redis: Redis.fromEnv(), + limiter: Ratelimit.slidingWindow(10, "10 s"), +}); + +export async function middleware(request: Request) { + const ip = request.headers.get("x-forwarded-for") ?? "127.0.0.1"; + const { success } = await ratelimit.limit(ip); + + if (!success) { + return new Response("Too Many Requests", { status: 429 }); + } +} +``` + +**Input Validation:** +```typescript +import { z } from "zod"; + +const searchParamsSchema = z.object({ + people: z.array(z.number().int().positive()).optional(), + tags: z.array(z.number().int().positive()).optional(), + dateFrom: z.string().datetime().optional(), + dateTo: z.string().datetime().optional(), + page: z.number().int().positive().default(1), + pageSize: z.number().int().positive().max(100).default(30), +}); +``` + +**Content Security Policy:** +```typescript +// next.config.js +const securityHeaders = [ + { + key: 'Content-Security-Policy', + value: ` + default-src 'self'; + img-src 'self' data: https:; + script-src 'self' 'unsafe-eval' 'unsafe-inline'; + style-src 'self' 'unsafe-inline'; + `.replace(/\s{2,}/g, ' ').trim() + }, + { + key: 'X-Frame-Options', + value: 'DENY' + }, + { + key: 'X-Content-Type-Options', + value: 'nosniff' + }, +]; +``` + +### 8.4 Privacy Considerations + +**Configuration Options:** +1. **Public vs Private Mode** + - Public: No auth required + - Private: Auth required for all pages + - Hybrid: Auth required for specific people/tags + +2. **Download Controls** + - Enable/disable photo downloads + - Watermark on downloads + - Resolution limits + +3. **EXIF Data** + - Strip GPS coordinates + - Remove camera metadata + - Keep only date_taken + +4. **Favorites & User Data** + - Per-user favorites + - View history + - User-specific albums + +--- + +## 9. Development Roadmap + +### Phase 1: Foundation (Week 1-2) +**Goal:** Basic project setup and infrastructure + +**Tasks:** +1. **Project Initialization** + - [ ] Create Next.js 14 project with TypeScript + - [ ] Install dependencies (TailwindCSS, shadcn/ui, React Query) + - [ ] Configure ESLint, Prettier + - [ ] Set up Git repository + +2. **Database Setup** + - [ ] Create Prisma schema from existing PostgreSQL schema + - [ ] Generate Prisma client + - [ ] Test read-only database connection + - [ ] Create database query helpers + +3. **Design System** + - [ ] Install shadcn/ui components + - [ ] Configure Tailwind theme + - [ ] Create color palette + - [ ] Set up typography + - [ ] Create base layout components (Header, Footer) + +4. **Authentication (Optional)** + - [ ] Set up NextAuth.js + - [ ] Create login page + - [ ] Implement simple password protection + - [ ] Add session management + +**Deliverables:** +- Working Next.js app with database connection +- Basic layout and navigation +- Authentication (if required) + +### Phase 2: Core Features (Week 3-4) +**Goal:** Photo gallery and basic navigation + +**Tasks:** +1. **Photo Grid** + - [ ] Create PhotoGrid component + - [ ] Implement infinite scroll + - [ ] Add lazy loading + - [ ] Create photo card with hover effects + +2. **Photo Detail Page** + - [ ] Create Lightbox component + - [ ] Add metadata panel + - [ ] Implement previous/next navigation + - [ ] Add keyboard shortcuts + +3. **Image Optimization** + - [ ] Configure Next.js Image component + - [ ] Generate blur placeholders + - [ ] Test responsive images + - [ ] Implement lazy loading + +4. **API Routes** + - [ ] Create `/api/photos` endpoint + - [ ] Create `/api/photos/[id]` endpoint + - [ ] Add pagination support + - [ ] Implement error handling + +**Deliverables:** +- Working photo gallery with infinite scroll +- Photo detail page with lightbox +- Optimized image loading + +### Phase 3: Search & Filtering (Week 5-6) +**Goal:** Comprehensive search functionality + +**Tasks:** +1. **People Search** + - [ ] Create people browser page + - [ ] Add people filter component + - [ ] Implement people autocomplete + - [ ] Create person detail page + +2. **Tag Search** + - [ ] Create tags browser page + - [ ] Add tag filter component + - [ ] Implement tag cloud + - [ ] Create tag detail page + +3. **Date Search** + - [ ] Add date range picker + - [ ] Implement date presets + - [ ] Create timeline view (optional) + +4. **Combined Search** + - [ ] Create search page + - [ ] Implement filter panel + - [ ] Add URL query parameter sync + - [ ] Create search results view + +**Deliverables:** +- Working search with all filter types +- People and tags browsers +- Combined search functionality + +### Phase 4: Polish & Optimization (Week 7-8) +**Goal:** Performance, UX, and visual polish + +**Tasks:** +1. **Performance Optimization** + - [ ] Audit Lighthouse scores + - [ ] Optimize bundle size + - [ ] Implement code splitting + - [ ] Add service worker (PWA) + +2. **UX Improvements** + - [ ] Add loading skeletons + - [ ] Implement error boundaries + - [ ] Add toast notifications + - [ ] Create empty states + +3. **Animations** + - [ ] Add page transitions + - [ ] Implement photo grid animations + - [ ] Add hover effects + - [ ] Create lightbox transitions + +4. **Responsive Design** + - [ ] Test on mobile devices + - [ ] Optimize touch interactions + - [ ] Add mobile navigation + - [ ] Test tablet layouts + +**Deliverables:** +- Lighthouse score > 90 +- Smooth animations and transitions +- Fully responsive design + +### Phase 5: Advanced Features (Week 9-10) +**Goal:** Nice-to-have features + +**Tasks:** +1. **User Features** + - [ ] Favorites system + - [ ] Create albums (optional) + - [ ] Share links + - [ ] Download photos (configurable) + +2. **Social Features** + - [ ] Comments on photos (optional) + - [ ] Photo ratings (optional) + - [ ] Share to social media + +3. **Advanced Search** + - [ ] Full-text search + - [ ] Save searches + - [ ] Search suggestions + - [ ] Similar photos + +4. **Admin Panel (Light)** + - [ ] Basic configuration + - [ ] View usage stats + - [ ] Manage access + +**Deliverables:** +- Enhanced user experience +- Social and sharing features +- Advanced search capabilities + +### Phase 6: Testing & Deployment (Week 11-12) +**Goal:** Production-ready application + +**Tasks:** +1. **Testing** + - [ ] Unit tests (Vitest) + - [ ] Integration tests (Playwright) + - [ ] Performance testing + - [ ] Security audit + +2. **Documentation** + - [ ] User guide + - [ ] Admin documentation + - [ ] API documentation + - [ ] Deployment guide + +3. **Deployment** + - [ ] Set up production environment + - [ ] Configure environment variables + - [ ] Deploy to Vercel/Railway/VPS + - [ ] Set up monitoring + +4. **Production Optimization** + - [ ] Configure CDN + - [ ] Set up error tracking (Sentry) + - [ ] Add analytics (optional) + - [ ] Create backup strategy + +**Deliverables:** +- Deployed, production-ready application +- Complete documentation +- Monitoring and analytics + +--- + +## 10. Deployment Options + +### 10.1 Vercel (Recommended - Easiest) + +**Pros:** +- Zero-config deployment for Next.js +- Automatic HTTPS +- Global CDN +- Image optimization built-in +- Free tier available + +**Cons:** +- Requires internet access to database +- Can be expensive for high traffic + +**Setup:** +```bash +# Install Vercel CLI +npm i -g vercel + +# Deploy +vercel deploy + +# Set environment variables +vercel env add DATABASE_URL_READONLY +vercel env add NEXTAUTH_SECRET +vercel env add NEXTAUTH_URL +``` + +**Configuration:** +```json +// vercel.json +{ + "buildCommand": "npm run build", + "outputDirectory": ".next", + "devCommand": "npm run dev", + "installCommand": "npm install", + "framework": "nextjs", + "regions": ["iad1"] +} +``` + +### 10.2 Docker (Self-Hosted) + +**Pros:** +- Full control +- Can run on local network +- One-time cost +- No vendor lock-in + +**Cons:** +- Requires server management +- Need to handle HTTPS, CDN + +**Dockerfile:** +```dockerfile +FROM node:20-alpine AS builder + +WORKDIR /app +COPY package*.json ./ +RUN npm ci + +COPY . . +RUN npx prisma generate +RUN npm run build + +FROM node:20-alpine AS runner +WORKDIR /app + +ENV NODE_ENV production + +COPY --from=builder /app/next.config.js ./ +COPY --from=builder /app/public ./public +COPY --from=builder /app/.next/standalone ./ +COPY --from=builder /app/.next/static ./.next/static + +EXPOSE 3000 +ENV PORT 3000 + +CMD ["node", "server.js"] +``` + +**docker-compose.yml:** +```yaml +version: '3.8' + +services: + viewer: + build: . + ports: + - "3000:3000" + environment: + - DATABASE_URL_READONLY=postgresql://viewer_readonly:password@postgres:5432/punimtag + - NEXTAUTH_SECRET=your_secret_here + - NEXTAUTH_URL=http://localhost:3000 + restart: unless-stopped + depends_on: + - postgres + + postgres: + image: postgres:16 + # Use existing PunimTag database + environment: + - POSTGRES_DB=punimtag + - POSTGRES_USER=punimtag + - POSTGRES_PASSWORD=punimtag_password + volumes: + - postgres_data:/var/lib/postgresql/data + +volumes: + postgres_data: +``` + +### 10.3 Railway + +**Pros:** +- Easy deployment from GitHub +- Built-in database support +- Affordable pricing +- Good for hobby projects + +**Cons:** +- Smaller network than Vercel +- Less mature platform + +### 10.4 Netlify + +**Pros:** +- Great for static sites +- Free tier +- Good CDN + +**Cons:** +- Less optimized for Next.js than Vercel +- Limited API route capabilities + +### 10.5 VPS (DigitalOcean, Linode, AWS EC2) + +**Pros:** +- Full control +- Predictable pricing +- Can run on private network + +**Cons:** +- Requires DevOps knowledge +- Manual scaling + +**Recommended:** Use Docker + Caddy for automatic HTTPS + +--- + +## 11. Configuration & Customization + +### 11.1 Environment Variables + +**.env.local:** +```bash +# Database (Read-Only) +DATABASE_URL_READONLY="postgresql://viewer_readonly:password@localhost:5432/punimtag" + +# Authentication +NEXTAUTH_SECRET="generate-with-openssl-rand-base64-32" +NEXTAUTH_URL="http://localhost:3000" + +# Optional: OAuth Providers +GOOGLE_ID="your-google-client-id" +GOOGLE_SECRET="your-google-client-secret" + +# Site Configuration +NEXT_PUBLIC_SITE_NAME="Family Photos" +NEXT_PUBLIC_SITE_DESCRIPTION="Our family photo collection" +NEXT_PUBLIC_REQUIRE_AUTH="true" + +# Feature Flags +NEXT_PUBLIC_ENABLE_DOWNLOADS="false" +NEXT_PUBLIC_ENABLE_FAVORITES="true" +NEXT_PUBLIC_ENABLE_COMMENTS="false" + +# Image Serving +NEXT_PUBLIC_PHOTO_BASE_PATH="/path/to/photos" + +# Optional: Analytics +NEXT_PUBLIC_ANALYTICS_ID="" + +# Optional: Sentry +SENTRY_DSN="" +``` + +### 11.2 Feature Flags + +**config/features.ts:** +```typescript +export const features = { + auth: { + enabled: process.env.NEXT_PUBLIC_REQUIRE_AUTH === "true", + providers: ['google', 'credentials'], + }, + photos: { + allowDownload: process.env.NEXT_PUBLIC_ENABLE_DOWNLOADS === "true", + maxDownloadSize: 1920, // max width/height for downloads + showExif: false, // Don't expose EXIF data in UI + }, + social: { + favorites: process.env.NEXT_PUBLIC_ENABLE_FAVORITES === "true", + comments: process.env.NEXT_PUBLIC_ENABLE_COMMENTS === "true", + sharing: true, + }, + search: { + fuzzySearch: true, + fullTextSearch: false, // Requires Elasticsearch + }, + ui: { + theme: 'system', // 'light' | 'dark' | 'system' + defaultView: 'grid', // 'grid' | 'masonry' | 'timeline' + photosPerPage: 30, + }, +}; +``` + +### 11.3 Branding Customization + +**config/branding.ts:** +```typescript +export const branding = { + siteName: process.env.NEXT_PUBLIC_SITE_NAME || "PunimTag Viewer", + siteDescription: process.env.NEXT_PUBLIC_SITE_DESCRIPTION || "Photo Gallery", + logo: "/logo.svg", + favicon: "/favicon.ico", + colors: { + primary: "#3b82f6", + secondary: "#8b5cf6", + }, + fonts: { + heading: "Inter", + body: "Inter", + }, +}; +``` + +--- + +## 12. Testing Strategy + +### 12.1 Unit Tests (Vitest) + +**Components:** +```typescript +// __tests__/components/PhotoCard.test.tsx +import { render, screen } from '@testing-library/react'; +import { PhotoCard } from '@/components/gallery/PhotoCard'; + +describe('PhotoCard', () => { + it('renders photo correctly', () => { + const photo = { + id: 1, + path: '/photos/test.jpg', + filename: 'test.jpg', + }; + + render(); + expect(screen.getByAlt('test.jpg')).toBeInTheDocument(); + }); +}); +``` + +**Utilities:** +```typescript +// __tests__/lib/queries.test.ts +import { searchPhotos } from '@/lib/queries'; + +describe('searchPhotos', () => { + it('filters by date range', async () => { + const results = await searchPhotos({ + dateFrom: new Date('2024-01-01'), + dateTo: new Date('2024-12-31'), + }); + + expect(results.length).toBeGreaterThan(0); + }); +}); +``` + +### 12.2 Integration Tests (Playwright) + +**E2E Tests:** +```typescript +// e2e/photo-gallery.spec.ts +import { test, expect } from '@playwright/test'; + +test('should load photo gallery', async ({ page }) => { + await page.goto('/'); + + // Wait for photos to load + await expect(page.locator('[data-testid="photo-card"]').first()).toBeVisible(); + + // Check infinite scroll + await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)); + await page.waitForTimeout(1000); + + const photoCount = await page.locator('[data-testid="photo-card"]').count(); + expect(photoCount).toBeGreaterThan(30); +}); + +test('should search by person', async ({ page }) => { + await page.goto('/'); + + // Open search + await page.click('[data-testid="search-button"]'); + + // Select person + await page.fill('[data-testid="people-search"]', 'John'); + await page.click('[data-testid="person-option-1"]'); + + // Apply filter + await page.click('[data-testid="apply-filters"]'); + + // Verify results + await expect(page.locator('[data-testid="person-badge"]')).toContainText('John'); +}); +``` + +### 12.3 Performance Tests + +**Lighthouse CI:** +```yaml +# .github/workflows/lighthouse.yml +name: Lighthouse CI +on: [push] + +jobs: + lighthouse: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + - run: npm ci + - run: npm run build + - run: npm run start & + - uses: treosh/lighthouse-ci-action@v9 + with: + urls: | + http://localhost:3000 + http://localhost:3000/people + http://localhost:3000/search + uploadArtifacts: true + temporaryPublicStorage: true +``` + +### 12.4 Load Testing (K6) + +```javascript +// load-test.js +import http from 'k6/http'; +import { check, sleep } from 'k6'; + +export let options = { + stages: [ + { duration: '1m', target: 50 }, + { duration: '3m', target: 50 }, + { duration: '1m', target: 0 }, + ], +}; + +export default function () { + // Test photo gallery + let res = http.get('http://localhost:3000/api/photos?page=1&pageSize=30'); + check(res, { + 'status is 200': (r) => r.status === 200, + 'response time < 200ms': (r) => r.timings.duration < 200, + }); + + sleep(1); +} +``` + +--- + +## 13. Monitoring & Analytics + +### 13.1 Error Tracking (Sentry) + +**Setup:** +```bash +npm install @sentry/nextjs +npx @sentry/wizard -i nextjs +``` + +**Configuration:** +```typescript +// sentry.client.config.ts +import * as Sentry from '@sentry/nextjs'; + +Sentry.init({ + dsn: process.env.SENTRY_DSN, + tracesSampleRate: 0.1, + environment: process.env.NODE_ENV, + beforeSend(event, hint) { + // Don't send errors in development + if (process.env.NODE_ENV === 'development') { + return null; + } + return event; + }, +}); +``` + +### 13.2 Analytics (Vercel Analytics or Plausible) + +**Vercel Analytics:** +```typescript +// app/layout.tsx +import { Analytics } from '@vercel/analytics/react'; + +export default function RootLayout({ children }) { + return ( + + + {children} + + + + ); +} +``` + +**Plausible (Privacy-Friendly):** +```typescript +// app/layout.tsx +import Script from 'next/script'; + +export default function RootLayout({ children }) { + return ( + + +