Files
BizzleBot efafbea297 v2.1.0: Copy Previous Week, Bulk Approve, Overtime Tracking
Features:
- Copy Previous Week: one-tap to duplicate last week's entries
- Bulk Approve/Reject: multi-select + batch actions for admin reviews
- Overtime Tracking: admin report tab + employee real-time OT warnings
- Homeowner filter on reports
- Homeowner edit/search/address fields
- Employee role management + deactivate/reactivate
- Reopen locked timesheets

Fixes:
- Timezone bug (UTC vs local date parsing)
- CORS, approve 400, PDF download, auto-save errors
- History page crash, rate limiting
2026-02-15 21:43:31 +00:00

305 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<p align="center">
<img src="docs/screenshots/01-login-mobile.png" alt="Coastal Timesheet" width="200" />
</p>
<h1 align="center">Coastal Timesheet v2</h1>
<p align="center">
<strong>A modern, mobile-first time tracking application for Coastal Contracting of FL</strong>
</p>
<p align="center">
<img src="https://img.shields.io/badge/React-18-61DAFB?logo=react&logoColor=white" alt="React 18" />
<img src="https://img.shields.io/badge/Express-4-000000?logo=express&logoColor=white" alt="Express" />
<img src="https://img.shields.io/badge/PostgreSQL-16-4169E1?logo=postgresql&logoColor=white" alt="PostgreSQL" />
<img src="https://img.shields.io/badge/Prisma-ORM-2D3748?logo=prisma&logoColor=white" alt="Prisma" />
<img src="https://img.shields.io/badge/Docker-Compose-2496ED?logo=docker&logoColor=white" alt="Docker" />
<img src="https://img.shields.io/badge/TailwindCSS-3-06B6D4?logo=tailwindcss&logoColor=white" alt="Tailwind" />
</p>
---
## ✨ Features
- **📱 Mobile-first design** — Built for field workers, optimized for phones
- **🔐 JWT authentication** — Secure login with access/refresh tokens + rate limiting
- **📅 Weekly timesheets** — Monday–Sunday pay period with auto-save
- **🏠 Multiple homeowners per day** — Track work at different job sites
- **📋 Copy Previous Week** — One tap to duplicate last week's entries as a template
- **✅ Submit → Approve workflow** — Employees submit, admins approve or reject
- **⚡ Bulk Approve/Reject** — Select all + approve 40 timesheets in one click
- **⏱️ Overtime Tracking** — Real-time OT warnings for employees, admin reports with per-employee weekly breakdown
- **📄 PDF generation** — Professional server-side PDF export
- **📧 Email integration** — Send timesheets via email with SMTP
- **👥 Admin panel** — Manage employees, homeowners, review timesheets
- **📊 Reporting** — Filter by employee, homeowner, date range, status
- **🏡 Homeowner management** — Add, edit, search, activate/deactivate with address fields
- **🌙 Dark mode** — System-aware with manual toggle
- **🐳 One-command deploy** — Single `docker compose up` for the entire stack
---
## 📸 Screenshots
<table>
<tr>
<td align="center"><strong>Timesheet Entry</strong></td>
<td align="center"><strong>Copy Previous Week</strong></td>
<td align="center"><strong>Dark Mode</strong></td>
</tr>
<tr>
<td><img src="docs/screenshots/02-timesheet-mobile.png" width="250" /></td>
<td><img src="docs/screenshots/08-timesheet-copy-week.png" width="250" /></td>
<td><img src="docs/screenshots/07-dark-mode-mobile.png" width="250" /></td>
</tr>
</table>
<table>
<tr>
<td align="center"><strong>Bulk Approve</strong></td>
<td align="center"><strong>Overtime Tracking</strong></td>
<td align="center"><strong>Reports & Filters</strong></td>
</tr>
<tr>
<td><img src="docs/screenshots/09-admin-bulk-approve.png" width="250" /></td>
<td><img src="docs/screenshots/10-admin-overtime.png" width="250" /></td>
<td><img src="docs/screenshots/12-admin-reports-filters.png" width="250" /></td>
</tr>
</table>
<table>
<tr>
<td align="center"><strong>Homeowner Management</strong></td>
<td align="center"><strong>History</strong></td>
<td align="center"><strong>Entry Form</strong></td>
</tr>
<tr>
<td><img src="docs/screenshots/11-admin-homeowners.png" width="250" /></td>
<td><img src="docs/screenshots/04-history-mobile.png" width="250" /></td>
<td><img src="docs/screenshots/03-entry-form-mobile.png" width="250" /></td>
</tr>
</table>
### Desktop
<img src="docs/screenshots/02-timesheet-desktop.png" width="700" />
<img src="docs/screenshots/06-admin-reports-desktop.png" width="700" />
---
## 🚀 Quick Start
### Prerequisites
- [Docker](https://docs.docker.com/get-docker/) and Docker Compose
- That's it. Everything else runs in containers.
### Deploy
```bash
# Clone the repo
git clone https://git.bizzle.lol/bizzle/coastal_timesheet.git
cd coastal_timesheet
git checkout v2
# Configure environment
cp .env.example .env
# Edit .env with your secrets (see Configuration below)
# Launch
cd docker
docker compose up -d
```
The app will be available at `http://localhost` (port 80).
### Default Admin Account
| Field | Value |
|----------|-------------------------|
| Email | `admin@coastal.com` |
| Password | `CoastalAdmin2026!` |
> ⚠️ **Change the admin password after first login.**
---
## ⚙️ Configuration
Copy `.env.example` to `.env` and configure:
```env
# Database (auto-configured in Docker)
DB_PASSWORD=your-secure-db-password
# JWT Secrets (CHANGE THESE!)
JWT_SECRET=your-jwt-secret-min-32-chars
JWT_REFRESH_SECRET=your-refresh-secret-min-32-chars
# CORS (add your domain)
CORS_ORIGINS=https://timesheets.yourdomain.com
# Email (optional — for sending timesheets)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password
SMTP_FROM=timesheets@yourdomain.com
ADMIN_EMAIL=admin@yourdomain.com
```
### Email Setup (Gmail)
1. Enable 2FA on your Google account
2. Go to [App Passwords](https://myaccount.google.com/apppasswords)
3. Generate a new app password for "Mail"
4. Use that as `SMTP_PASS`
---
## 🏗️ Architecture
```
┌─────────────────────────────────────────────┐
│ Nginx │
│ (reverse proxy) │
│ /api/* → backend:3001 │
│ /* → static frontend │
├──────────────────┬──────────────────────────┤
│ Frontend │ Backend │
│ React + Vite │ Express + Prisma │
│ Tailwind CSS │ JWT Auth │
│ Lucide Icons │ @react-pdf/renderer │
│ │ Nodemailer │
│ ├──────────────────────────┤
│ │ PostgreSQL 16 │
│ │ (persistent volume) │
└──────────────────┴──────────────────────────┘
```
### Tech Stack
| Layer | Technology |
|-----------|-----------------------------------------------|
| Frontend | React 18, Vite 6, Tailwind CSS 3, Lucide |
| Backend | Express 4, Prisma ORM, bcryptjs, jsonwebtoken |
| Database | PostgreSQL 16 (Alpine) |
| PDF | @react-pdf/renderer (server-side) |
| Email | Nodemailer + SMTP |
| Proxy | Nginx 1.27 (Alpine) |
| Container | Docker Compose v3.9 |
---
## 📁 Project Structure
```
.
├── frontend/ # React SPA
│ ├── src/
│ │ ├── api/ # Axios client with token refresh
│ │ ├── components/ # Reusable UI components
│ │ ├── contexts/ # Auth context (JWT)
│ │ ├── hooks/ # Custom hooks (theme, swipe, auto-save)
│ │ └── pages/ # Route pages
│ └── vite.config.js
├── backend/ # Express API
│ ├── prisma/
│ │ ├── schema.prisma # Database schema
│ │ └── seed.js # Seed admin + homeowners
│ └── src/
│ ├── middleware/ # Auth middleware
│ ├── routes/ # API routes
│ └── utils/ # PDF, email, validation
├── docker/ # Deployment
│ ├── docker-compose.yml
│ ├── backend/Dockerfile
│ ├── frontend/Dockerfile
│ └── nginx/default.conf
└── docs/screenshots/ # App screenshots
```
---
## 🔒 Security
- **bcrypt** password hashing (12 rounds)
- **JWT** access tokens (15min) + refresh tokens (7 days)
- **Helmet** security headers
- **Rate limiting** on auth endpoints (50 req / 15 min)
- **Zod** input validation on all endpoints
- **Prisma ORM** — parameterized queries (no SQL injection)
- **Non-root Docker** containers
- **CORS** origin whitelist
---
## 📡 API Endpoints
### Auth
| Method | Endpoint | Description |
|--------|----------------------|----------------------|
| POST | `/api/auth/login` | Login, get tokens |
| POST | `/api/auth/refresh` | Refresh access token |
| GET | `/api/auth/me` | Current user info |
### Entries
| Method | Endpoint | Description |
|--------|---------------------|------------------------|
| GET | `/api/entries` | List entries (by week) |
| POST | `/api/entries` | Create entry |
| PUT | `/api/entries/:id` | Update entry |
| DELETE | `/api/entries/:id` | Delete entry |
### Timesheets
| Method | Endpoint | Description |
|--------|------------------------------|----------------------|
| GET | `/api/timesheets` | Get current week |
| GET | `/api/timesheets/history` | All user timesheets |
| POST | `/api/timesheets/submit` | Submit for approval |
| GET | `/api/timesheets/:id/pdf` | Download PDF |
### Admin
| Method | Endpoint | Description |
|--------|-------------------------------------|-------------------------|
| GET | `/api/admin/timesheets` | All timesheets (filter) |
| GET | `/api/admin/timesheets/:id` | Timesheet detail |
| PUT | `/api/admin/timesheets/:id/approve` | Approve timesheet |
| PUT | `/api/admin/timesheets/:id/reject` | Reject timesheet |
| PUT | `/api/admin/timesheets/:id/reopen` | Reopen for editing |
| GET | `/api/admin/users` | List employees |
| POST | `/api/admin/users` | Create employee |
| GET | `/api/admin/homeowners` | List homeowners |
| POST | `/api/admin/homeowners` | Add homeowner |
| GET | `/api/admin/reports` | Reporting with filters |
---
## 🔄 Upgrading from v1
v2 is a complete rewrite. Key differences:
| Feature | v1 | v2 |
|-----------------|-----------------------------|----------------------------------|
| Storage | Browser localStorage | PostgreSQL database |
| Auth | None | JWT with roles |
| Multi-user | No | Yes — unlimited employees |
| Approval flow | No | Submit → Approve/Reject |
| PDF | Client-side (jsPDF) | Server-side (@react-pdf) |
| Email | mailto: link | SMTP with PDF attachment |
| Deploy | Static HTML | Docker Compose (one command) |
| Admin panel | No | Full admin with reporting |
| Dark mode | No | System-aware + manual toggle |
---
## 📝 License
Private — Coastal Contracting of FL. All rights reserved.
---
<p align="center">
Built with ☀️ in Florida
</p>