# FabDash **Fabrication Dashboard** — A sleek, modern project management & scheduling application built for fabrication workflows. ![Version](https://img.shields.io/badge/version-1.0.0-gold) ![Stack](https://img.shields.io/badge/stack-React%20%2B%20Flask%20%2B%20SQLite-informational) ![Theme](https://img.shields.io/badge/theme-Dark%20%2F%20Gold-yellow) ![Docker](https://img.shields.io/badge/deployment-Single%20Docker%20Container-blue) --- ## Table of Contents - [Overview](#overview) - [Tech Stack](#tech-stack) - [Project Structure](#project-structure) - [Features](#features) - [Interaction Reference](#interaction-reference) - [API Reference](#api-reference) - [Component Architecture](#component-architecture) - [Unraid Installation](#unraid-installation) - [Updating FabDash on Unraid](#updating-fabdash-on-unraid) - [Local Development](#local-development) - [Environment Variables](#environment-variables) - [Database Schema](#database-schema) - [Roadmap](#roadmap) --- ## 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 ```js // 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:** ```json 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 ```bash 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 3–5 minutes (downloads Node + Python layers, compiles React). ```bash docker build -t fabdash:latest . ``` Verify the image exists: ```bash docker images | grep fabdash ``` --- ### Step 4 — Create the Data Directory ```bash 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 Device** → **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 FabDash at `http://YOUR_UNRAID_IP:8080` #### Volume / Path Mapping Click **Add another Path, Port, Variable, Label or Device** → **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 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: ```bash 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 `fabdash` → **Logs**, or via SSH: ```bash 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 ```bash 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 ```bash docker logs fabdash --tail 20 ``` --- ## Data Backup Your database lives at: ``` /mnt/user/appdata/fabdash/data/fabdash.db ``` **Manual backup:** ```bash 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:** ```bash 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:** ```bash 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 ```sql 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: ```python 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)* - [x] Dark/gold Tailwind design system - [x] FullCalendar — Month, Week, and Day views - [x] Drag-and-drop deliverable rescheduling - [x] Multi-deliverable project creation with inline rows - [x] Add / Edit / Delete for projects and deliverables - [x] Color-coded projects (12-swatch palette + custom hex) - [x] Google Drive folder link per project - [x] Deliverable Focus View — slide-up drawer, horizontal timeline - [x] Click any Focus View card to activate/select it - [x] Active deliverable gold highlight in Focus View - [x] Status badges — Upcoming / In Progress / Completed / Overdue - [x] Auto-overdue detection (computed at API read time, no cron job) - [x] Right-click context menus — calendar events, sidebar rows, Focus View cards - [x] Double-click shortcuts — events, sidebar rows, project header, Focus View cards - [x] Quick status change from Focus View right-click menu - [x] Flask REST API + SQLite persistence - [x] Cascade delete (project → deliverables) - [x] Inline schema migrations (no manual flask db upgrade needed) - [x] Single Docker container deployment (multi-stage build) - [x] 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.*