2026-03-05 15:44:46 -06:00
2026-03-05 14:05:41 -06:00
2026-03-05 15:44:46 -06:00
2026-03-05 12:13:22 -06:00
2026-03-05 12:13:22 -06:00
2026-03-05 12:13:22 -06:00
2026-03-05 12:37:42 -06:00
2026-03-05 15:24:34 -06:00

FabDash

Fabrication Dashboard — A sleek, modern project management & scheduling application built for fabrication workflows.

Version Stack Theme Docker


Table of Contents


Overview

FabDash is a self-hosted, full-stack project management dashboard built for fabrication teams. It features a large interactive calendar, multi-deliverable project tracking, drag-and-drop scheduling, a per-project timeline Focus View, and a rich right-click context menu system. All wrapped in a dark/gold UI and deployed as a single Docker container with no external dependencies.


Tech Stack

Frontend

Package Version Purpose
React ^18.3.1 UI framework
Vite ^5.4.11 Build tool and dev server
Tailwind CSS ^3.4.15 Utility-first styling
@fullcalendar/core 6.1.15 FullCalendar core (pinned)
@fullcalendar/react 6.1.15 Calendar component
@fullcalendar/daygrid 6.1.15 Month/week grid view
@fullcalendar/timegrid 6.1.15 Time-slot view
@fullcalendar/interaction 6.1.15 Drag-and-drop, click events
react-chrono 2.6.0 Focus View timeline (pinned — last React 18 compatible)
Zustand ^4.5.5 Global state management
Axios ^1.7.9 HTTP client
date-fns ^3.6.0 Date formatting

Note: All FullCalendar packages are pinned to exactly 6.1.15 to prevent version mismatch errors during Docker builds. react-chrono is pinned to 2.6.0 — versions ≥ 2.7.0 require React 19.

Backend

Package Version Purpose
Flask 3.1.0 REST API + static file serving
Flask-SQLAlchemy 3.1.1 ORM for SQLite
Flask-Migrate 4.0.7 Schema migration support
Flask-CORS 5.0.0 Cross-origin support (dev)
Gunicorn 23.0.0 Production WSGI server

Database

  • SQLite — Zero-config, file-based persistence at /app/data/fabdash.db
  • Mounted as a named Docker volume so data survives all container restarts and rebuilds
  • Inline migrations — New columns are applied automatically on startup via _run_migrations() in app/__init__.py; no manual migration commands required

Project Structure

fabdash/
├── Dockerfile
├── docker-compose.yml
├── .env.example
├── .gitignore
├── README.md
│
├── backend/
│   ├── run.py
│   ├── config.py
│   ├── requirements.txt
│   └── app/
│       ├── __init__.py          # App factory + inline migrations
│       ├── extensions.py
│       ├── models.py            # Project + Deliverable models
│       └── routes/
│           ├── __init__.py
│           ├── projects.py
│           └── deliverables.py
│
└── frontend/
    ├── package.json
    ├── vite.config.js           # optimizeDeps for FullCalendar
    ├── tailwind.config.js
    ├── postcss.config.js
    ├── index.html
    └── src/
        ├── main.jsx
        ├── App.jsx
        ├── api/
        │   ├── projects.js
        │   └── deliverables.js
        ├── components/
        │   ├── Calendar/
        │   │   └── MainCalendar.jsx     # dblclick + right-click on events
        │   ├── Projects/
        │   │   ├── ProjectList.jsx
        │   │   ├── ProjectCard.jsx      # dblclick + right-click, Drive link
        │   │   └── ProjectModal.jsx     # Drive URL input field
        │   ├── Deliverables/
        │   │   └── DeliverableModal.jsx
        │   ├── FocusView/
        │   │   ├── FocusDrawer.jsx
        │   │   ├── FocusTimeline.jsx    # Click any card to activate
        │   │   └── DeliverableCard.jsx  # dblclick + right-click, status menu
        │   └── UI/
        │       ├── Button.jsx
        │       ├── Badge.jsx
        │       ├── Modal.jsx
        │       ├── Drawer.jsx
        │       └── ContextMenu.jsx      # Floating right-click menu
        ├── store/
        │   ├── useProjectStore.js
        │   └── useFocusStore.js         # setActiveDeliverable action
        ├── utils/
        │   ├── dateHelpers.js
        │   └── statusHelpers.js
        └── styles/
            └── globals.css

Features

