2025-10-07 00:23:17 -06:00
2025-10-07 00:23:17 -06:00
2025-10-07 00:23:17 -06:00
2025-10-29 15:58:55 -06:00
2025-10-29 15:58:55 -06:00
2025-10-23 03:29:01 +00:00
2025-10-29 15:58:55 -06:00
2025-10-20 21:13:06 -06:00
2025-09-16 21:36:20 -06:00
2025-09-16 21:36:20 -06:00
2025-09-16 21:36:20 -06:00
2025-10-29 15:58:55 -06:00
2025-09-16 21:36:20 -06:00
2025-09-16 21:36:20 -06:00
2025-09-16 21:36:20 -06:00
2025-10-07 01:58:10 -06:00

🎯 About The Project

United Tattoo Studio

United Tattoo is a comprehensive tattoo studio management platform designed for United Tattoo, a professional tattoo studio in Fountain, Colorado. This platform seamlessly integrates artist portfolios, appointment booking, flash tattoo marketplace, and real-time calendar synchronization—all running on Cloudflare's global edge network for exceptional performance.

Key Features

🎨 Artist Management

  • Dynamic portfolio galleries with Instagram integration
  • SEO-friendly artist pages (/artists/artist-name)
  • Specialty tracking and hourly rate management
  • Active/inactive status control
  • Self-service artist dashboard

Flash Tattoo Marketplace

  • Pre-drawn designs available for instant booking
  • Responsive carousel UI with smooth scrolling
  • Pricing and availability management
  • Direct integration with booking system

📅 Smart Booking System

  • Client appointment scheduling
  • Multiple status workflows (PENDING → CONFIRMED → IN_PROGRESS → COMPLETED)
  • Deposit and payment tracking
  • Real-time conflict detection

🔄 CalDAV/Nextcloud Integration

  • Bidirectional calendar sync with Nextcloud
  • Real-time availability checking
  • Per-artist calendar configuration
  • Sync logging and monitoring
  • Automatic conflict prevention

🔐 Nextcloud OAuth Authentication

  • Auto-provisioning based on Nextcloud groups
  • Role-based access control (SUPER_ADMIN, SHOP_ADMIN, ARTIST, CLIENT)
  • Seamless integration with existing artist portal
  • Emergency admin credentials fallback

📊 Comprehensive Dashboards

  • Admin Dashboard: Analytics, portfolio management, calendar oversight, artist management
  • Artist Dashboard: Profile editing, portfolio management, appointment view
  • File upload management with R2 cloud storage

💡 Why United Tattoo?

🌍 Edge-First Architecture Built on Cloudflare Workers for global performance with <100ms response times worldwide.

🔗 Nextcloud-First Integration Seamless authentication and calendar sync with existing artist infrastructure—no duplicate account management.

Zero-ORM Overhead Direct Cloudflare D1 integration via bindings for maximum performance.

📦 Bundle Size Enforced CI/CD enforces 3MB static budget to ensure lightning-fast page loads.

🧪 Test Coverage Comprehensive Vitest test suite with coverage tracking.

(back to top)


🛠️ Tech Stack

Core Framework

Next.js React TypeScript

Cloudflare Infrastructure

Cloudflare Workers OpenNext

UI & Styling

ShadCN UI Tailwind CSS Framer Motion

Database & Storage

Cloudflare D1 Cloudflare R2

Authentication & Integration

NextAuth.js CalDAV

Testing & Quality

Vitest ESLint Prettier

📦 Complete Dependency List

Core Dependencies:

  • Next.js 14.2.33 (App Router)
  • React 18
  • TypeScript 5
  • OpenNext for Cloudflare 1.8.2

UI Components:

  • 30+ Radix UI components (via ShadCN)
  • Framer Motion 12.23.24 (animations)
  • Lenis (smooth scrolling)
  • next-themes 0.4.6 (dark mode)

Data & State:

  • TanStack React Query 5.89.0
  • Zod 3.25.67 (validation)
  • React Hook Form 7.60.0

Calendar & CalDAV:

  • tsdav 2.1.5 (CalDAV client)
  • react-big-calendar 1.19.4
  • ical.js 1.5.0
  • date-fns

File Processing:

  • Sharp 0.34.4 (image processing)
  • heic-convert 2.1.0 (HEIC → JPEG)
  • AWS SDK for S3 3.890.0 (R2 compatible)

Testing:

  • Vitest 3.2.4
  • React Testing Library 16.3.0
  • @vitest/coverage-v8

