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
305 lines
11 KiB
Markdown
305 lines
11 KiB
Markdown
<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>
|