2026-03-05 17:05:47 -06:00
2026-03-05 16:58:36 -06:00
2026-03-05 17:05:47 -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

FABDASH

Fabrication Dashboard — A sleek, modern project management & scheduling application built for fabrication workflows. Repo: https://github.com/jasonMPM/fabdash

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 Focus View timeline, workload heatmap, agenda panel, right-click context menus, keyboard shortcuts, and a collapsible branded sidebar — 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 — version must match all FC packages)
@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, select events
react-chrono 2.6.0 Focus View timeline (pinned — v2.7+ requires React 19)
Zustand ^4.5.5 Global state management
Axios ^1.7.9 HTTP client
date-fns ^3.6.0 Date formatting and calculations

Dependency notes:

  • All FullCalendar packages are pinned to exactly 6.1.15 (no ^) to prevent version mismatch build errors
  • react-chrono is pinned to 2.6.0 — versions ≥ 2.7.0 require React 19 which breaks the build
  • npm install --legacy-peer-deps is used in the Dockerfile and local dev to handle peer dependency conflicts

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 Docker volume so data survives all container restarts and image rebuilds
  • Inline migrations — New columns are applied automatically on startup via _run_migrations() in app/__init__.py; no flask db upgrade commands needed

Project Structure

fabdash/
├── Dockerfile                       # Multi-stage: Node (React build) → Python (Flask)
├── docker-compose.yml
├── .env.example
├── .gitignore
├── README.md
│
├── backend/
│   ├── run.py
│   ├── config.py
│   ├── requirements.txt
│   └── app/
│       ├── __init__.py              # App factory + inline schema migrations
│       ├── extensions.py
│       ├── models.py                # Project + Deliverable ORM models
│       └── routes/
│           ├── __init__.py
│           ├── projects.py          # CRUD + drive_url support
│           └── deliverables.py
│
└── frontend/
    ├── package.json                 # react-chrono@2.6.0, @fullcalendar/* pinned to 6.1.15
    ├── vite.config.js               # optimizeDeps for FullCalendar, /api proxy
    ├── tailwind.config.js
    ├── postcss.config.js
    ├── index.html
    └── public/
        └── logo.png                 # ← Drop your square logo here
    └── src/
        ├── main.jsx
        ├── App.jsx                  # Sidebar layout, keyboard shortcuts, toast container
        ├── api/
        │   ├── projects.js
        │   └── deliverables.js
        ├── components/
        │   ├── Calendar/
        │   │   ├── MainCalendar.jsx      # Events, drag-drop, tooltip, select, heatmap toggle
        │   │   ├── EventTooltip.jsx      # Hover preview card
        │   │   ├── AgendaPanel.jsx       # Upcoming deliverables (next 60 days)
        │   │   └── WorkloadHeatmap.jsx   # 20-week density heatmap + stat cards
        │   ├── Projects/
        │   │   ├── ProjectList.jsx       # Logo header, tab toggle, empty state
        │   │   ├── ProjectCard.jsx       # Drive link, dblclick, right-click
        │   │   └── ProjectModal.jsx      # Drive URL field
        │   ├── Deliverables/
        │   │   └── DeliverableModal.jsx
        │   ├── FocusView/
        │   │   ├── FocusDrawer.jsx
        │   │   ├── FocusTimeline.jsx     # px padding fix for scale-105 clip
        │   │   └── DeliverableCard.jsx   # Click=select, dblclick=edit, right-click=menu
        │   └── UI/
        │       ├── Button.jsx
        │       ├── Badge.jsx
        │       ├── Modal.jsx             # Animated enter transition
        │       ├── Drawer.jsx
        │       ├── ContextMenu.jsx       # Floating right-click menu
        │       └── Toast.jsx             # 30-second undo toast with countdown ring
        ├── store/
        │   ├── useProjectStore.js
        │   ├── useFocusStore.js          # setActiveDeliverable action
        │   ├── useUIStore.js             # sidebarOpen, sidebarTab, showHeatmap
        │   └── useToastStore.js          # Toast queue management
        ├── utils/
        │   ├── dateHelpers.js
        │   └── statusHelpers.js
        └── styles/
            └── globals.css              # FullCalendar dark theme, slide-up animation

Features

Calendar

  • Full-width calendar — Month, Week, and Day views via FullCalendar
  • Week numbers — ISO week numbers displayed in gold on the left edge
  • Drag-and-drop rescheduling — Move any deliverable to a new date; backend patches immediately with a 30-second undo toast
  • Date range drag-select — Click and drag across multiple days to open Add Deliverable modal pre-filled with the start date
  • Click empty date cell — Opens Add Deliverable modal pre-filled with that date
  • Hover tooltip — Shows project name, deliverable title, due date, and status badge without clicking
  • 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)

Workload Heatmap