(back to top)


🏗️ Architecture

graph TB
    subgraph "Client Layer"
        Browser[🌐 Browser]
    end

    subgraph "Edge Layer - Cloudflare Workers"
        NextJS[Next.js 14<br/>App Router]
        OpenNext[OpenNext<br/>Adapter]
    end

    subgraph "Cloudflare Services"
        D1[(D1 Database<br/>SQLite)]
        R2[R2 Storage<br/>Files & Images]
    end

    subgraph "External Services"
        Nextcloud[Nextcloud<br/>OAuth + CalDAV]
    end

    Browser --> NextJS
    NextJS --> OpenNext
    OpenNext --> D1
    OpenNext --> R2
    OpenNext <--> Nextcloud

    style Browser fill:#667eea,color:#fff
    style NextJS fill:#000,color:#fff
    style OpenNext fill:#f38020,color:#fff
    style D1 fill:#f38020,color:#fff
    style R2 fill:#f38020,color:#fff
    style Nextcloud fill:#0082c9,color:#fff

🔄 System Flow

Authentication Flow
User Request → NextAuth.js
    ↓
    ├─ Nextcloud OAuth (Primary)
    │   ├─ Check user groups (via OCS API)
    │   ├─ Assign role based on group
    │   │   ├─ "admins" → SUPER_ADMIN
    │   │   ├─ "shop_admins" → SHOP_ADMIN
    │   │   ├─ "artists" → ARTIST (auto-create profile)
    │   │   └─ Other → Deny access
    │   └─ Create/update user in D1
    │
    ├─ Credentials (Fallback - Admin Only)
    │   ├─ Query parameter: ?admin=true
    │   ├─ Email/password validation
    │   └─ Dev mode: Auto-create SUPER_ADMIN
    │
    └─ Session Created (JWT)
CalDAV Sync Flow
Appointment Created/Updated
    ↓
syncAppointmentToCalendar()
    ↓
    ├─ Get artist's Nextcloud calendar config
    ├─ Connect to CalDAV server
    ├─ Create/update VEVENT
    ├─ Store calendar_event_uid in D1
    └─ Log sync operation

Background Sync (Periodic)
    ↓
pullCalendarEventsToDatabase()
    ↓
    ├─ Fetch events from CalDAV
    ├─ Compare with D1 appointments
    ├─ Update changed appointments
    ├─ Import external events
    └─ Log sync results
Database Schema

Core Tables:

Table Description Key Fields
users Authentication & profiles id, email, name, role, nextcloud_user_id
artists Artist profiles id, user_id, slug, bio, specialties (JSON), hourly_rate
portfolio_images Artist work gallery id, artist_id, image_url, tags (JSON), order, is_public
flash_items Pre-drawn designs id, artist_id, title, price, is_available
appointments Booking system id, artist_id, client_id, status, start_time, end_time, deposit
availability Artist schedules id, artist_id, day_of_week, start_time, end_time
artist_calendars CalDAV configuration id, artist_id, calendar_url, username, password
calendar_sync_logs Sync monitoring id, artist_id, operation, status, details
site_settings Global config key, value, type
file_uploads R2 file tracking id, user_id, file_url, file_size, mime_type

Indexes: Optimized for artist lookups, appointment queries, and calendar sync operations.

📁 Project Structure

united-tattoo/
├── app/                      # Next.js App Router
│   ├── (public)/            # Public pages (no auth)
│   │   ├── artists/         # Artist profiles & portfolios
│   │   ├── book/            # Booking pages
│   │   └── page.tsx         # Homepage
│   ├── admin/               # Admin dashboard (SUPER_ADMIN, SHOP_ADMIN)
│   ├── artist-dashboard/    # Artist self-service (ARTIST)
│   ├── api/                 # API routes
│   │   ├── artists/         # Artist CRUD
│   │   ├── appointments/    # Booking endpoints
│   │   ├── caldav/          # Calendar sync
│   │   ├── flash/           # Flash items
│   │   └── upload/          # R2 file uploads
│   └── auth/                # NextAuth pages
├── lib/                     # Core logic
│   ├── db.ts               # D1 database functions
│   ├── auth.ts             # NextAuth config + helpers
│   ├── caldav-client.ts    # CalDAV integration
│   ├── calendar-sync.ts    # Sync logic
│   ├── nextcloud-client.ts # Nextcloud API client
│   ├── r2-upload.ts        # R2 file handling
│   └── env.ts              # Environment validation
├── components/              # React components
│   ├── ui/                 # ShadCN components
│   └── ...                 # Feature components
├── sql/                     # Database
│   ├── schema.sql          # Main schema
│   └── migrations/         # Migration files
├── docs/                    # Documentation
├── .gitea/workflows/       # CI/CD pipelines
└── wrangler.toml           # Cloudflare config