Calendar

  • Large full-width calendar — Month, Week, and Day views via FullCalendar
  • Drag-and-drop rescheduling — Move any deliverable to a new date; backend patches immediately
  • Click empty date cell — Opens Add Deliverable modal pre-filled with that date
  • Single-click event — Opens Deliverable Focus View
  • Double-click event — Opens Edit Deliverable modal directly
  • Right-click event — Context menu (Edit, Focus View, Open Drive, Delete)

Projects

  • Multi-deliverable projects — Add unlimited deliverables per project, each with its own title, due date, and status
  • Inline creation — Add all deliverables while creating the project in a single form submission
  • Color coding — 12-swatch palette + custom hex input; color appears on calendar events and sidebar cards
  • Google Drive link — Optional Drive folder URL stored per project; appears as a button on the project card and in context menus
  • Double-click project header — Opens Edit Project modal instantly
  • Right-click project header — Context menu (Edit, Open Drive, Delete)

Deliverables

  • Status tracking — Upcoming / In Progress / Completed / Overdue
  • Auto-overdue detection — Any deliverable whose due date has passed is automatically returned as overdue by the API unless it is marked completed. Logic is computed at read time — no cron job needed
  • Double-click sidebar row — Opens Edit Deliverable modal
  • Right-click sidebar row — Context menu (Edit, Focus View, Delete)

Deliverable Focus View

  • Slide-up drawer — Triggered by clicking any calendar event or sidebar deliverable
  • Horizontal timeline — All deliverables for the project rendered in date order
  • Active card highlighting — Clicked deliverable glows in gold with a "Selected" badge
  • Click any card — Switches the active/selected card without closing the drawer
  • Double-click any card — Opens Edit Deliverable modal
  • Right-click any card — Context menu with quick status change (all 4 statuses), Edit, and Delete

Right-Click Context Menu

A custom floating ContextMenu component appears at the cursor position and auto-adjusts to stay within the viewport. It closes on outside click or Escape. Available everywhere:

Trigger Menu Items
Calendar event Edit Deliverable, Open Focus View, Open Drive*, Delete
Sidebar project header Edit Project, Open Drive*, Delete Project
Sidebar deliverable row Edit Deliverable, Open Focus View, Delete
Focus View card Edit Deliverable, Mark Upcoming/In Progress/Completed/Overdue, Delete

*Only shown when a Drive URL is set on the project.

Theme & Design System

// tailwind.config.js — custom tokens
colors: {
  gold:               '#C9A84C',   // Primary accent
  'gold-light':       '#E8C96A',
  'gold-muted':       '#8A6E2F',
  surface:            '#111111',   // Page background
  'surface-raised':   '#1A1A1A',   // Sidebar, drawer
  'surface-elevated': '#242424',   // Cards
  'surface-border':   '#2E2E2E',   // Borders
  'text-primary':     '#F5F5F5',
  'text-muted':       '#888888',
}

Interaction Reference

Location Single Click Double Click Right Click
Calendar event Open Focus View Edit Deliverable modal Context menu
Calendar empty cell Add Deliverable (pre-filled date)
Sidebar project header Edit Project modal Context menu
Sidebar deliverable row Open Focus View Edit Deliverable modal Context menu
Focus View card Select / activate Edit Deliverable modal Context menu
Focus View active card Edit button Edit Deliverable modal

API Reference

Projects

Method Endpoint Description
GET /api/projects List all projects with nested deliverables
POST /api/projects Create project (with optional inline deliverables)
GET /api/projects/:id Get single project
PATCH /api/projects/:id Update name, color, description, or drive_url
DELETE /api/projects/:id Delete project + cascade deliverables

Deliverables

Method Endpoint Description
GET /api/deliverables?project_id=:id List deliverables for a project
POST /api/deliverables Create deliverable
PATCH /api/deliverables/:id Update title, due_date, or status
DELETE /api/deliverables/:id Delete deliverable

Example — Create Project:

POST /api/projects
{
  "name": "CODA",
  "color": "#4A90D9",
  "description": "ADA Call Column and Shelter Equipment Box",
  "drive_url": "https://drive.google.com/drive/folders/your-folder-id",
  "deliverables": [
    { "title": "Cut Extrusion to Length",       "due_date": "2026-03-05", "status": "in_progress" },
    { "title": "Cut Faceplates and Top Plates",  "due_date": "2026-03-06", "status": "upcoming"    },
    { "title": "Fabricate Shelter Assembly Box", "due_date": "2026-03-11", "status": "upcoming"    }
  ]
}