Toggle between the calendar and heatmap with the ⬡ Heatmap button in the top-right of the main area.

  • 20-week grid — MonSun rows, week columns, centered 10 weeks before and after today
  • Color intensity — Cells shade from dark (no deliverables) to full gold (3+ deliverables)
  • Today ring — Current day highlighted with a white ring
  • Hover tooltip — Shows date + all deliverable titles and project names for that day (up to 5, with overflow count)
  • Click cell — Opens Focus View for the first deliverable on that day
  • Stat cards — Total / Upcoming / In Progress / Completed / Overdue counts across all projects

Sidebar

  • Collapsible — Click the tab or press B to collapse. The toggle becomes a branded square tile showing your logo
  • Projects tab — Project cards with deliverable rows; empty state SVG illustration with keyboard hint
  • Upcoming tab — Agenda panel showing all deliverables due in the next 60 days, grouped by date with sticky headers; Today highlighted in gold
  • Keyboard shortcut legend — Pinned at the bottom of the sidebar

Projects

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

Deliverables

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

Deliverable Focus View

  • Slide-up drawer — Opens from bottom of screen when clicking any calendar event or sidebar row
  • Horizontal timeline — All deliverables for the project rendered in chronological order
  • Active card — Highlighted in gold with a "Selected" badge floating above; properly padded so scale-105 transform never clips against scroll container edges
  • Click any card — Switches the active/selected card
  • Double-click any card — Opens Edit Deliverable modal
  • Right-click any card — Context menu with quick status change (all 4 statuses inline), Edit, and Delete

Right-Click Context Menu

Custom floating ContextMenu component auto-adjusts to stay within the viewport. Closes on outside click or Escape.

Trigger Menu Items
Calendar event Edit Deliverable, Open Focus View, Open Drive Folder*, 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 Google Drive URL is set on the project.

Undo Toast

After a drag-and-drop rescheduling, a toast appears at the bottom of the screen with:

  • The new date
  • An Undo button that patches back to the original date
  • A circular SVG countdown ring showing seconds remaining (30s default)
  • Auto-dismisses when the timer expires

Animations & Polish

  • Modal enter — Backdrop fades in, dialog scales up from 95% with a translateY, using requestAnimationFrame for reliable transitions
  • Sidebar slidetransition-[width] duration-300 for smooth collapse/expand
  • Toast slide-up@keyframes slide-up animation in globals.css
  • Heatmap cellshover:scale-125 on each cell for tactile feedback

Interaction Reference

Location Single Click Double Click Right Click
Calendar event Open Focus View Edit Deliverable modal Context menu
Calendar empty cell Add Deliverable (date pre-filled)
Calendar date range drag Add Deliverable (start date pre-filled)
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
Heatmap cell (with deliverables) Open Focus View for first deliverable

Keyboard Shortcuts

Key Action
N Open New Project modal
B Toggle sidebar open / collapsed
Calendar — previous month/week/day
Calendar — next month/week/day
T Calendar — jump to today
Esc Close any open modal or drawer

Shortcuts are blocked when focus is inside an input, textarea, or select field.


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 with Drive link:

POST /api/projects
{
  "name": "Tucson",
  "color": "#E67E22",
  "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-10", "status": "upcoming"    }
  ]
}

Component Architecture

App
├── <aside> Sidebar (collapsible, w-72 ↔ w-0)
│   └── ProjectList
│       ├── Header: [logo] FabDash + [+ Project]
│       ├── Tabs: [Projects] [Upcoming]
│       ├── ProjectCard (× N)              ← dblclick header, right-click
│       │   ├── DeliverableRow (× N)       ← click, dblclick, right-click
│       │   ├── DeliverableModal (local)
│       │   └── ContextMenu (local)
│       ├── AgendaPanel                    ← Upcoming tab
│       ├── Keyboard shortcut legend
│       └── ProjectModal
│
├── Sidebar toggle button
│   ├── Open:     ◀ arrow tab
│   └── Collapsed: [logo] square tile + ▶
│
├── <main> MainCalendar
│   ├── [⬡ Heatmap] toggle button
│   ├── FullCalendar                       ← eventDidMount: tooltip, dblclick, right-click
│   │   └── Events (drag, click, select)
│   ├── WorkloadHeatmap (toggled)
│   ├── EventTooltip (hover)
│   ├── DeliverableModal
│   └── ContextMenu
│
├── FocusDrawer (slide-up overlay)
│   ├── FocusTimeline
│   │   └── DeliverableCard (× N)          ← click=select, dblclick=edit, right-click
│   │       └── ContextMenu (local)
│   └── DeliverableModal
│
└── ToastContainer (fixed bottom-center)
    └── ToastItem (× N) — countdown ring + Undo

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 — Add Your Logo (Optional)

Place your square logo file at:

frontend/public/logo.png

Supported formats: .png, .jpg, .svg, .webp — rename to logo.png. If omitted, the sidebar header shows text only and the collapsed toggle shows "FD".