(back to top)


🚀 Getting Started

📋 Prerequisites

⚠️ Required Accounts & Access

Before starting, ensure you have:

  • Cloudflare Account with access to Workers, D1, R2, and Pages
  • Nextcloud Instance with admin access for OAuth app creation
  • Node.js 18+ and npm installed
  • Wrangler CLI version 3+

Install Wrangler:

npm install -g wrangler

Cloudflare Resources Required:

  • Workers & Pages: For hosting the application
  • D1 Database: SQLite database (named united-tattoo)
  • R2 Buckets: File storage (united-tattoo, united-tattoo-inc-cache)

⚙️ Installation

  1. Clone the repository

    git clone https://git.biohazardvfx.com/nicholai/united-tattoo.git
    cd united-tattoo
    
  2. Install dependencies

    npm install
    
  3. Configure environment variables

    cp .env.example .env.local
    # Edit .env.local with your credentials (see Environment Variables section)
    
  4. Set up Cloudflare D1 database

    # Create D1 database (if not exists)
    wrangler d1 create united-tattoo
    
    # Apply schema to local D1
    npm run db:migrate:local
    
    # Apply schema to preview/production
    npm run db:migrate:latest:preview
    
  5. Configure Nextcloud OAuth

    See docs/NEXTCLOUD-OAUTH-SETUP.md for detailed instructions on:

    • Creating OAuth application in Nextcloud
    • Configuring group-based role assignment
    • Setting up service account for CalDAV
  6. Run locally

    # Next.js dev server (port 3000)
    npm run dev
    
    # OR with Cloudflare Workers simulation
    npm run dev:wrangler
    
  7. Access the application

    • Local: http://localhost:3000
    • Sign in: http://localhost:3000/auth/signin
    • Admin signin: http://localhost:3000/auth/signin?admin=true

(back to top)

🔐 Environment Variables

📝 Required Variables
Variable Description Example
Database
DATABASE_URL PostgreSQL URL (legacy, using D1 via bindings) postgresql://...
DIRECT_URL Direct database connection (optional) postgresql://...
Authentication
NEXTAUTH_URL Application URL https://united-tattoos.com
NEXTAUTH_SECRET NextAuth secret key Generate with openssl rand -base64 32
File Storage (Cloudflare R2)
AWS_ACCESS_KEY_ID R2 access key ID From Cloudflare dashboard
AWS_SECRET_ACCESS_KEY R2 secret access key From Cloudflare dashboard
AWS_REGION Region (any valid AWS region) us-east-1
AWS_BUCKET_NAME R2 bucket name united-tattoo
AWS_ENDPOINT_URL R2 endpoint URL https://<account-id>.r2.cloudflarestorage.com
Nextcloud OAuth
NEXTCLOUD_BASE_URL Nextcloud instance URL https://portal.united-tattoos.com
NEXTCLOUD_OAUTH_CLIENT_ID OAuth app client ID From Nextcloud admin
NEXTCLOUD_OAUTH_CLIENT_SECRET OAuth app client secret From Nextcloud admin
NEXTCLOUD_ARTISTS_GROUP Group name for artists artists
NEXTCLOUD_ADMINS_GROUP Group name for shop admins shop_admins
🔧 Optional Variables
Variable Description Default
CalDAV (Calendar Sync)
NEXTCLOUD_USERNAME Service account username
NEXTCLOUD_PASSWORD Service account password/app password
NEXTCLOUD_CALENDAR_BASE_PATH CalDAV base path /remote.php/dav/calendars
OAuth Providers (Deprecated)
GOOGLE_CLIENT_ID Google OAuth client ID
GOOGLE_CLIENT_SECRET Google OAuth client secret
GITHUB_CLIENT_ID GitHub OAuth client ID
GITHUB_CLIENT_SECRET GitHub OAuth client secret
Migration
MIGRATE_TOKEN Token for public migration endpoint Generate random string
Analytics
VERCEL_ANALYTICS_ID Vercel Analytics ID
💡 Pro Tip: Use .env.local for local development and configure production variables in Cloudflare dashboard under Settings → Environment Variables.

(back to top)


💻 Development

