commit b4e89ea9ee914bf6025593fd409d2a3e71ff67ec
Author: Philip
Date: Tue Feb 17 17:34:50 2026 -0800
Initial commit: Next.js rewrite of Super Bowl Squares app
Full rewrite of the legacy PHP/MySQL app using Next.js 14, PostgreSQL,
Prisma, NextAuth, Tailwind CSS, and WebSocket-based live chat/grid updates.
Deployed via Docker Compose with a custom Node.js server for WebSocket support.
Fix chat display names by passing userId from the NextAuth session over
WebSocket instead of attempting to read the HttpOnly session cookie (which
is inaccessible to JavaScript). Server now looks up the user's first name
from the database using the userId.
Co-Authored-By: Claude Sonnet 4.6
diff --git a/.claude/settings.local.json b/.claude/settings.local.json
new file mode 100644
index 0000000..3a791c2
--- /dev/null
+++ b/.claude/settings.local.json
@@ -0,0 +1,25 @@
+{
+ "permissions": {
+ "allow": [
+ "Bash(npm install:*)",
+ "Bash(npx next build:*)",
+ "Bash(git init:*)",
+ "Bash(git add:*)",
+ "Bash(git rm:*)",
+ "Bash(docker-compose up:*)",
+ "Bash(docker compose:*)",
+ "Bash(docker version:*)",
+ "Bash(sudo apt-get update:*)",
+ "Bash(chmod:*)",
+ "Bash(dpkg:*)",
+ "Bash(docker:*)",
+ "Bash(curl:*)",
+ "Bash(ss:*)",
+ "Bash(echo:*)",
+ "Bash(iptables:*)",
+ "Bash(npx tsc:*)",
+ "Bash(npx prisma generate:*)",
+ "Bash(timeout 3 node:*)"
+ ]
+ }
+}
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..eed3ff6
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,7 @@
+node_modules
+.next
+.git
+legacy
+*.md
+.env
+.env.local
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..5d1197a
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,11 @@
+# PostgreSQL
+POSTGRES_USER=superbowl
+POSTGRES_PASSWORD=superbowl
+POSTGRES_DB=superbowl
+
+# Database connection (app container talks to "db" service)
+DATABASE_URL="postgresql://superbowl:superbowl@db:5432/superbowl?schema=public"
+
+# NextAuth
+NEXTAUTH_SECRET="change-me-to-a-random-secret"
+NEXTAUTH_URL="http://localhost:3000"
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..104b71f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,34 @@
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# local env files
+.env
+.env*.local
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..c3418cd
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,198 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Project Overview
+
+Super Bowl Squares — a web application that runs a 10x10 grid-based betting game for the Super Bowl. Players purchase squares, random numbers (0-9) are assigned to each row/column for AFC and NFC teams, and winners are determined by the last digit of each team's score at the end of each quarter.
+
+This is a rewrite of a legacy PHP/MySQL application (preserved in `legacy/` for reference). The legacy app has significant security issues (SQL injection, plaintext passwords, no CSRF protection) and uses early-2000s HTML patterns.
+
+## Tech Stack
+
+- **Framework**: Next.js 14 (App Router) with TypeScript
+- **Database**: PostgreSQL 16 via Prisma ORM
+- **Auth**: NextAuth.js with JWT strategy and credentials provider
+- **Styling**: Tailwind CSS with custom theme (dark mode, `primary`/`accent` color scales, glow shadows)
+- **Real-time**: WebSocket server (ws) for live chat, mounted at `/ws/chat`
+- **Deployment**: Docker Compose (app + postgres)
+
+## Development Commands
+
+```bash
+# Start everything (build + run with Docker Compose)
+docker compose up --build -d
+
+# View logs
+docker compose logs app --tail 50
+docker compose logs app -f # follow
+
+# Rebuild after code changes
+docker compose up --build -d
+
+# Stop
+docker compose down
+
+# Database operations (run from host with node_modules present)
+npx prisma db push # sync schema to DB
+npx prisma generate # regenerate client after schema changes
+npx tsx prisma/seed.ts # seed default data
+npm run db:migrate # deploy prisma migrations (production)
+npm run db:generate # regenerate Prisma client (alias)
+
+# Dev server (requires local postgres with DATABASE_URL pointing to it)
+npm run dev # runs tsx server.ts -> server.js
+```
+
+**Note**: The app runs inside Docker where `DATABASE_URL` points to the `db` service. For local dev outside Docker, update `DATABASE_URL` to `postgresql://superbowl:superbowl@localhost:5432/superbowl`.
+
+## Environment Variables
+
+Required variables (see `.env.example`):
+- `DATABASE_URL` — PostgreSQL connection string
+- `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB` — Database credentials
+- `NEXTAUTH_SECRET` — Random secret for JWT signing (generate with `openssl rand -base64 32`)
+- `NEXTAUTH_URL` — Full URL where app is deployed (e.g., `http://localhost:3000`)
+
+## Architecture
+
+### Custom Server (`server.js`)
+
+Next.js runs behind a custom HTTP server that also handles WebSocket upgrades. This is required for the live chat and real-time grid update features. The server has two boot modes:
+
+- **Dev mode** (`NODE_ENV !== 'production'`): Uses the standard `next()` API with `app.prepare()`, creates its own HTTP server, and adds WebSocket upgrade handling on `/ws/chat`.
+- **Production standalone mode**: Monkey-patches `http.createServer` to intercept the HTTP server that Next.js's `startServer()` creates, injecting WebSocket upgrade handling for `/ws/chat` before Next.js registers its own upgrade handler. Reads the embedded `nextConfig` from `server.standalone.js` (saved during Docker build) and sets `__NEXT_PRIVATE_STANDALONE_CONFIG` env var so Next.js skips webpack loading.
+
+The server also runs:
+- Chat message broadcasting with blacklist filtering and JWT token decoding for user identity
+- `squares:changed` → `squares:refresh` broadcast for real-time grid updates
+- Payment reminder scheduler (15-minute interval) that checks unconfirmed squares approaching grace period deadline
+
+`server.ts` is just a dev shim that requires `server.js`.
+
+**Critical Dockerfile detail**: The standalone build output includes its own `server.js`. The Dockerfile saves it as `server.standalone.js` then copies our custom `server.js` over it. Extra node_modules not included in standalone output (`ws`, `nodemailer`, `next-auth`, `jose`, `@panva`, `uuid`, `@babel`, `preact`, `oauth`, `openid-client`, `cookie`) are explicitly copied in the Dockerfile.
+
+### Authentication Flow
+
+- `src/lib/auth.ts` — NextAuth config with credentials provider (email/password, bcrypt)
+- `src/middleware.ts` — Route protection: public routes (`/`, `/login`, `/register`, `/signup`, `/setup`, `/api/auth`, `/api/setup`), admin routes require ADMIN or VIEWER role, `/my-squares` requires any authenticated user
+- `src/types/index.ts` — Augments NextAuth session/JWT types with `role` and `id`
+- JWT tokens carry `role` (ADMIN/VIEWER/PLAYER) and `id` fields
+
+### Database Pattern
+
+- Prisma schema at `prisma/schema.prisma`
+- Singleton pattern: `GameSettings`, `Score`, and `EmailSettings` use `id: "singleton"` — there's always exactly one row
+- `prisma/seed.ts` — Seeds 100 squares (positions "00"-"99"), default settings, default score row, email settings, and 8 email templates (welcome, square_confirmation, square_confirmed, square_released, payment_reminder, numbers_assigned, game_results, custom). Uses upserts so it's idempotent. Email templates use `update: {}` to preserve manual edits (only creates, never overwrites).
+- Shared Prisma client at `src/lib/prisma.ts` (global singleton to avoid connection exhaustion in dev)
+
+### Page Structure
+
+- `/` — Main page: 10x10 grid + chat window. Redirects to `/setup` if no admin exists.
+- `/setup` — First-run admin account creation
+- `/login`, `/register` — Auth pages
+- `/signup?squares=01,02,...` — Square purchase form (guest or authenticated)
+- `/my-squares` — Player's own squares (requires auth)
+- `/admin/*` — Admin panel with sidebar nav (`layout.tsx` is a client component with role-based nav filtering)
+
+Admin sub-pages: dashboard, squares, numbers, scores, balance, settings, users, email, chat, backup. Viewers are restricted from settings, users, and backup.
+
+### API Routes (`src/app/api/`)
+
+All API routes use `getServerSession(authOptions)` for auth checks. Pattern: GET for reads, POST for creates, PUT for full updates, PATCH for partial updates.
+
+Key routes:
+- `squares/` — GET (list all), POST (purchase), PATCH (admin: confirm/release/reserve/bulk-reserve/edit)
+- `settings/` — GET (public), PUT (admin-only, handles payment methods separately)
+- `numbers/` — POST generates random 0-9 shuffles for NFC/AFC axes
+- `scores/` — PUT updates quarter scores and determines winners
+- `backup/` — Export/import game data
+- `upload/` — Image upload for team/SB logos
+
+### Component Patterns
+
+- Server components fetch data directly via Prisma (e.g., `page.tsx` for `/`)
+- Client components use `'use client'` and fetch via API routes
+- `src/components/Providers.tsx` wraps app with `SessionProvider` + `WebSocketProvider` + `ToastProvider`
+- Grid components: `SquareGrid` (10x10 table with real-time WS refresh), `SquareCell` (individual cell), `GridHeader` (team logos/matchup banner/event info)
+- `src/lib/ws.tsx` — React Context `WebSocketProvider` shares a single WebSocket connection app-wide. Exports `useWebSocket()` hook returning `{ messages, connected, sendMessage, deleteMessage, squaresVersion, notifySquaresChanged }`. The `squaresVersion` counter increments on `squares:refresh` events; components watch it to re-fetch grid data.
+
+### Styling Conventions
+
+- Dark theme throughout: `bg-gray-950` base, `bg-gray-900` cards
+- Custom Tailwind classes in `globals.css`: `.btn-primary`, `.btn-secondary`, `.btn-danger`, `.btn-success`, `.input-field`, `.card`, `.card-elevated`
+- Custom color scales: `primary` (blue), `accent` (purple), `success`, `warning`, `danger`
+- Custom glow shadows: `shadow-glow-sm`, `shadow-glow`, `shadow-glow-lg`, `shadow-glow-green`, `shadow-glow-amber`
+
+### Docker Setup
+
+- `Dockerfile` — Multi-stage build: builder (npm install, next build, compile seed script) -> runner (standalone output, prisma CLI for runtime migrations). The runner stage saves the original standalone `server.js` as `server.standalone.js` then copies our custom `server.js` over it, and copies extra node_modules not included in standalone output.
+- `docker-entrypoint.sh` — Runs `prisma db push`, seeds DB, then starts `node server.js`
+- `docker-compose.yml` — Two services: `app` (port 3000) and `db` (postgres:16-alpine, port 5432, healthcheck)
+
+### Next.js Configuration
+
+`next.config.js` settings:
+- `output: 'standalone'` — Minimal production build with embedded dependencies for Docker
+- `serverComponentsExternalPackages: ['nodemailer']` — Prevents bundling nodemailer (requires native modules)
+- `images: { unoptimized: true }` — Disables Next.js image optimization for simpler Docker deployment
+
+### Real-time Updates
+
+Two real-time channels share a single WebSocket connection per client:
+- **Chat**: Messages are stored in DB and broadcast to all connected clients
+- **Grid refresh**: When squares change (purchase, admin action), the acting client sends `{type: 'squares:changed'}` via WS. The server broadcasts `{type: 'squares:refresh'}` to all clients. Clients increment `squaresVersion` which triggers a re-fetch of `/api/squares`.
+
+### Path Alias
+
+`@/*` maps to `./src/*` (configured in `tsconfig.json`). Use `@/lib/prisma`, `@/components/ui/Button`, etc.
+
+## Game Logic
+
+- 100 squares in a 10x10 grid, positions "00" through "99" (row digit + column digit)
+- Row = NFC team axis, Column = AFC team axis
+- Numbers (0-9) are randomly shuffled independently for each axis; can only be generated when all 100 squares are claimed
+- Winners: last digit of each team's quarter score maps to the grid number → intersecting square wins
+- Four quarters: Q1 (first), Q2 (half), Q3 (third), Final — each with configurable payout percentage
+- Max 10 squares per purchase submission
+- Squares can be guest-purchased (name+email) or purchased by authenticated players
+
+## Feature Requirements
+
+### Role-Based Access (3 roles)
+
+**Admin** (full control): manage all accounts, game settings (bet amount, teams, logos, payouts, payment methods, rules), generate numbers, edit squares, edit scores, email system, backup/restore, chat moderation
+
+**Viewer** (limited admin — for co-commissioners): view balance sheet, edit squares, update scores, send emails, moderate chat
+
+**Player** (authenticated participant): view own squares, purchase squares, live chat, change own name/password
+
+### Email System
+- Templates with `{{placeholder}}` variables (stored in DB, seeded with defaults)
+- Configurable SMTP/SSL transport via `EmailSettings`
+- `src/lib/email.ts` — `sendEmail()`, `renderTemplate()`, `getTransporter()` core functions
+- `src/lib/autoEmail.ts` — Fire-and-forget automated emails triggered by game events:
+ - `sendWelcomeEmail` — on user registration (from `POST /api/users`)
+ - `sendPurchaseConfirmationEmail` — on square purchase (from `POST /api/squares`)
+ - `sendSquareConfirmedEmail` / `sendSquareReleasedEmail` — on admin confirm/release (from `PATCH /api/squares`)
+ - `sendNumbersAssignedEmails` — to all participants when numbers generated (from `POST /api/numbers`)
+ - `sendGameResultsEmails` — to all participants when final score entered (from `PUT /api/scores`)
+ - `checkPaymentReminders` — scheduled in server.js every 15 minutes, sends reminders 2 hours before grace period deadline
+- Admin Send tab (`POST /api/email`) populates all template variables per-recipient from DB (squares, amount, payment info, winners)
+- Available template variables: `{{name}}`, `{{email}}`, `{{squares}}`, `{{amountDue}}`, `{{commissioner}}`, `{{eventName}}`, `{{gameUrl}}`, `{{graceHours}}`, `{{paymentInstructions}}`, `{{paymentMethods}}`, `{{winners}}`, `{{rulesText}}`
+
+### Live Chat
+- WebSocket-based, embedded on front page (YouTube/Twitch live-chat style)
+- Blacklist-based word filtering
+- Admin/viewer can delete messages
+
+### Backup/Restore
+- Config-only or full (config + square/game data) export/import
+
+## Testing
+
+No testing framework is currently configured. To add tests, consider installing Jest or Vitest with React Testing Library.
+
+## Assets
+
+Team logos in `public/images/`: `afc-{team}.png` and `nfc-{team}.png` for all 32 NFL teams, plus conference logos, generic placeholders, Super Bowl event logos, and background images.
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..fd25a4f
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,72 @@
+FROM node:20-alpine AS base
+
+# --- Builder ---
+FROM base AS builder
+WORKDIR /app
+
+COPY package.json package-lock.json* ./
+COPY prisma ./prisma/
+RUN npm install
+
+COPY . .
+RUN npm run build
+
+# Compile seed script to JS so the runner doesn't need tsx
+RUN npx tsx --compile prisma/seed.ts > /dev/null 2>&1 || true
+RUN test -f prisma/seed.js || npx esbuild prisma/seed.ts --bundle --platform=node --outfile=prisma/seed.js --external:@prisma/client 2>/dev/null || true
+
+# --- Runner ---
+FROM base AS runner
+WORKDIR /app
+
+ENV NODE_ENV=production
+
+RUN addgroup --system --gid 1001 nodejs
+RUN adduser --system --uid 1001 nextjs
+
+# Install prisma CLI for db push at runtime
+RUN npm install -g prisma@6
+
+COPY --from=builder /app/public ./public
+COPY --from=builder /app/prisma ./prisma
+COPY --from=builder /app/docker-entrypoint.sh ./docker-entrypoint.sh
+
+# Copy standalone output (includes node_modules with @prisma/client)
+COPY --from=builder /app/.next/standalone ./
+COPY --from=builder /app/.next/static ./.next/static
+
+# Save the original standalone server.js (contains embedded nextConfig)
+# then override with our custom server that adds WebSocket support
+RUN cp /app/server.js /app/server.standalone.js
+COPY --from=builder /app/server.js ./server.js
+
+# Copy modules needed by custom server (not included in standalone output)
+COPY --from=builder /app/node_modules/ws ./node_modules/ws
+COPY --from=builder /app/node_modules/nodemailer ./node_modules/nodemailer
+# next-auth/jwt for WebSocket chat user identification
+COPY --from=builder /app/node_modules/next-auth ./node_modules/next-auth
+COPY --from=builder /app/node_modules/jose ./node_modules/jose
+COPY --from=builder /app/node_modules/@panva ./node_modules/@panva
+COPY --from=builder /app/node_modules/uuid ./node_modules/uuid
+COPY --from=builder /app/node_modules/@babel ./node_modules/@babel
+COPY --from=builder /app/node_modules/preact ./node_modules/preact
+COPY --from=builder /app/node_modules/preact-render-to-string ./node_modules/preact-render-to-string
+COPY --from=builder /app/node_modules/oauth ./node_modules/oauth
+COPY --from=builder /app/node_modules/openid-client ./node_modules/openid-client
+COPY --from=builder /app/node_modules/cookie ./node_modules/cookie
+
+# Seed script
+COPY --from=builder /app/prisma/seed.js ./prisma/seed.js
+
+# Fix Windows line endings and make executable
+RUN sed -i 's/\r$//' ./docker-entrypoint.sh && chmod +x ./docker-entrypoint.sh
+RUN chown -R nextjs:nodejs /app
+
+USER nextjs
+
+EXPOSE 3000
+
+ENV PORT=3000
+ENV HOSTNAME="0.0.0.0"
+
+ENTRYPOINT ["./docker-entrypoint.sh"]
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..7813234
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,27 @@
+services:
+ app:
+ build: .
+ network_mode: host
+ env_file: .env
+ environment:
+ - DATABASE_URL=postgresql://superbowl:superbowl@127.0.0.1:5432/superbowl?schema=public
+ depends_on:
+ db:
+ condition: service_healthy
+ restart: unless-stopped
+
+ db:
+ image: postgres:16-alpine
+ network_mode: host
+ env_file: .env
+ volumes:
+ - pgdata:/var/lib/postgresql/data
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U superbowl"]
+ interval: 5s
+ timeout: 5s
+ retries: 5
+ restart: unless-stopped
+
+volumes:
+ pgdata:
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
new file mode 100644
index 0000000..f6cd64e
--- /dev/null
+++ b/docker-entrypoint.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+set -e
+
+echo "Running Prisma migrations..."
+npx prisma db push --skip-generate
+
+echo "Seeding database..."
+node prisma/seed.js || echo "Seed skipped (may already exist)"
+
+echo "Starting server..."
+exec node server.js
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 0000000..4cbfe13
Binary files /dev/null and b/favicon.ico differ
diff --git a/images/AFC_logo_1738803834.png b/images/AFC_logo_1738803834.png
new file mode 100644
index 0000000..806942a
Binary files /dev/null and b/images/AFC_logo_1738803834.png differ
diff --git a/images/FootballField_Low.jpg b/images/FootballField_Low.jpg
new file mode 100644
index 0000000..47af4b6
Binary files /dev/null and b/images/FootballField_Low.jpg differ
diff --git a/images/NFC_logo_1738803826.png b/images/NFC_logo_1738803826.png
new file mode 100644
index 0000000..259a9b6
Binary files /dev/null and b/images/NFC_logo_1738803826.png differ
diff --git a/images/NFL-logo.gif b/images/NFL-logo.gif
new file mode 100644
index 0000000..bd3a7ff
Binary files /dev/null and b/images/NFL-logo.gif differ
diff --git a/images/afc-bengals.png b/images/afc-bengals.png
new file mode 100644
index 0000000..19c1379
Binary files /dev/null and b/images/afc-bengals.png differ
diff --git a/images/afc-bills.png b/images/afc-bills.png
new file mode 100644
index 0000000..890df0f
Binary files /dev/null and b/images/afc-bills.png differ
diff --git a/images/afc-broncos.png b/images/afc-broncos.png
new file mode 100644
index 0000000..2cb26fb
Binary files /dev/null and b/images/afc-broncos.png differ
diff --git a/images/afc-browns.png b/images/afc-browns.png
new file mode 100644
index 0000000..17a7ebf
Binary files /dev/null and b/images/afc-browns.png differ
diff --git a/images/afc-chargers.png b/images/afc-chargers.png
new file mode 100644
index 0000000..de6e251
Binary files /dev/null and b/images/afc-chargers.png differ
diff --git a/images/afc-chiefs.png b/images/afc-chiefs.png
new file mode 100644
index 0000000..96ee391
Binary files /dev/null and b/images/afc-chiefs.png differ
diff --git a/images/afc-colts.png b/images/afc-colts.png
new file mode 100644
index 0000000..5a93320
Binary files /dev/null and b/images/afc-colts.png differ
diff --git a/images/afc-dolphins.png b/images/afc-dolphins.png
new file mode 100644
index 0000000..74bf1b7
Binary files /dev/null and b/images/afc-dolphins.png differ
diff --git a/images/afc-generic.png b/images/afc-generic.png
new file mode 100644
index 0000000..42ab6ab
Binary files /dev/null and b/images/afc-generic.png differ
diff --git a/images/afc-jaguars.png b/images/afc-jaguars.png
new file mode 100644
index 0000000..6df917a
Binary files /dev/null and b/images/afc-jaguars.png differ
diff --git a/images/afc-jets.png b/images/afc-jets.png
new file mode 100644
index 0000000..186681b
Binary files /dev/null and b/images/afc-jets.png differ
diff --git a/images/afc-logo.png b/images/afc-logo.png
new file mode 100644
index 0000000..4acaa4e
Binary files /dev/null and b/images/afc-logo.png differ
diff --git a/images/afc-patriots.png b/images/afc-patriots.png
new file mode 100644
index 0000000..56d623e
Binary files /dev/null and b/images/afc-patriots.png differ
diff --git a/images/afc-raiders.png b/images/afc-raiders.png
new file mode 100644
index 0000000..0267bb9
Binary files /dev/null and b/images/afc-raiders.png differ
diff --git a/images/afc-ravens.png b/images/afc-ravens.png
new file mode 100644
index 0000000..fe71311
Binary files /dev/null and b/images/afc-ravens.png differ
diff --git a/images/afc-steelers.png b/images/afc-steelers.png
new file mode 100644
index 0000000..d91556b
Binary files /dev/null and b/images/afc-steelers.png differ
diff --git a/images/afc-texans.png b/images/afc-texans.png
new file mode 100644
index 0000000..cfb2828
Binary files /dev/null and b/images/afc-texans.png differ
diff --git a/images/afc-titans.png b/images/afc-titans.png
new file mode 100644
index 0000000..ea385ed
Binary files /dev/null and b/images/afc-titans.png differ
diff --git a/images/afcteam.png b/images/afcteam.png
new file mode 100644
index 0000000..3452e88
Binary files /dev/null and b/images/afcteam.png differ
diff --git a/images/background.jpg b/images/background.jpg
new file mode 100644
index 0000000..7985505
Binary files /dev/null and b/images/background.jpg differ
diff --git a/images/nfc-bears.png b/images/nfc-bears.png
new file mode 100644
index 0000000..5a8477d
Binary files /dev/null and b/images/nfc-bears.png differ
diff --git a/images/nfc-buccaneers.png b/images/nfc-buccaneers.png
new file mode 100644
index 0000000..e958136
Binary files /dev/null and b/images/nfc-buccaneers.png differ
diff --git a/images/nfc-cardinals.png b/images/nfc-cardinals.png
new file mode 100644
index 0000000..8c7b9f6
Binary files /dev/null and b/images/nfc-cardinals.png differ
diff --git a/images/nfc-commanders.png b/images/nfc-commanders.png
new file mode 100644
index 0000000..331ddcb
Binary files /dev/null and b/images/nfc-commanders.png differ
diff --git a/images/nfc-cowboys.png b/images/nfc-cowboys.png
new file mode 100644
index 0000000..a7ac941
Binary files /dev/null and b/images/nfc-cowboys.png differ
diff --git a/images/nfc-eagles.png b/images/nfc-eagles.png
new file mode 100644
index 0000000..d648b38
Binary files /dev/null and b/images/nfc-eagles.png differ
diff --git a/images/nfc-falcons.png b/images/nfc-falcons.png
new file mode 100644
index 0000000..0cb45fe
Binary files /dev/null and b/images/nfc-falcons.png differ
diff --git a/images/nfc-generic.png b/images/nfc-generic.png
new file mode 100644
index 0000000..9c308e0
Binary files /dev/null and b/images/nfc-generic.png differ
diff --git a/images/nfc-giants.png b/images/nfc-giants.png
new file mode 100644
index 0000000..2be66f3
Binary files /dev/null and b/images/nfc-giants.png differ
diff --git a/images/nfc-lions.png b/images/nfc-lions.png
new file mode 100644
index 0000000..8cb40f1
Binary files /dev/null and b/images/nfc-lions.png differ
diff --git a/images/nfc-logo.png b/images/nfc-logo.png
new file mode 100644
index 0000000..b15ab40
Binary files /dev/null and b/images/nfc-logo.png differ
diff --git a/images/nfc-packers.png b/images/nfc-packers.png
new file mode 100644
index 0000000..3ba4e6f
Binary files /dev/null and b/images/nfc-packers.png differ
diff --git a/images/nfc-panthers.png b/images/nfc-panthers.png
new file mode 100644
index 0000000..f0c9b17
Binary files /dev/null and b/images/nfc-panthers.png differ
diff --git a/images/nfc-rams.png b/images/nfc-rams.png
new file mode 100644
index 0000000..8c4c47c
Binary files /dev/null and b/images/nfc-rams.png differ
diff --git a/images/nfc-saints.png b/images/nfc-saints.png
new file mode 100644
index 0000000..bf9d125
Binary files /dev/null and b/images/nfc-saints.png differ
diff --git a/images/nfc-seahawks.png b/images/nfc-seahawks.png
new file mode 100644
index 0000000..cbb1a2c
Binary files /dev/null and b/images/nfc-seahawks.png differ
diff --git a/images/nfc-team.png b/images/nfc-team.png
new file mode 100644
index 0000000..ab61e14
Binary files /dev/null and b/images/nfc-team.png differ
diff --git a/images/nfc-vikings.png b/images/nfc-vikings.png
new file mode 100644
index 0000000..dd3c656
Binary files /dev/null and b/images/nfc-vikings.png differ
diff --git a/images/nfcteam.png b/images/nfcteam.png
new file mode 100644
index 0000000..0b61347
Binary files /dev/null and b/images/nfcteam.png differ
diff --git a/images/nfl-logo.png b/images/nfl-logo.png
new file mode 100644
index 0000000..08dab84
Binary files /dev/null and b/images/nfl-logo.png differ
diff --git a/images/superbowlnumber.png b/images/superbowlnumber.png
new file mode 100644
index 0000000..3e14b3f
Binary files /dev/null and b/images/superbowlnumber.png differ
diff --git a/images/superbowlnumber_LVIII.png b/images/superbowlnumber_LVIII.png
new file mode 100644
index 0000000..100888c
Binary files /dev/null and b/images/superbowlnumber_LVIII.png differ
diff --git a/legacy/admin.php b/legacy/admin.php
new file mode 100644
index 0000000..9b3a5ad
--- /dev/null
+++ b/legacy/admin.php
@@ -0,0 +1,181 @@
+" . "\r\n";
+$headers .= 'Bcc: $commissioner <$ADMIN_EMAIL>' . "\r\n";
+
+function notify_admin($mailto, $mailmessage, $mail_headers) {
+ mail($mailto, "Super Bowl Squares", $mailmessage, $mail_headers);
+}
+?>
+
+ADMINISTRATOR APPROVAL
+
+
+
+
+
+ Confirm user selected square when payment is verified, or release the square if allowed time for payment has expired.
+
+
+
+
+
+
+
+
+
+
+
+ 0) {
+ $input = "";
+ $whereclause = "(";
+
+ for ($e = 0; $e < count($SQUARE); $e++) {
+ $whereclause .= "SQUARE = '" . trim($SQUARE[$e]) . "' OR ";
+ $square_list = $square_list . ", " . $SQUARE[$e];
+ }
+
+ $whereclause = substr_replace($whereclause, "", -3); // strip off ' OR'
+ $whereclause = $whereclause . ")";
+
+ $square_list = substr_replace($square_list, "", 0, 2);
+
+ $query = "SELECT * FROM VNSB_squares WHERE $whereclause";
+ $result = mysqli_query($conn, $query);
+ if (!$result) {
+ echo mysqli_error();
+ exit;
+ }
+
+ $USER_EMAIL_LIST = '';
+ while ($record = mysqli_fetch_assoc($result)) {
+ $USER_EMAIL = $record["EMAIL"];
+ $pos = strpos($USER_EMAIL_LIST, $USER_EMAIL);
+ if ($pos === false) {
+ $USER_EMAIL_LIST = $USER_EMAIL_LIST . ", " . $USER_EMAIL;
+ }
+ }
+ $USER_EMAIL_LIST = substr_replace($USER_EMAIL_LIST, "", 0, 2);
+
+ $bodyMessage = "\r\nNOTIFICATION: \r\n";
+ if ($CONFIRM == 1 && $RELEASE != 1) {
+ $query = "UPDATE VNSB_squares SET CONFIRM='1' WHERE $whereclause";
+ $bodyMessage .= "Your square $square_list is now confirmed.\r\n\n";
+ } else if ($RELEASE == 1 && $CONFIRM != 1) {
+ $query = "UPDATE VNSB_squares SET NAME='AVAILABLE', EMAIL='', NOTES='', DATE='', CONFIRM='0' WHERE $whereclause";
+ $bodyMessage .= "Your square $square_list selection is now released due to non payment.\r\n";
+ $bodyMessage .= " If this is an error, please contact the commissioner or re-select your square/squares. \r\n\n";
+ } else if (($CONFIRM != 1 && $RELEASE != 1) || ($RELEASE == 1 && $CONFIRM == 1)) {
+ echo "Must select ONLY one 'Confirm' or 'Release' !!!
";
+ echo "Back
";
+ exit;
+ }
+
+ $result = mysqli_query($conn, $query);
+ if (!$result) {
+ echo mysqli_error();
+ } else {
+ $bodyMessage .= " Reason given: \r\n";
+ $bodyMessage .= $NOTES . "\r\n\n";
+ $bodyMessage .= " Good Luck and enjoy the game!\r\n";
+ $bodyMessage .= " - $commissioner\r\n";
+ $bodyMessage .= "$sb_URL \r\n";
+
+ notify_admin($USER_EMAIL_LIST, $bodyMessage, $headers);
+ echo "Square(s) " . $square_list . " updated successfully
";
+ echo "Emailed to: " . $USER_EMAIL_LIST . "
";
+ echo "
+
+
+ ";
+ unset($confirmation, $SQUARE, $CONFIRM, $RELEASE, $NOTES, $ADM_EMAIL, $ADM_PASSWORD);
+ }
+ } else {
+ echo "Must select at least one Square to Confirm or Release' !!!
";
+ echo "Back
";
+ exit;
+ }
+}
+require "footer.inc";
+?>
\ No newline at end of file
diff --git a/legacy/adminlogin.php b/legacy/adminlogin.php
new file mode 100644
index 0000000..28e53b8
--- /dev/null
+++ b/legacy/adminlogin.php
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+ADMIN - Login
+
+
+
+
+
+ ";
+ echo "Admin Log out. ";
+ exit;
+ }
+ // check input variables against database
+ include "config.php";
+ $sql = "SELECT Admin_email, Admin_pwd FROM VNSB_settings";
+ $result = mysqli_query($conn,$sql);
+ // in case of an error, throw up an error message and exit
+ if (!$result) {
+ echo "Sorry, there is a problem with accessing your database!!!";
+ exit;
+ } else {
+ $record = mysqli_fetch_assoc($result);
+ if ($email==$record['Admin_email'] AND $pass==$record['Admin_pwd']) {
+ $_SESSION['VNSB']=$record['Admin_email'];
+ mysqli_close($conn);
+ header ("Location: admin.php");
+ } else {
+ echo "Invalid login Admin login
";
+ mysqli_close($conn);
+ exit;
+ }
+ }
+}
+
+?>
+
+
+Admin login
+
+
+ Email:
+ Password:
+
+
+
+
+
+
diff --git a/legacy/adminlogout.php b/legacy/adminlogout.php
new file mode 100644
index 0000000..64970ff
--- /dev/null
+++ b/legacy/adminlogout.php
@@ -0,0 +1,33 @@
+
+
+
+
+ADMIN - Logout
+
+
+
+
+
+
+ Logout
+ Don't forget to close your brower when you are done!!! ";
+
+ // if the user isnt logged in, let them know that
+ } else {
+ echo "You haven't even logged in yet.";
+ }
+ ?>
+
+ ">Home
+ Admin
+
+
+
+
diff --git a/legacy/config.php b/legacy/config.php
new file mode 100644
index 0000000..a931389
--- /dev/null
+++ b/legacy/config.php
@@ -0,0 +1,19 @@
+
+
+
diff --git a/legacy/emailall.php b/legacy/emailall.php
new file mode 100644
index 0000000..b229863
--- /dev/null
+++ b/legacy/emailall.php
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+ ";
+
+ function notify_admin ($mailto, $mailmessage, $mail_headers)
+ {
+ mail("$mailto", "Super Bowl Squares", "$mailmessage", "$mail_headers");
+ }
+?>
+
+ Send Email to ALL
+
+
+
+
+ Click the button below to send an email to everyone and let them know the numbers have been picked and assigned for them to view and print as needed.
+
+
+
+
+
+
+
+
+
+
+ Emails send to:";
+ $bodyMessage = "\r\nNOTIFICATION\r\n";
+ $bodyMessage .= "All squares have been selected and all numbers have been picked and assigned.\r\n";
+ $bodyMessage .= "You can view and print your own sheet at $sb_URL.\r\n\n";
+ $bodyMessage .= "Good Luck and enjoy the game.\r\n\n";
+ $bodyMessage .= "$commissioner\r\n";
+ $headers = "From: $commissioner <$ADMIN_EMAIL>\r\n";
+ $sql="SELECT * FROM VNSB_squares ORDER BY EMAIL";
+ $result = mysqli_query($conn,$sql);
+ if (!$result) {
+ echo mysqli_error();
+ exit;
+ }
+ while ($record = mysqli_fetch_assoc($result)) {
+ if ($USER_EMAIL != $record["EMAIL"]) {
+ $USER_NAME = $record["NAME"];
+ $USER_EMAIL = $record["EMAIL"];
+ notify_admin($USER_EMAIL,$bodyMessage,$headers);
+ echo "".$USER_NAME." : ".$USER_EMAIL."
";
+ }
+ }
+
+ echo $LINKS;
+ unset($sendemails);
+ $headers = "From: $commissioner <$ADMIN_EMAIL>\r\n";
+ notify_admin($ADMIN_EMAIL,$bodyMessage,$headers);
+ } ?>
+
diff --git a/legacy/err_log b/legacy/err_log
new file mode 100644
index 0000000..e69de29
diff --git a/legacy/footer.inc b/legacy/footer.inc
new file mode 100644
index 0000000..0c706c7
--- /dev/null
+++ b/legacy/footer.inc
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+