# IT Resource Management Portal — Build Spec & Status This is the working spec for the internal Resource Management Portal. It mirrors `IT_Resource_Portal_Specs.docx` (v1.0, April 2026) and tracks progress phase by phase. Update the checklist boxes (`- [ ]` → `- [x]`) as items ship and pass testing. > Reference document: `IT_Resource_Portal_Specs.docx` (Project Overview, > Technology Stack, Container Architecture, Data Model, Pages & Workflows, > Frontend Components, Backend Implementation, Build Phases, Configuration). --- ## 1. Stack snapshot | Layer | Choice | Status | | ---------------- | --------------------------------------- | ------ | | Container | Podman, single image | Done | | Process manager | Supervisord (Nginx + Uvicorn) | Done | | Reverse proxy | Nginx (`/api/v1/*` → FastAPI on `:8000`)| Done | | Frontend | React 18 + Vite + TypeScript | Done | | State / data | Zustand (UI), TanStack Query (server) | Done | | HTTP client | Axios with HTTP Basic interceptor | Done | | Backend | FastAPI + Uvicorn | Done | | ORM / DB | SQLAlchemy 2.0 + SQLite (WAL) | Done | | Migrations | Alembic at startup | Done | | Persistence | Podman volume `portal-data` → `/data` | Done | | Auth | HTTP Basic (admin / manager / viewer) | Done | --- ## 2. Phase 1 — MVP Goal: A working container with data persistence, basic CRUD for people and projects, and the resource schedule view with manual allocation entry. - [x] Containerfile, Nginx, Supervisord, Uvicorn wired up and running - [x] Alembic migrations for all tables run at startup - [x] FastAPI routers: `/people`, `/projects`, `/allocations`, `/leaves`, `/reports`, `/users` - [x] React scaffold: sidebar navigation, page shell, routing - [x] HTTP Basic Auth + login page + sign out + session credentials - [x] Dashboard page: KPI cards and overallocation alerts - [x] People page: list + add / edit person form + skills assignment + archive - [x] Projects page: list / card view + add / edit project form - [x] Projects page: milestones add / edit / complete - [x] Schedule page: read-only allocation bars across people rows - [x] Schedule page: create / edit / delete allocations - [x] Leaves: log leave from People page (modal) and Schedule page (button) - [x] Public holidays admin from Settings page - [x] Toasts for success / error feedback - [x] Empty states + loading skeletons on all primary pages --- ## 3. Phase 2 — Core Interactions Goal: The schedule view becomes interactive. Capacity heatmap ships. Gantt view ships. - [ ] Drag-to-create on the schedule timeline - [ ] Drag-to-resize on existing allocation bars - [ ] Drag-to-move on existing allocation bars - [x] Conflict detection and overallocation highlighting (red bars) - [x] Capacity page: heatmap grid with color coding - [x] Capacity page: capacity vs demand chart (totals per week) - [x] Projects page: Gantt-like view with milestone markers and today line - [x] Leave management: log leave from the People page and schedule view - [x] Public holidays: admin can add dates that block capacity for all --- ## 4. Phase 3 — Reports & Polish Goal: Reports, CSV export, settings page, and UX polish. - [x] Reports page: utilization table + chart - [x] Reports page: availability report - [x] Reports page: leave summary - [x] Reports page: overallocated report - [x] CSV export for all report types - [x] Settings page: user management - [x] Settings page: skill library - [x] Settings page: working week config (read-only display in v1) - [ ] Person detail side panel with mini schedule and leave calendar - [ ] Project detail side panel with team members and milestone list - [ ] Responsive layout improvements (works on tablets) - [x] Error states, empty states, loading skeletons on all pages --- ## 5. Backend endpoints (current surface) All endpoints require HTTP Basic auth. Mutations require `manager` or `admin` role; user management requires `admin`. | Method | Path | Description | | ------ | ----------------------------------------------- | ---------------------------------------------------------- | | GET | `/api/v1/health` | Liveness check | | GET | `/api/v1/me` | Current user (used by login) | | GET | `/api/v1/dashboard` | KPIs + overallocations + upcoming milestones | | GET | `/api/v1/people` | List active people (with skills) | | POST | `/api/v1/people` | Create a person | | GET | `/api/v1/people/{id}` | Get a person | | PATCH | `/api/v1/people/{id}` | Update a person (incl. `skill_ids`) | | DELETE | `/api/v1/people/{id}` | Archive (soft-delete) a person | | GET | `/api/v1/people/{id}/availability?start&end` | Capacity for one person over a window | | GET | `/api/v1/skills` | List skills | | POST | `/api/v1/skills` | Create a skill | | GET | `/api/v1/projects` | List active projects (with milestones) | | POST | `/api/v1/projects` | Create a project | | PATCH | `/api/v1/projects/{id}` | Update a project | | DELETE | `/api/v1/projects/{id}` | Archive (soft-delete) a project | | POST | `/api/v1/projects/{id}/milestones` | Add a milestone | | PATCH | `/api/v1/projects/{id}/milestones/{milestoneId}`| Update a milestone | | GET | `/api/v1/allocations?person_id&project_id&start&end` | List allocations | | POST | `/api/v1/allocations` | Create an allocation (capacity-validated) | | PATCH | `/api/v1/allocations/{id}` | Update an allocation | | DELETE | `/api/v1/allocations/{id}` | Delete an allocation | | GET | `/api/v1/leaves?person_id&start&end&leave_type` | List leave records | | POST | `/api/v1/leaves` | Create a leave entry | | PATCH | `/api/v1/leaves/{id}` | Update a leave entry | | DELETE | `/api/v1/leaves/{id}` | Delete a leave entry | | GET | `/api/v1/public-holidays?year` | List public holidays | | POST | `/api/v1/public-holidays` | Create a public holiday | | GET | `/api/v1/reports/utilization?start&end` | Utilization per person | | GET | `/api/v1/reports/capacity?start&end` | Aggregate capacity per week | | GET | `/api/v1/reports/availability?start&end` | Available hours per person | | GET | `/api/v1/reports/overallocated?start&end` | People > 100% utilized | | GET | `/api/v1/reports/project-allocation?start&end` | Allocations broken out by project | | GET | `/api/v1/users` | List portal users | | POST | `/api/v1/users` | Create a user | | GET | `/api/v1/admin/seed-demo/status` | Whether portal DB is empty (admin only) | | POST | `/api/v1/admin/seed-demo?replace=true` | Wipe portal data (except users) and load sample IT dataset | Set `ENABLE_DEMO_DATA=false` on the office Linux box to hide and disable these endpoints in production. ### Sample / demo data (testing) Admin-only fictional dataset for exploring the portal (similar to Runn’s trial explore flow): - **7 people**, **5 projects** (Address Discovery, Hold IST, KTLO/Admin, Mainframe, Pimcore PaaS), roles, teams, skills, allocations (including one overallocated person), leaves, and public holidays - **Insights** (empty portal): “Load sample data” button with confirmation - **Manage → Users**: “Load sample data” / “Reload sample data” - **Hugging Face Spaces**: database is ephemeral — reload sample data after each deploy - **Office Podman** (`portal-data` volume): data persists; reload only when you choose to reset demo data --- ## 6. Local & container workflow ### Local backend ```bash cd backend python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt ADMIN_PASSWORD=admin uvicorn main:app --reload ``` API docs: `http://localhost:8000/docs`. ### Local frontend ```bash cd frontend npm install npm run dev ``` The frontend proxies `/api/v1/*` to the local backend. The login page collects the credentials at runtime — no `VITE_ADMIN_*` env vars are required. ### Container ```bash podman volume create portal-data podman build -t resource-portal . podman run -d --replace -p 8080:80 -v portal-data:/data --name portal \ -e ADMIN_USERNAME=admin \ -e ADMIN_PASSWORD=admin \ resource-portal ``` Open `http://localhost:8080` and sign in with the admin credentials. The portal starts empty — use **Insights → Load sample data** (admin) or add data manually from **Manage → People** and **Manage → Projects**. --- ## 7. Phase 4 — Runn-style planner parity Inspired by [Runn’s People Planner](https://help.runn.io/en/articles/5185963-set-up-your-runn-account) and the [Project Manager guide](https://help.runn.io/en/articles/8273502-a-guide-for-project-managers). - [x] Top-tab navigation (People · Projects · Schedule · Capacity · Reports · Manage) - [x] People Planner: weekly availability grid grouped by team / department - [x] Per-week cell shows free / Full / over hours with Runn color coding - [x] Aggregate row per team (collapsible) - [x] Sub-toolbar: search, group-by, sort, chart toggle, tentative toggle, range - [x] Date controls: « ‹ Today › » with month / quarter range selectors - [x] "+ New Person" inline action at the bottom of the planner - [x] Per-person weekly availability endpoint (`GET /api/v1/reports/people-weekly-availability`) - [x] Runn-flavored purple/lavender visual language - [ ] Project Planner — assignments grouped under each project - [ ] Drag-to-create / resize / move on planner cells - [ ] Time-off display mode and "Bulk add" assignments - [ ] Phases on projects and milestone diamonds in the planner row - [ ] Project budget tracker and dashboard view - [ ] Custom fields for people and projects ## 8. Hosted deployment workflow See `DEPLOY.md` for the full step-by-step guide. - [x] CORS origins configurable via `CORS_ORIGINS` and `CORS_ORIGIN_REGEX` env vars - [x] `vercel.json` at repo root building `frontend/` - [x] `frontend/.env.example` documenting `VITE_API_BASE_URL` - [x] `.github/workflows/ci.yml` — type-check + build on PR / push - [x] `.github/workflows/vercel-deploy.yml` — production push, preview PR - [x] `render.yaml` blueprint for the backend container with persistent disk - [x] `DEPLOY.md` — switching git remote, secrets, day-to-day workflow - [ ] First production deploy executed (Vercel + Render URLs captured) - [ ] Custom domain wired up on Vercel (optional) ## 10. Open follow-ups - Drag-and-drop on the planner (Phase 2 / Runn parity). - Right-side detail panels for People and Projects (Phase 3 polish). - Responsive tablet breakpoints below 1024px. - OAuth2 / SSO upgrade path is explicitly v2 territory and out of scope here.