⌨️ Common Commands

Command Description
🔧 Development
npm run dev Start Next.js dev server (port 3000)
npm run dev:wrangler Build and preview with OpenNext/Cloudflare
🧪 Testing
npm run test Run Vitest in watch mode
npm run test:ui Run Vitest with interactive UI
npm run test:run Run tests once (CI mode)
npm run test:coverage Run tests with coverage report
🏗️ Build & Deployment
npm run pages:build Build with OpenNext for Cloudflare
npm run build Standard Next.js build (standalone)
npm run preview Preview OpenNext build locally
npm run deploy Deploy to Cloudflare Pages
🔍 Code Quality
npm run ci:lint Run ESLint
npm run ci:typecheck TypeScript type checking (noEmit)
npm run ci:test Run tests with coverage (CI)
npm run ci:build Build for production (CI)
npm run ci:budgets Check bundle size budgets
Formatting
npm run lint Run ESLint
npm run format Format code with Prettier
npm run format:check Check formatting without changing files

🗄️ Database Management

📦 Migration Commands

Local Database:

# Apply schema to local D1
npm run db:migrate:local

# View tables in local D1
npm run db:studio:local

# Backup local database
npm run db:backup:local

Preview Environment (default):

# Apply schema to preview D1
npm run db:migrate

# Apply all migrations from sql/migrations/
npm run db:migrate:latest:preview

# View tables in preview D1
npm run db:studio

# Backup preview database
npm run db:backup

Production Environment:

# Apply specific migration to production
npm run db:migrate:up:prod

# Apply all migrations to production
npm run db:migrate:latest:prod

Direct Wrangler Commands:

# Execute SQL query on local D1
wrangler d1 execute united-tattoo --local --command="SELECT * FROM artists"

# Apply schema file
wrangler d1 execute united-tattoo --file=./sql/schema.sql

# Execute with preview (remote)
wrangler d1 execute united-tattoo --command="SELECT * FROM users"
🔄 Creating New Migrations
  1. Create migration file in sql/migrations/ with format:

    YYYYMMDD_NNNN_description.sql
    

    Example: 20250130_0001_add_flash_items_table.sql

  2. Write your SQL migration:

    -- Add flash_items table
    CREATE TABLE IF NOT EXISTS flash_items (
      id TEXT PRIMARY KEY,
      artist_id TEXT NOT NULL,
      title TEXT NOT NULL,
      price REAL NOT NULL,
      is_available INTEGER DEFAULT 1,
      created_at TEXT DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (artist_id) REFERENCES artists(id) ON DELETE CASCADE
    );
    
  3. Test locally:

    npm run db:migrate:local
    
  4. Apply to preview:

    npm run db:migrate:latest:preview
    
  5. Apply to production (when ready):

    npm run db:migrate:latest:prod
    

(back to top)


🌍 Deployment

Production URL: https://united-tattoos.com

🚀 Deployment Process

Quick Deploy Command

npm run pages:build && wrangler deploy

Step-by-Step:

  1. Build with OpenNext

    npm run pages:build
    

    This creates a Cloudflare-compatible build in .vercel/output/static

  2. Deploy to Cloudflare Pages

    wrangler pages deploy .vercel/output/static
    
  3. Verify deployment

🔄 CI/CD Pipeline

The project uses Gitea workflows for automated CI/CD:

Workflows:

  • ci.yaml - Main CI pipeline

    • ESLint
    • TypeScript type checking
    • Vitest tests with coverage
    • Production build
    • Bundle size budgets
  • deploy.yaml - Automated deployments

    • Triggers on push to main branch
    • Builds and deploys to Cloudflare
  • security.yaml - Security audits

    • npm audit
    • Dependency vulnerability scanning
  • performance.yaml - Performance checks

    • Bundle size analysis
    • Preview smoke tests

Bundle Size Budgets:

  • Total static assets: 3MB max
  • Individual assets: 1.5MB max

Enforced by scripts/budgets.mjs in CI.

🐳 Docker Support

🐋 Docker Deployment (Alternative)

The project includes a Dockerfile for self-hosting:

# Build image
docker build -t united-tattoo:latest .

# Run container
docker run --rm -p 3000:3000 \
  -e PORT=3000 \
  -e NEXTAUTH_URL=http://localhost:3000 \
  -e NEXTAUTH_SECRET=your-secret \
  # ... other env vars
  united-tattoo:latest

Note: Docker deployment bypasses Cloudflare Workers and uses Next.js standalone mode. For production, Cloudflare deployment is recommended.

