Skip to content

Latest commit

 

History

History
197 lines (157 loc) · 6.83 KB

File metadata and controls

197 lines (157 loc) · 6.83 KB

Development Guide


Local Setup

See INSTALLING.md for full installation steps. The short version:

docker compose up -d db                    # Start PostgreSQL
cd backend && npm install && npm run dev   # Backend on :3001
cd frontend && npm install && npm run dev  # Frontend on :5173

Folder Structure

Backend (backend/)

backend/
├── Dockerfile                  # node:20-slim + openssl, runs prisma db push + seed + server
├── package.json
├── tsconfig.json
├── prisma/
│   ├── schema.prisma           # Single source of truth for the database schema
│   ├── seed.ts                 # Seed script (run via `npx tsx prisma/seed.ts`)
│   └── migrations/
│       ├── 0001_init/
│       │   └── migration.sql   # Initial schema SQL
│       └── migration_lock.toml
└── src/
    ├── server.ts               # Express app: CORS, JSON, cookie-parser, route mounting
    ├── middleware/
    │   ├── auth.ts             # authMiddleware (JWT verify) + adminOnly guard
    │   └── errorHandler.ts     # Global Express error handler
    ├── routes/
    │   ├── auth.ts             # POST login/refresh/logout, GET me
    │   ├── clients.ts          # CRUD — admin only
    │   ├── engagements.ts      # CRUD — admin only
    │   ├── users.ts            # CRUD + password change — admin only
    │   ├── costRates.ts        # CRUD — admin only
    │   ├── assignments.ts      # CRUD with overlap validation — admin only
    │   ├── timesheets.ts       # List + status transitions (submit, approve, send-back)
    │   ├── timeEntries.ts      # CRUD + hour validation + assignment lookup
    │   ├── reports.ts          # 3 report endpoints (JSON + Excel export) — admin only
    │   └── margins.ts          # Margin calculation — admin only
    └── utils/
        └── jwt.ts              # Sign/verify helpers for access and refresh tokens

Frontend (frontend/)

frontend/
├── Dockerfile                  # node:20-alpine, runs `npm run dev`
├── index.html                  # Single HTML entry point
├── package.json
├── tsconfig.json
├── vite.config.ts              # Vite: React plugin, host 0.0.0.0
├── tailwind.config.ts          # Tailwind content paths
├── postcss.config.js           # PostCSS: Tailwind + Autoprefixer
└── src/
    ├── main.tsx                # ReactDOM.createRoot
    ├── App.tsx                 # BrowserRouter + all route definitions
    ├── index.css               # Tailwind directives (@tailwind base/components/utilities)
    ├── vite-env.d.ts           # Vite client type reference
    ├── api/
    │   └── client.ts           # Axios instance + request/response interceptors
    ├── auth/
    │   ├── AuthContext.tsx      # React Context: user state, login, logout, silent refresh
    │   ├── ProtectedRoute.tsx  # Route guard (redirects to /login or /timesheets)
    │   └── LoginPage.tsx       # Login form
    ├── components/
    │   ├── Layout.tsx          # Header + sidebar + <Outlet />
    │   ├── Modal.tsx           # Generic modal dialog (esc to close, overlay click)
    │   ├── MonthPicker.tsx     # Left/right month navigation
    │   ├── SearchableSelect.tsx# Dropdown with type-ahead filter
    │   ├── Spinner.tsx         # Loading spinner
    │   └── ConfirmDialog.tsx   # Delete confirmation modal
    ├── features/
    │   ├── admin/
    │   │   ├── ClientsPage.tsx
    │   │   ├── EngagementsPage.tsx
    │   │   ├── UsersPage.tsx
    │   │   ├── CostRatesPage.tsx
    │   │   ├── AssignmentsPage.tsx
    │   │   └── AdminTimesheetsPage.tsx
    │   ├── timesheets/
    │   │   └── TimesheetPage.tsx
    │   └── reports/
    │       ├── UserMonthlyReport.tsx
    │       ├── UserEngagementReport.tsx
    │       ├── UserClientReport.tsx
    │       └── MarginsPage.tsx
    └── types/
        └── index.ts            # TypeScript interfaces (User, Client, Engagement, etc.)

Coding Conventions

  • Language: TypeScript everywhere (strict mode).
  • Backend framework: Express 4 with route-level middleware. Each route file exports a single Router.
  • ORM: Prisma Client. Each route file creates its own PrismaClient() instance.
  • Frontend state: React hooks + Context API (no external state management).
  • Styling: Tailwind CSS utility classes. No component library.
  • Notifications: react-hot-toast for success/error/warning toasts.
  • Dates: stored as DATE in PostgreSQL, serialised as ISO strings in the API, formatted as DD/MM/YYYY in the UI.
  • Currency: formatted with 2 decimal places and the symbol in the UI.

Database Changes

Modifying the schema

  1. Edit backend/prisma/schema.prisma.

  2. Push changes to the local database:

    cd backend
    DATABASE_URL="postgresql://marginio:marginio@localhost:5432/marginio" npx prisma db push
  3. Regenerate the Prisma client:

    npx prisma generate
  4. If you need a migration file for deployment, generate the SQL diff:

    npx prisma migrate diff \
      --from-schema-datamodel prisma/schema.prisma \
      --to-schema-datasource prisma/schema.prisma \
      --script > prisma/migrations/XXXX_description/migration.sql

Re-seeding

DATABASE_URL="postgresql://marginio:marginio@localhost:5432/marginio" npx tsx prisma/seed.ts

The seed script is idempotent — it deletes all existing data before inserting.


Type Checking

Both projects use TypeScript strict mode. To verify without emitting output:

# Backend
cd backend && npx tsc --noEmit

# Frontend
cd frontend && npx tsc --noEmit

Building for Production

Backend

cd backend
npm run build         # Compiles to dist/
npm start             # Runs dist/server.js

Frontend

cd frontend
npm run build         # Type-checks + produces dist/
npm run preview       # Preview the build locally

The dist/ folder contains static HTML/CSS/JS that can be served by any HTTP server or CDN.


Key Environment Variables for Development

Variable Where Value
DATABASE_URL Backend shell postgresql://marginio:marginio@localhost:5432/marginio
JWT_SECRET Backend shell change-me-in-production
JWT_REFRESH_SECRET Backend shell change-me-refresh
PORT Backend shell 3001
VITE_API_URL Frontend shell http://localhost:3001/api (default, no need to set)