FabDash
Fabrication Dashboard — A sleek, modern project management & scheduling application built for fabrication workflows.
Table of Contents
- Overview
- Core Philosophy
- Tech Stack
- Project Structure
- Data Architecture
- Features
- API Reference
- Component Architecture
- Docker Deployment
- Local Development
- Environment Variables
- Database Schema
- Roadmap
Overview
FabDash is a self-hosted, full-stack project management and scheduling application built for fabrication teams who need a clean, fast, and visually intuitive way to manage multi-deliverable projects across time. It combines a large interactive calendar with a per-project timeline focus system — all wrapped in a dark, modern UI with gold accents.
Deployed as a single Docker container, FabDash runs anywhere Docker runs with zero external dependencies.
Core Philosophy
| Principle | Implementation |
|---|---|
| Clarity over clutter | One focused view at a time — calendar or timeline, never both fighting for space |
| Speed of interaction | Drag, click, and edit without losing context |
| Data ownership | Local SQLite persistence, no cloud dependency |
| Easy deployment | Single Docker container — one command to run anywhere |
| Visual hierarchy | Gold accents guide the eye to what matters most |
Tech Stack
Frontend
| Package | Version | Purpose |
|---|---|---|
| React | ^18.x | UI component framework |
| Vite | ^5.x | Build tool and dev server |
| Tailwind CSS | ^3.x | Utility-first styling with custom tokens |
| @fullcalendar/react | ^6.x | Main calendar view |
| @fullcalendar/daygrid | ^6.x | Month/week/day grid views |
| @fullcalendar/interaction | ^6.x | Drag-and-drop and click events |
| @fullcalendar/timegrid | ^6.x | Time-slot grid view |
| react-chrono | ^2.x | Deliverable Focus View timeline |
| Zustand | ^4.x | Global state management |
| Axios | ^1.x | HTTP client for Flask API |
| React Router | ^6.x | Client-side routing |
| date-fns | ^3.x | Date formatting and manipulation |
Backend
| Package | Version | Purpose |
|---|---|---|
| Flask | ^3.x | REST API server + static file serving |
| Flask-SQLAlchemy | ^3.x | ORM for SQLite |
| Flask-CORS | ^4.x | Cross-origin support for React dev server |
| Flask-Migrate | ^4.x | Database schema migrations |
| Gunicorn | ^21.x | Production WSGI server (inside Docker) |
Database
- SQLite — Zero-config, file-based persistence at
/app/data/fabdash.db - Mounted as a Docker volume so data survives container restarts
Project Structure
fabdash/
├── Dockerfile
├── docker-compose.yml
├── .env.example
├── .gitignore
├── README.md
│
├── backend/
│ ├── run.py
│ ├── config.py
│ ├── requirements.txt
│ └── app/
│ ├── __init__.py
│ ├── extensions.py
│ ├── models.py
│ └── routes/
│ ├── projects.py
│ └── deliverables.py
│
└── frontend/
├── package.json
├── vite.config.js
├── tailwind.config.js
├── index.html
└── src/
├── main.jsx
├── App.jsx
├── api/
│ ├── projects.js
│ └── deliverables.js
├── components/
│ ├── Calendar/
│ │ ├── MainCalendar.jsx
│ │ ├── EventChip.jsx
│ │ └── CalendarToolbar.jsx
│ ├── Projects/
│ │ ├── ProjectList.jsx
│ │ ├── ProjectCard.jsx
│ │ └── ProjectModal.jsx
│ ├── Deliverables/
│ │ ├── DeliverableModal.jsx
│ │ └── DeliverableChip.jsx
│ ├── FocusView/
│ │ ├── FocusDrawer.jsx
│ │ ├── FocusTimeline.jsx
│ │ └── DeliverableCard.jsx
│ └── UI/
│ ├── Button.jsx
│ ├── Badge.jsx
│ ├── Modal.jsx
│ └── Drawer.jsx
├── store/
│ ├── useProjectStore.js
│ └── useFocusStore.js
├── hooks/
│ ├── useProjects.js
│ └── useDeliverables.js
├── utils/
│ ├── dateHelpers.js
│ └── statusHelpers.js
└── styles/
└── globals.css
Data Architecture
Relationships
Project (1) ─────────────── (many) Deliverable
│ │
├── id (PK) ├── id (PK)
├── name ├── project_id (FK → projects.id)
├── color (hex) ├── title
├── description ├── due_date
└── created_at ├── status (enum)
└── created_at
Each Project owns one or more Deliverables. Every deliverable inherits the parent project's color when rendered on the calendar. Deleting a project cascades to remove all its deliverables.
Features
Main Calendar View
The primary interface is a large, full-width FullCalendar grid with three switchable view modes:
- Month View — Full month overview, all deliverables visible as colored event chips
- Week View — Focused 7-day view with time slots
- Day View — Single-day granularity for heavy scheduling days
Interactions:
- Drag-and-drop any deliverable to a new date — backend is patched immediately
- Click any event to open the Deliverable Focus View
- Right-click an event for quick actions: Edit, Delete, Open Project
- Click an empty date cell to open the Add Deliverable modal pre-filled with that date
Project & Deliverable Management
Adding a Project:
- Click "+ New Project" in the sidebar
- Enter project name, optional description, and choose a color swatch
- Add one or more deliverables inline before saving
- Submit — project and all deliverables persist in a single transaction
Editing & Deleting:
- Edit any deliverable from the sidebar, calendar event click, or Focus View
- Delete a project via the sidebar (confirmation dialog warns of cascade delete)
- Delete individual deliverables from their edit modal or via right-click context menu
Deliverable Focus View
Clicking any calendar event opens a slide-up drawer with the full project timeline.
- All deliverables render as a horizontal timeline via
react-chronoinHORIZONTAL_ALLmode - The clicked deliverable is highlighted with a gold glow and elevated scale
- Other deliverables appear as dimmed context nodes
- Drawer dismisses on outside click,
Escapekey, or close button - Edit Deliverable and Edit Project buttons are available inline
[ Deliverable 1 ]────────[ Deliverable 2 ]────────[ ★ Deliverable 3 ★ ]
Jan 15, 2026 Feb 28, 2026 Mar 28, 2026
Completed In Progress ← ACTIVE / FOCUSED
Color Coding System
Each project is assigned a hex color used across:
- Background of event chips on the calendar grid
- Left border accent on sidebar project cards
- Timeline node color in the Focus View for non-active deliverables
Colors are selectable from a curated 12-swatch palette (all readable on dark backgrounds) plus a custom hex input.
Theme & Design System
// tailwind.config.js
theme: {
extend: {
colors: {
gold: '#C9A84C',
'gold-light': '#E8C96A',
'gold-muted': '#8A6E2F',
surface: '#111111',
'surface-raised': '#1A1A1A',
'surface-elevated': '#242424',
'surface-border': '#2E2E2E',
'text-primary': '#F5F5F5',
'text-muted': '#888888',
},
boxShadow: {
gold: '0 0 12px rgba(201, 168, 76, 0.4)',
},
fontFamily: {
sans: ['Inter', 'sans-serif'],
mono: ['JetBrains Mono', 'monospace'],
},
},
}
API Reference
Projects
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/projects |
Fetch all projects with nested deliverables |
POST |
/api/projects |
Create a new project |
GET |
/api/projects/:id |
Fetch a single project |
PATCH |
/api/projects/:id |
Update project name, color, or description |
DELETE |
/api/projects/:id |
Delete project and cascade deliverables |
Deliverables
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/deliverables?project_id=:id |
Fetch deliverables for a project |
POST |
/api/deliverables |
Create a new deliverable |
PATCH |
/api/deliverables/:id |
Update title, date, or status |
DELETE |
/api/deliverables/:id |
Delete a single deliverable |
Example — Create Project with Deliverables:
POST /api/projects
{
"name": "CODA",
"color": "#4A90D9",
"description": "Example fabrication project",
"deliverables": [
{ "title": "Deliverable 1 – Concept Brief", "due_date": "2026-01-15" },
{ "title": "Deliverable 2 – Draft Review", "due_date": "2026-02-28" },
{ "title": "Deliverable 3 – Final Submission", "due_date": "2026-03-28" }
]
}
Component Architecture
App
├── Sidebar
│ ├── ProjectList
│ │ └── ProjectCard (× N)
│ │ └── DeliverableChip (× N)
│ └── Button ["+ New Project"]
├── MainCalendar
│ ├── CalendarToolbar
│ └── FullCalendar
│ └── EventChip (× N)
├── ProjectModal
├── DeliverableModal
└── FocusDrawer
└── FocusTimeline
└── DeliverableCard (× N, one highlighted)
Docker Deployment
FabDash ships as a single Docker container. The build process compiles the React frontend with Vite, copies the static output into Flask's static/ folder, and serves everything through Gunicorn. No separate web server or container needed.
How It Works
Docker Build
├── Stage 1 (node:18-alpine)
│ └── npm run build → frontend/dist/
└── Stage 2 (python:3.12-slim)
├── Copy dist/ → backend/app/static/
├── pip install -r requirements.txt
├── Flask serves API at /api/*
├── Flask serves React at /* (catch-all → index.html)
└── Gunicorn runs on port 8080
Dockerfile
# ── Stage 1: Build React frontend ──────────────────────────────────────
FROM node:18-alpine AS frontend-build
WORKDIR /app/frontend
COPY frontend/package*.json ./
RUN npm ci
COPY frontend/ ./
RUN npm run build
# ── Stage 2: Flask + Gunicorn production image ─────────────────────────
FROM python:3.12-slim
WORKDIR /app
# Install Python dependencies
COPY backend/requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
# Copy Flask app
COPY backend/ ./
# Copy compiled React build into Flask static folder
COPY --from=frontend-build /app/frontend/dist ./app/static
# Persistent volume mount point for SQLite database
RUN mkdir -p /app/data
EXPOSE 8080
CMD ["gunicorn", "--bind", "0.0.0.0:8080", "--workers", "2", "run:app"]
docker-compose.yml
version: "3.9"
services:
fabdash:
build: .
container_name: fabdash
ports:
- "8080:8080"
volumes:
- fabdash-data:/app/data # Persists SQLite database across restarts
environment:
- FLASK_ENV=production
- DATABASE_URL=sqlite:////app/data/fabdash.db
- SECRET_KEY=${SECRET_KEY}
restart: unless-stopped
volumes:
fabdash-data:
Flask Catch-All Route (SPA Support)
Add this to backend/app/__init__.py so React Router handles all non-API routes:
import os
from flask import send_from_directory
@app.route("/", defaults={"path": ""})
@app.route("/<path:path>")
def serve_react(path):
static_folder = os.path.join(app.root_path, "static")
if path and os.path.exists(os.path.join(static_folder, path)):
return send_from_directory(static_folder, path)
return send_from_directory(static_folder, "index.html")
Deploy (One Command)
# Clone and run
git clone https://github.com/yourname/fabdash.git
cd fabdash
cp .env.example .env # Add your SECRET_KEY
docker compose up -d --build
# FabDash is live at http://localhost:8080
Useful Docker Commands
docker compose logs -f fabdash # Stream logs
docker compose down # Stop container
docker compose up -d --build # Rebuild and restart
docker exec -it fabdash flask db upgrade # Run migrations
Local Development
For active development, run frontend and backend separately so Vite HMR works:
Terminal 1 — Flask backend:
cd backend
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
flask db upgrade
flask run --port 5000
Terminal 2 — React frontend:
cd frontend
npm install
npm run dev # Vite dev server at http://localhost:5173
Set VITE_API_BASE_URL=http://localhost:5000/api in frontend/.env during development.
In production (Docker), the frontend calls /api/* relative to the same origin — no CORS needed.
Environment Variables
# .env (used by docker-compose.yml)
SECRET_KEY=replace-with-a-strong-random-key
FLASK_ENV=production
DATABASE_URL=sqlite:////app/data/fabdash.db
# frontend/.env (development only)
VITE_API_BASE_URL=http://localhost:5000/api
Database Schema
CREATE TABLE projects (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
color TEXT NOT NULL DEFAULT '#C9A84C',
description TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE deliverables (
id INTEGER PRIMARY KEY AUTOINCREMENT,
project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
title TEXT NOT NULL,
due_date DATE NOT NULL,
status TEXT NOT NULL DEFAULT 'upcoming'
CHECK(status IN ('upcoming','in_progress','completed','overdue')),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_deliverables_project ON deliverables(project_id);
CREATE INDEX idx_deliverables_due_date ON deliverables(due_date);
Roadmap
v1.0 — Core Release (current scope)
- Dark/gold design system with Tailwind custom tokens
- FullCalendar main view with Month / Week / Day modes
- Drag-and-drop deliverable rescheduling
- Project creation with multiple deliverables and color selection
- Add / Edit / Delete for projects and deliverables
- Deliverable Focus View — slide-up drawer with horizontal timeline
- Active deliverable highlighting in Focus View
- Status badges (Upcoming / In Progress / Completed / Overdue)
- Flask REST API with SQLite persistence
- Cascade delete (project → deliverables)
- Right-click context menu on calendar events
- Single Docker container deployment with persistent volume
v1.1 — Polish & UX
- Keyboard shortcuts (
N= new project,Esc= close modal, arrow keys = calendar nav) - Undo/redo for drag-and-drop with 30-second undo toast
- Animated transitions on drawer and modal open/close
- Deliverable sorting in Focus View (by date, by status)
- Empty state illustrations for no projects or no deliverables
- Responsive layout with collapsible sidebar for smaller screens
v1.2 — Enhanced Calendar
- Agenda sidebar showing all upcoming deliverables across projects
- Week numbers in calendar header
- Hover tooltip previewing deliverable details without opening Focus View
- Date range selection for bulk deliverable creation
- "Today" jump button with smooth scroll
v2.0 — Collaboration & Auth
- User authentication (Flask-Login + JWT)
- Multi-user support with project ownership and sharing
- Role-based access per project (Owner / Editor / Viewer)
- Activity log per project (who changed what, when)
- Comment threads on individual deliverables
v2.1 — Notifications & Integrations
- In-app notification center for approaching due dates
- Email reminders at configurable intervals before due date
- iCal / Google Calendar export per project
- Slack webhook integration for deliverable status changes
- CSV import/export for bulk project setup
v2.2 — Advanced Views
- Gantt view as an alternate layout
- Kanban board (columns: Upcoming / In Progress / Completed)
- Cross-project timeline showing all projects on one horizontal axis
- Workload heatmap showing deliverable density per day
- Archived projects with searchable history
v3.0 — Intelligence Layer
- AI-assisted scheduling suggestions based on project cadence
- Auto-detect and surface overdue deliverables on dashboard load
- Conflict detection — flag days with too many concurrent deliverables
- Natural language input ("Add final draft due next Friday to CODA")
Built with intention. No subscriptions. No bloat. Just your fabrication workflow.