(back to top)


📚 Documentation

Comprehensive documentation is available in the docs/ directory:

🔑 Key Documentation

Document Description
📘 NEXTCLOUD-OAUTH-SETUP.md Complete guide to setting up Nextcloud OAuth and group-based authentication
📗 CALDAV-SETUP.md Instructions for configuring CalDAV calendar synchronization
📙 CI-CD-PIPELINE.md Detailed CI/CD pipeline documentation and troubleshooting
📕 BOOKING-WORKFLOW-FINAL-PLAN.md Complete booking system architecture and workflow

📑 Additional Documentation

View All Documentation Files

Authentication & Integration:

Booking & Calendar:

CI/CD & DevOps:

Performance & SEO:

Project Management:

🤖 AI Development Guide

The project includes CLAUDE.md with comprehensive instructions for AI assistants (like Claude Code) working with this codebase, including:

  • Complete architecture overview
  • Common commands reference
  • Database layer patterns
  • Authentication flows
  • CalDAV integration details
  • Development best practices

(back to top)


🗺️ Roadmap

Completed Features

  • Artist portfolio system with galleries
  • Nextcloud OAuth with auto-provisioning
  • CalDAV bidirectional sync
  • Flash tattoo marketplace
  • Admin dashboard with analytics
  • Artist self-service dashboard
  • Appointment booking system
  • R2 file storage integration
  • Role-based access control
  • CI/CD pipeline with Gitea
  • Bundle size enforcement
  • HEIC image conversion
  • Artist slug-based URLs

🚧 In Progress

  • Email notifications for appointments
  • SMS reminders for clients
  • Advanced calendar conflict resolution
  • Payment integration (Stripe/Square)
  • Gift card system enhancements
  • Enhanced analytics dashboard

💡 Future Enhancements

  • Client self-service portal
  • Online deposit payments
  • Artist earnings reports
  • Inventory management
  • Social media auto-posting
  • Mobile app (React Native)
  • Webhook integrations
  • Advanced reporting

See the open issues for a full list of proposed features and known issues.

(back to top)


🤝 Contributing

Contributions are welcome! This project follows standard Git workflows and conventional commits.

Development Workflow

  1. Fork the project

    # Via Gitea UI or git clone
    git clone https://git.biohazardvfx.com/nicholai/united-tattoo.git
    
  2. Create your feature branch

    git checkout -b feat/amazing-feature
    
  3. Make your changes

    • Follow existing code style (enforced by ESLint/Prettier)
    • Add tests for new features
    • Update documentation as needed
  4. Run quality checks

    npm run lint              # Check linting
    npm run format            # Format code
    npm run ci:typecheck      # Check types
    npm run test:run          # Run tests
    npm run ci:budgets        # Check bundle sizes
    
  5. Commit your changes

    git add .
    git commit -m "feat: add amazing feature"
    

    Use Conventional Commits format:

    • feat: New feature
    • fix: Bug fix
    • docs: Documentation changes
    • style: Formatting, missing semicolons, etc.
    • refactor: Code refactoring
    • test: Adding tests
    • chore: Maintenance tasks
  6. Push to your branch

    git push origin feat/amazing-feature
    
  7. Open a Pull Request

    • Via Gitea UI
    • Provide clear description of changes
    • Reference any related issues

Code Style Guidelines

  • TypeScript: Prefer strict typing, avoid any
  • React: Use functional components with hooks
  • File organization: Keep components modular
  • Comments: Explain "why", not "what"
  • Tests: Test user-facing behavior, not implementation

Top Contributors

Contributors

(back to top)


📄 License

⚠️ License Status

This project currently does not have a LICENSE file in the repository. If you intend to use GNU GPLv3 (as referenced in the original README template), please add a LICENSE or COPYING file with the full license text.

Alternatively, consider:

  • MIT License - Permissive, allows commercial use
  • Apache 2.0 - Permissive with patent grant
  • GNU GPLv3 - Copyleft, requires source disclosure
  • Proprietary - All rights reserved

Choose A License can help you decide.

Current Status: No license specified. Please add a LICENSE file to clarify usage terms.

(back to top)


📬 Contact


Star this repository if you find it helpful!

(back to top)


Made with ❤️ for United Tattoo Studio, Fountain, CO

Description
United Tattoo's website - made in nextjs
Readme 812 MiB
Languages
TypeScript 95.4%
Shell 2.1%
CSS 1.3%
JavaScript 1.1%