A modern, mobile-first time tracking application for Coastal Contracting of FL
| Timesheet Entry | Copy Previous Week | Dark Mode |
![]() |
![]() |
![]() |
| Bulk Approve | Overtime Tracking | Reports & Filters |
![]() |
![]() |
![]() |
| Homeowner Management | History | Entry Form |
![]() |
![]() |
![]() |
---
## π 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.
---
Built with βοΈ in Florida