Step 4 — Build the Docker Image

docker build -t fabdash:latest .

First build takes 35 minutes (downloads Node + Python base layers, compiles React).

Verify the image:

docker images | grep fabdash

Step 5 — Create the Data Directory

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

Step 6 — Add the Container via Unraid Docker GUI

  1. Go to the Docker tab → Add Container
  2. 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

Add → Port:

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

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

Volume / Path Mapping

Add → Path:

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 persists across all rebuilds and reboots.

Environment Variables

Add → Variable for each:

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 7 — Apply and Launch

Click Apply. Open your browser: http://YOUR_UNRAID_IP:8080


Step 8 — Verify (Optional)

docker logs fabdash --tail 50

A healthy startup:

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

FabDash supports a custom square logo that appears in two places:

  1. Sidebar header — Next to the "FabDash" wordmark (32×32px, object-contain)
  2. Collapsed sidebar toggle — Replaces the arrow with a branded square tile (32×32px)

To add your logo:

# Copy your logo into the public directory
cp /path/to/your/logo.png /mnt/user/appdata/fabdash/frontend/public/logo.png

# Rebuild and restart
cd /mnt/user/appdata/fabdash
docker build -t fabdash:latest .

Then restart the container from the Unraid Docker GUI.

Supported formats: .png, .jpg, .svg, .webp — file must be named logo.png

If the file is missing, both image elements silently hide themselves via onError — no broken image icons, no errors.


Updating FabDash on Unraid

Step 1 — Pull latest 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

Docker tab → click fabdash container icon → Restart

Your database is untouched. New schema columns apply automatically on startup.

Step 3 — Verify

docker logs fabdash --tail 20

Data Backup

/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: Use the Appdata Backup/Restore plugin from Unraid Community Applications.


Local Development

Terminal 1 — Flask backend:

cd backend
python -m venv venv
source venv/bin/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 automatically via vite.config.js.

To use your logo in development, place it at frontend/public/logo.png — Vite serves public/ at the root URL automatically.


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
DATABASE_URL No sqlite:////app/data/fabdash.db 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 added to existing databases automatically at startup via _run_migrations() in app/__init__.py.

Auto-Overdue Logic

def effective_status(self):
    """Computed at read time — never modifies the database."""
    if self.status != 'completed' and self.due_date < date.today():
        return 'overdue'
    return self.status
  • Past-due + not completed → overdue
  • completed is never auto-downgraded, even if the date has passed
  • Stored status value is preserved; this is read-only logic

Inline Migration Pattern

New columns are applied to existing databases automatically on app startup:

def _run_migrations():
    migrations = [
        'ALTER TABLE projects ADD COLUMN drive_url VARCHAR(500)',
    ]
    with db.engine.connect() as conn:
        for stmt in migrations:
            try:
                conn.execute(text(stmt))
                conn.commit()
            except Exception:
                pass  # Column already exists — safe to ignore

To add a new column in a future version, append its ALTER TABLE statement to the migrations list.


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; scale-105 clip fix
  • Active deliverable gold highlight with "Selected" badge
  • 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
  • 30-second undo toast with circular countdown ring after drag-and-drop
  • Hover tooltip on calendar events (project, title, date, status)
  • Collapsible sidebar with branded logo tile when collapsed
  • Custom logo support — frontend/public/logo.png
  • Animated modal enter transitions (backdrop + scale + translateY)
  • Keyboard shortcuts — N, B, ← →, T, Esc
  • Keyboard shortcut legend pinned to sidebar footer
  • Workload heatmap — 20-week density grid with stat cards
  • Date range drag-select on calendar
  • ISO week numbers on calendar
  • Agenda / Upcoming panel in sidebar (next 60 days)
  • Empty state SVG illustration with keyboard hint
  • Flask REST API + SQLite persistence
  • Cascade delete (project → deliverables)
  • Inline schema migrations (auto-applied on startup)
  • Multi-stage Docker build (Node → Python, single container)
  • Unraid installation — local build + Docker GUI management

v1.1 — Polish & UX (in progress)

  • Animated modal and drawer exit transitions
  • Drag-and-drop between projects (reassign deliverable)
  • Mini calendar in sidebar for quick date navigation
  • Responsive mobile layout

v1.2 — Calendar Enhancements

  • Recurring deliverables (weekly, bi-weekly, monthly)
  • Bulk status update from heatmap selection
  • Print / export calendar view to PDF
  • Custom calendar event display (show status color, not just project color)

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
  • Kanban board (columns by status)
  • Cross-project timeline
  • Heatmap drill-down — click cell to expand day detail panel
  • Archived projects with searchable history

v3.0 — Intelligence Layer

  • AI-assisted scheduling suggestions
  • Conflict detection — flag overloaded days
  • Natural language deliverable input

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%