Component Architecture

App
├── Sidebar
│   ├── ProjectList
│   │   └── ProjectCard (× N)            ← dblclick + right-click
│   │       ├── DeliverableRow (× N)     ← dblclick + right-click
│   │       ├── DeliverableModal (local)
│   │       └── ContextMenu (local)
│   └── Button ["+ Project"]
│
├── MainCalendar                          ← FullCalendar + eventDidMount
│   ├── FullCalendar events               ← dblclick + right-click via DOM
│   ├── DeliverableModal
│   └── ContextMenu
│
└── FocusDrawer (slide-up)
    ├── FocusTimeline
    │   └── DeliverableCard (× N)         ← click=select, dblclick=edit, right-click=menu
    │       └── ContextMenu (local)
    └── DeliverableModal

Unraid Installation

FabDash is installed by cloning the repo and building the Docker image locally on your Unraid server via SSH, then managing the container through the Unraid Docker GUI.


Step 1 — Enable SSH on Unraid

Go to Settings → Management Access and confirm SSH is enabled. Note your Unraid server's local IP address from the dashboard header.


Step 2 — SSH into Unraid and Clone the Repo

ssh root@YOUR_UNRAID_IP
cd /mnt/user/appdata
git clone https://github.com/yourname/fabdash.git
cd fabdash

Step 3 — Build the Docker Image

Builds the image on your Unraid server and tags it fabdash:latest. First build takes 35 minutes (downloads Node + Python layers, compiles React).

docker build -t fabdash:latest .

Verify the image exists:

docker images | grep fabdash

Step 4 — Create the Data Directory

mkdir -p /mnt/user/appdata/fabdash/data

Step 5 — Add the Container via Unraid Docker GUI

  1. Go to the Docker tab in Unraid
  2. Click Add Container at the bottom
  3. Toggle Advanced View in the top-right of the form

Basic Settings

Field Value
Name fabdash
Repository fabdash:latest
Docker Hub URL (leave blank — local image)
Network Type Bridge
Console shell command bash
Privileged Off

Port Mapping

Click Add another Path, Port, Variable, Label or DevicePort:

Field Value
Name Web UI
Container Port 8080
Host Port 8080
Connection Type TCP

Change Host Port if 8080 is already in use. Access FabDash at http://YOUR_UNRAID_IP:8080

Volume / Path Mapping

Click Add another Path, Port, Variable, Label or DevicePath:

Field Value
Name AppData
Container Path /app/data
Host Path /mnt/user/appdata/fabdash/data
Access Mode Read/Write

Your database (fabdash.db) lives here and survives all rebuilds and reboots.

Environment Variables

Add each as Variable:

Name Key Value
Secret Key SECRET_KEY (generate below)
Flask Environment FLASK_ENV production
Database URL DATABASE_URL sqlite:////app/data/fabdash.db

Generate a strong SECRET_KEY from the Unraid terminal:

cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 48 | head -n 1

Extra Parameters

Field Value
Extra Parameters --restart unless-stopped

Step 6 — Apply and Launch

Click Apply. The container starts using your locally built image.

Open your browser: http://YOUR_UNRAID_IP:8080


Step 7 — Verify (Optional)

Check logs from the Unraid Docker tab → click fabdashLogs, or via SSH:

docker logs fabdash --tail 50

A healthy startup looks like:

[INFO] Starting gunicorn 23.0.0
[INFO] Listening at: http://0.0.0.0:8080
[INFO] Worker booting (pid: ...)

Updating FabDash on Unraid

Step 1 — Pull latest code and rebuild

ssh root@YOUR_UNRAID_IP
cd /mnt/user/appdata/fabdash
git pull
docker build -t fabdash:latest .

Step 2 — Restart via Unraid Docker GUI

Go to Docker tab → click the fabdash container icon → Restart

The container reloads with the new image. Your database is untouched. New schema columns (if any) are applied automatically on startup.

Step 3 — Verify

docker logs fabdash --tail 20

Data Backup

Your database lives at:

/mnt/user/appdata/fabdash/data/fabdash.db

Manual backup:

cp /mnt/user/appdata/fabdash/data/fabdash.db \
   /mnt/user/backups/fabdash-$(date +%Y%m%d-%H%M).db

Automated backup: Use the Appdata Backup/Restore plugin from Unraid Community Applications to schedule regular backups of /mnt/user/appdata/fabdash/.


Local Development

Terminal 1 — Flask backend:

cd backend
python -m venv venv
source venv/bin/activate      # Windows: venv\Scripts\activate
pip install -r requirements.txt
export FLASK_ENV=development
flask run --port 5000

Terminal 2 — React frontend:

cd frontend
npm install --legacy-peer-deps
npm run dev                   # http://localhost:5173

Vite proxies all /api/* requests to Flask on port 5000 via vite.config.js. No CORS configuration needed in development.


Environment Variables

Variable Required Default Description
SECRET_KEY Yes Flask session secret. Use a long random string
FLASK_ENV No production Set to development for debug mode and auto-reload
DATABASE_URL No sqlite:////app/data/fabdash.db Full SQLite path (4 slashes = absolute path)

Database Schema

CREATE TABLE projects (
  id          INTEGER  PRIMARY KEY AUTOINCREMENT,
  name        TEXT     NOT NULL,
  color       TEXT     NOT NULL DEFAULT '#C9A84C',
  description TEXT,
  drive_url   VARCHAR(500),
  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);

drive_url is applied to existing databases automatically via the inline migration in app/__init__.py — no manual SQL required.

Auto-Overdue Logic

The Deliverable.effective_status() method is called on every API read:

def effective_status(self):
    if self.status != 'completed' and self.due_date < date.today():
        return 'overdue'
    return self.status
  • Past-due + not completed → returns overdue to the frontend
  • completed deliverables are never auto-downgraded regardless of date
  • The stored database value is never modified by this logic

Roadmap

v1.0 — Core Release (current)

  • Dark/gold Tailwind design system
  • FullCalendar — Month, Week, and Day views
  • Drag-and-drop deliverable rescheduling
  • Multi-deliverable project creation with inline rows
  • Add / Edit / Delete for projects and deliverables
  • Color-coded projects (12-swatch palette + custom hex)
  • Google Drive folder link per project
  • Deliverable Focus View — slide-up drawer, horizontal timeline
  • Click any Focus View card to activate/select it
  • Active deliverable gold highlight in Focus View
  • Status badges — Upcoming / In Progress / Completed / Overdue
  • Auto-overdue detection (computed at API read time, no cron job)
  • Right-click context menus — calendar events, sidebar rows, Focus View cards
  • Double-click shortcuts — events, sidebar rows, project header, Focus View cards
  • Quick status change from Focus View right-click menu
  • Flask REST API + SQLite persistence
  • Cascade delete (project → deliverables)
  • Inline schema migrations (no manual flask db upgrade needed)
  • Single Docker container deployment (multi-stage build)
  • Unraid installation — local build + Docker GUI management

v1.1 — Polish & UX

  • Keyboard shortcuts (N = new project, Esc = close modal/drawer, arrow keys = calendar nav)
  • 30-second undo toast for drag-and-drop moves
  • Animated modal and drawer enter/exit transitions
  • Hover tooltip on calendar events (preview without opening Focus View)
  • Responsive layout with collapsible sidebar
  • Empty state illustrations

v1.2 — Calendar Enhancements

  • Agenda sidebar showing all upcoming deliverables across projects
  • Date range selection for bulk deliverable creation
  • "Today" jump button
  • Week numbers in calendar header
  • Mini calendar thumbnail for quick date navigation

v2.0 — Auth & Multi-user

  • User login (Flask-Login + JWT)
  • Multi-user support with project ownership
  • Role-based access per project (Owner / Editor / Viewer)
  • Activity log per project
  • Comment threads on deliverables

v2.1 — Notifications & Integrations

  • In-app notification center for approaching due dates
  • Email reminders at configurable intervals (Flask-Mail)
  • iCal / Google Calendar export per project
  • Slack webhook for deliverable status changes
  • CSV import/export for bulk project setup

v2.2 — Advanced Views

  • Gantt view alternate layout
  • Kanban board (columns by status)
  • Cross-project timeline view
  • Workload heatmap — deliverable density per day
  • Archived projects with searchable history

v3.0 — Intelligence Layer

  • AI-assisted scheduling suggestions based on project cadence
  • Conflict detection — flag overloaded days
  • Natural language deliverable input ("Add final draft due next Friday to CODA")

Built with intention. No subscriptions. No bloat. Just your fabrication workflow.

Description
No description provided
Readme 564 KiB
Languages
JavaScript 59%
HTML 32.2%
Python 6.4%
CSS 1.8%
Dockerfile 0.6%