Files
fabdash/README.md

751 lines
26 KiB
Markdown
Raw Normal View History

2026-03-05 16:52:31 -06:00
# FABDASH
2026-03-05 11:59:14 -06:00
2026-03-05 15:24:34 -06:00
**Fabrication Dashboard** — A sleek, modern project management & scheduling application built for fabrication workflows.
2026-03-05 16:52:31 -06:00
Repo: https://github.com/jasonMPM/fabdash
2026-03-05 11:53:28 -06:00
2026-03-05 12:13:22 -06:00
![Version](https://img.shields.io/badge/version-1.0.0-gold)
2026-03-05 11:53:28 -06:00
![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)
2026-03-05 11:59:14 -06:00
![Docker](https://img.shields.io/badge/deployment-Single%20Docker%20Container-blue)
2026-03-05 11:53:28 -06:00
---
## Table of Contents
- [Overview](#overview)
- [Tech Stack](#tech-stack)
- [Project Structure](#project-structure)
- [Features](#features)
2026-03-05 15:24:34 -06:00
- [Interaction Reference](#interaction-reference)
2026-03-05 16:13:12 -06:00
- [Keyboard Shortcuts](#keyboard-shortcuts)
2026-03-05 11:53:28 -06:00
- [API Reference](#api-reference)
2026-03-05 15:24:34 -06:00
- [Component Architecture](#component-architecture)
2026-03-05 12:13:22 -06:00
- [Unraid Installation](#unraid-installation)
2026-03-05 16:13:12 -06:00
- [Adding Your Logo](#adding-your-logo)
2026-03-05 12:20:22 -06:00
- [Updating FabDash on Unraid](#updating-fabdash-on-unraid)
2026-03-05 11:59:14 -06:00
- [Local Development](#local-development)
2026-03-05 11:53:28 -06:00
- [Environment Variables](#environment-variables)
- [Database Schema](#database-schema)
- [Roadmap](#roadmap)
2026-03-05 11:59:14 -06:00
2026-03-05 11:53:28 -06:00
---
## Overview
2026-03-05 16:13:12 -06:00
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.
2026-03-05 11:53:28 -06:00
---
## Tech Stack
2026-03-05 15:24:34 -06:00
### 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 |
2026-03-05 16:13:12 -06:00
| @fullcalendar/core | 6.1.15 | FullCalendar core (pinned — version must match all FC packages) |
2026-03-05 15:24:34 -06:00
| @fullcalendar/react | 6.1.15 | Calendar component |
| @fullcalendar/daygrid | 6.1.15 | Month/week grid view |
| @fullcalendar/timegrid | 6.1.15 | Time-slot view |
2026-03-05 16:13:12 -06:00
| @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) |
2026-03-05 15:24:34 -06:00
| Zustand | ^4.5.5 | Global state management |
| Axios | ^1.7.9 | HTTP client |
2026-03-05 16:13:12 -06:00
| date-fns | ^3.6.0 | Date formatting and calculations |
2026-03-05 15:24:34 -06:00
2026-03-05 16:13:12 -06:00
> **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
2026-03-05 15:24:34 -06:00
### 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`
2026-03-05 16:13:12 -06:00
- 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
2026-03-05 11:53:28 -06:00
---
## Project Structure
2026-03-05 11:59:14 -06:00
```
2026-03-05 11:53:28 -06:00
fabdash/
2026-03-05 16:13:12 -06:00
├── Dockerfile # Multi-stage: Node (React build) → Python (Flask)
2026-03-05 11:59:14 -06:00
├── docker-compose.yml
2026-03-05 11:53:28 -06:00
├── .env.example
├── .gitignore
2026-03-05 11:59:14 -06:00
├── README.md
├── backend/
│ ├── run.py
│ ├── config.py
│ ├── requirements.txt
│ └── app/
2026-03-05 16:13:12 -06:00
│ ├── __init__.py # App factory + inline schema migrations
2026-03-05 11:59:14 -06:00
│ ├── extensions.py
2026-03-05 16:13:12 -06:00
│ ├── models.py # Project + Deliverable ORM models
2026-03-05 11:59:14 -06:00
│ └── routes/
2026-03-05 12:13:22 -06:00
│ ├── __init__.py
2026-03-05 16:13:12 -06:00
│ ├── projects.py # CRUD + drive_url support
2026-03-05 11:59:14 -06:00
│ └── deliverables.py
└── frontend/
2026-03-05 16:13:12 -06:00
├── package.json # react-chrono@2.6.0, @fullcalendar/* pinned to 6.1.15
├── vite.config.js # optimizeDeps for FullCalendar, /api proxy
2026-03-05 11:59:14 -06:00
├── tailwind.config.js
2026-03-05 12:13:22 -06:00
├── postcss.config.js
2026-03-05 11:59:14 -06:00
├── index.html
2026-03-05 16:13:12 -06:00
└── public/
└── logo.png # ← Drop your square logo here
2026-03-05 11:59:14 -06:00
└── src/
├── main.jsx
2026-03-05 16:13:12 -06:00
├── App.jsx # Sidebar layout, keyboard shortcuts, toast container
2026-03-05 11:59:14 -06:00
├── api/
│ ├── projects.js
│ └── deliverables.js
├── components/
│ ├── Calendar/
2026-03-05 16:13:12 -06:00
│ │ ├── 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
2026-03-05 11:59:14 -06:00
│ ├── Projects/
2026-03-05 16:13:12 -06:00
│ │ ├── ProjectList.jsx # Logo header, tab toggle, empty state
│ │ ├── ProjectCard.jsx # Drive link, dblclick, right-click
│ │ └── ProjectModal.jsx # Drive URL field
2026-03-05 11:59:14 -06:00
│ ├── Deliverables/
2026-03-05 12:13:22 -06:00
│ │ └── DeliverableModal.jsx
2026-03-05 11:59:14 -06:00
│ ├── FocusView/
│ │ ├── FocusDrawer.jsx
2026-03-05 16:13:12 -06:00
│ │ ├── FocusTimeline.jsx # px padding fix for scale-105 clip
│ │ └── DeliverableCard.jsx # Click=select, dblclick=edit, right-click=menu
2026-03-05 11:59:14 -06:00
│ └── UI/
│ ├── Button.jsx
│ ├── Badge.jsx
2026-03-05 16:13:12 -06:00
│ ├── Modal.jsx # Animated enter transition
2026-03-05 15:24:34 -06:00
│ ├── Drawer.jsx
2026-03-05 16:13:12 -06:00
│ ├── ContextMenu.jsx # Floating right-click menu
│ └── Toast.jsx # 30-second undo toast with countdown ring
2026-03-05 11:59:14 -06:00
├── store/
│ ├── useProjectStore.js
2026-03-05 16:13:12 -06:00
│ ├── useFocusStore.js # setActiveDeliverable action
│ ├── useUIStore.js # sidebarOpen, sidebarTab, showHeatmap
│ └── useToastStore.js # Toast queue management
2026-03-05 11:59:14 -06:00
├── utils/
│ ├── dateHelpers.js
│ └── statusHelpers.js
└── styles/
2026-03-05 16:13:12 -06:00
└── globals.css # FullCalendar dark theme, slide-up animation
2026-03-05 11:59:14 -06:00
```
2026-03-05 11:53:28 -06:00
---
2026-03-05 12:13:22 -06:00
## Features
2026-03-05 11:53:28 -06:00
2026-03-05 15:24:34 -06:00
### Calendar
2026-03-05 16:13:12 -06:00
- **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
2026-03-05 15:24:34 -06:00
- **Click empty date cell** — Opens Add Deliverable modal pre-filled with that date
2026-03-05 16:13:12 -06:00
- **Hover tooltip** — Shows project name, deliverable title, due date, and status badge without clicking
2026-03-05 15:24:34 -06:00
- **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)
2026-03-05 16:13:12 -06:00
### 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
2026-03-05 15:24:34 -06:00
### Projects
2026-03-05 16:13:12 -06:00
- **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
2026-03-05 15:24:34 -06:00
- **Right-click project header** — Context menu (Edit, Open Drive, Delete)
### Deliverables
- **Status tracking** — Upcoming / In Progress / Completed / Overdue
2026-03-05 16:13:12 -06:00
- **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
2026-03-05 15:24:34 -06:00
- **Double-click sidebar row** — Opens Edit Deliverable modal
- **Right-click sidebar row** — Context menu (Edit, Focus View, Delete)
### Deliverable Focus View
2026-03-05 16:13:12 -06:00
- **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
2026-03-05 15:24:34 -06:00
- **Double-click any card** — Opens Edit Deliverable modal
2026-03-05 16:13:12 -06:00
- **Right-click any card** — Context menu with quick status change (all 4 statuses inline), Edit, and Delete
2026-03-05 15:24:34 -06:00
### Right-Click Context Menu
2026-03-05 16:13:12 -06:00
Custom floating `ContextMenu` component auto-adjusts to stay within the viewport. Closes on outside click or `Escape`.
2026-03-05 15:24:34 -06:00
| Trigger | Menu Items |
|---|---|
2026-03-05 16:13:12 -06:00
| Calendar event | Edit Deliverable, Open Focus View, Open Drive Folder*, Delete |
2026-03-05 15:24:34 -06:00
| Sidebar project header | Edit Project, Open Drive*, Delete Project |
| Sidebar deliverable row | Edit Deliverable, Open Focus View, Delete |
2026-03-05 16:13:12 -06:00
| 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 slide** — `transition-[width] duration-300` for smooth collapse/expand
- **Toast slide-up** — `@keyframes slide-up` animation in `globals.css`
- **Heatmap cells** — `hover:scale-125` on each cell for tactile feedback
2026-03-05 15:24:34 -06:00
---
## Interaction Reference
| Location | Single Click | Double Click | Right Click |
|---|---|---|---|
| Calendar event | Open Focus View | Edit Deliverable modal | Context menu |
2026-03-05 16:13:12 -06:00
| Calendar empty cell | Add Deliverable (date pre-filled) | — | — |
| Calendar date range drag | Add Deliverable (start date pre-filled) | — | — |
2026-03-05 15:24:34 -06:00
| 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 |
2026-03-05 16:13:12 -06:00
| 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.
2026-03-05 11:53:28 -06:00
2026-03-05 12:13:22 -06:00
---
2026-03-05 11:53:28 -06:00
2026-03-05 12:13:22 -06:00
## API Reference
2026-03-05 11:53:28 -06:00
2026-03-05 12:13:22 -06:00
### Projects
2026-03-05 11:53:28 -06:00
2026-03-05 12:13:22 -06:00
| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/api/projects` | List all projects with nested deliverables |
2026-03-05 12:20:22 -06:00
| `POST` | `/api/projects` | Create project (with optional inline deliverables) |
2026-03-05 12:13:22 -06:00
| `GET` | `/api/projects/:id` | Get single project |
2026-03-05 15:24:34 -06:00
| `PATCH` | `/api/projects/:id` | Update name, color, description, or drive_url |
2026-03-05 12:13:22 -06:00
| `DELETE` | `/api/projects/:id` | Delete project + cascade deliverables |
2026-03-05 11:53:28 -06:00
2026-03-05 12:13:22 -06:00
### Deliverables
2026-03-05 11:53:28 -06:00
2026-03-05 12:13:22 -06:00
| 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 |
2026-03-05 11:53:28 -06:00
2026-03-05 16:13:12 -06:00
**Example — Create Project with Drive link:**
2026-03-05 15:24:34 -06:00
```json
POST /api/projects
{
2026-03-05 16:13:12 -06:00
"name": "Tucson",
"color": "#E67E22",
2026-03-05 15:24:34 -06:00
"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" },
2026-03-05 16:13:12 -06:00
{ "title": "Fabricate Shelter Assembly Box", "due_date": "2026-03-10", "status": "upcoming" }
2026-03-05 15:24:34 -06:00
]
}
```
---
## Component Architecture
```
App
2026-03-05 16:13:12 -06:00
├── <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
2026-03-05 15:24:34 -06:00
2026-03-05 16:13:12 -06:00
├── 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)
2026-03-05 15:24:34 -06:00
│ ├── DeliverableModal
│ └── ContextMenu
2026-03-05 16:13:12 -06:00
├── 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
2026-03-05 15:24:34 -06:00
```
2026-03-05 12:13:22 -06:00
---
2026-03-05 11:53:28 -06:00
2026-03-05 12:13:22 -06:00
## Unraid Installation
2026-03-05 11:53:28 -06:00
2026-03-05 15:24:34 -06:00
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.
2026-03-05 11:53:28 -06:00
---
2026-03-05 12:20:22 -06:00
### Step 1 — Enable SSH on Unraid
2026-03-05 12:13:22 -06:00
2026-03-05 15:24:34 -06:00
Go to **Settings → Management Access** and confirm SSH is enabled. Note your Unraid server's local IP address from the dashboard header.
2026-03-05 12:13:22 -06:00
2026-03-05 12:20:22 -06:00
---
2026-03-05 11:53:28 -06:00
2026-03-05 12:20:22 -06:00
### Step 2 — SSH into Unraid and Clone the Repo
2026-03-05 11:53:28 -06:00
2026-03-05 12:13:22 -06:00
```bash
ssh root@YOUR_UNRAID_IP
cd /mnt/user/appdata
git clone https://github.com/yourname/fabdash.git
cd fabdash
2026-03-05 11:59:14 -06:00
```
2026-03-05 11:53:28 -06:00
2026-03-05 12:20:22 -06:00
---
2026-03-05 16:13:12 -06:00
### 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".
---
2026-03-05 12:20:22 -06:00
2026-03-05 16:13:12 -06:00
### Step 4 — Build the Docker Image
2026-03-05 11:53:28 -06:00
2026-03-05 12:13:22 -06:00
```bash
2026-03-05 12:20:22 -06:00
docker build -t fabdash:latest .
2026-03-05 12:13:22 -06:00
```
2026-03-05 11:53:28 -06:00
2026-03-05 16:13:12 -06:00
First build takes 35 minutes (downloads Node + Python base layers, compiles React).
Verify the image:
2026-03-05 11:59:14 -06:00
2026-03-05 12:20:22 -06:00
```bash
docker images | grep fabdash
2026-03-05 12:13:22 -06:00
```
2026-03-05 11:53:28 -06:00
2026-03-05 12:20:22 -06:00
---
2026-03-05 16:13:12 -06:00
### Step 5 — Create the Data Directory
2026-03-05 11:53:28 -06:00
2026-03-05 12:13:22 -06:00
```bash
2026-03-05 12:20:22 -06:00
mkdir -p /mnt/user/appdata/fabdash/data
2026-03-05 11:59:14 -06:00
```
2026-03-05 11:53:28 -06:00
2026-03-05 12:20:22 -06:00
---
2026-03-05 11:53:28 -06:00
2026-03-05 16:13:12 -06:00
### Step 6 — Add the Container via Unraid Docker GUI
2026-03-05 11:53:28 -06:00
2026-03-05 16:13:12 -06:00
1. Go to the **Docker** tab → **Add Container**
2. Toggle **Advanced View** in the top-right of the form
2026-03-05 11:53:28 -06:00
2026-03-05 12:20:22 -06:00
#### Basic Settings
2026-03-05 11:53:28 -06:00
2026-03-05 12:20:22 -06:00
| Field | Value |
|---|---|
| **Name** | `fabdash` |
| **Repository** | `fabdash:latest` |
| **Docker Hub URL** | *(leave blank — local image)* |
| **Network Type** | `Bridge` |
| **Console shell command** | `bash` |
| **Privileged** | `Off` |
2026-03-05 11:53:28 -06:00
2026-03-05 12:20:22 -06:00
#### Port Mapping
2026-03-05 11:53:28 -06:00
2026-03-05 16:13:12 -06:00
**Add → Port:**
2026-03-05 11:53:28 -06:00
2026-03-05 12:20:22 -06:00
| Field | Value |
|---|---|
| **Name** | `Web UI` |
| **Container Port** | `8080` |
| **Host Port** | `8080` |
| **Connection Type** | `TCP` |
2026-03-05 12:13:22 -06:00
2026-03-05 16:13:12 -06:00
> Change Host Port if `8080` is already in use. Access at `http://YOUR_UNRAID_IP:8080`
2026-03-05 11:53:28 -06:00
2026-03-05 12:20:22 -06:00
#### Volume / Path Mapping
2026-03-05 11:53:28 -06:00
2026-03-05 16:13:12 -06:00
**Add → Path:**
2026-03-05 11:53:28 -06:00
2026-03-05 12:20:22 -06:00
| Field | Value |
|---|---|
| **Name** | `AppData` |
| **Container Path** | `/app/data` |
| **Host Path** | `/mnt/user/appdata/fabdash/data` |
| **Access Mode** | `Read/Write` |
2026-03-05 11:53:28 -06:00
2026-03-05 16:13:12 -06:00
> Your database (`fabdash.db`) lives here and persists across all rebuilds and reboots.
2026-03-05 11:53:28 -06:00
2026-03-05 12:20:22 -06:00
#### Environment Variables
2026-03-05 11:53:28 -06:00
2026-03-05 16:13:12 -06:00
**Add → Variable** for each:
2026-03-05 11:53:28 -06:00
2026-03-05 12:20:22 -06:00
| Name | Key | Value |
|---|---|---|
2026-03-05 15:24:34 -06:00
| Secret Key | `SECRET_KEY` | *(generate below)* |
2026-03-05 12:20:22 -06:00
| Flask Environment | `FLASK_ENV` | `production` |
| Database URL | `DATABASE_URL` | `sqlite:////app/data/fabdash.db` |
2026-03-05 15:24:34 -06:00
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
```
2026-03-05 11:53:28 -06:00
2026-03-05 15:24:34 -06:00
#### Extra Parameters
2026-03-05 11:59:14 -06:00
2026-03-05 12:13:22 -06:00
| Field | Value |
|---|---|
2026-03-05 12:20:22 -06:00
| **Extra Parameters** | `--restart unless-stopped` |
2026-03-05 11:59:14 -06:00
2026-03-05 12:20:22 -06:00
---
2026-03-05 11:59:14 -06:00
2026-03-05 16:13:12 -06:00
### Step 7 — Apply and Launch
2026-03-05 11:59:14 -06:00
2026-03-05 16:13:12 -06:00
Click **Apply**. Open your browser: **`http://YOUR_UNRAID_IP:8080`**
2026-03-05 11:59:14 -06:00
2026-03-05 12:20:22 -06:00
---
2026-03-05 16:13:12 -06:00
### Step 8 — Verify (Optional)
2026-03-05 12:20:22 -06:00
```bash
docker logs fabdash --tail 50
```
2026-03-05 16:13:12 -06:00
A healthy startup:
2026-03-05 12:20:22 -06:00
```
[INFO] Starting gunicorn 23.0.0
[INFO] Listening at: http://0.0.0.0:8080
[INFO] Worker booting (pid: ...)
```
---
2026-03-05 16:13:12 -06:00
## Adding Your Logo
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:**
```bash
# 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.
---
2026-03-05 12:20:22 -06:00
## Updating FabDash on Unraid
2026-03-05 16:13:12 -06:00
### Step 1 — Pull latest and rebuild
2026-03-05 12:20:22 -06:00
```bash
ssh root@YOUR_UNRAID_IP
cd /mnt/user/appdata/fabdash
git pull
docker build -t fabdash:latest .
```
2026-03-05 15:24:34 -06:00
### Step 2 — Restart via Unraid Docker GUI
2026-03-05 12:20:22 -06:00
2026-03-05 16:13:12 -06:00
**Docker** tab → click `fabdash` container icon → **Restart**
2026-03-05 12:20:22 -06:00
2026-03-05 16:13:12 -06:00
Your database is untouched. New schema columns apply automatically on startup.
2026-03-05 12:20:22 -06:00
### Step 3 — Verify
2026-03-05 11:59:14 -06:00
2026-03-05 12:20:22 -06:00
```bash
docker logs fabdash --tail 20
```
2026-03-05 11:59:14 -06:00
2026-03-05 12:13:22 -06:00
---
2026-03-05 11:59:14 -06:00
2026-03-05 12:20:22 -06:00
## Data Backup
2026-03-05 11:59:14 -06:00
2026-03-05 12:13:22 -06:00
```
/mnt/user/appdata/fabdash/data/fabdash.db
2026-03-05 11:59:14 -06:00
```
2026-03-05 12:20:22 -06:00
**Manual backup:**
2026-03-05 11:59:14 -06:00
```bash
2026-03-05 12:20:22 -06:00
cp /mnt/user/appdata/fabdash/data/fabdash.db \
/mnt/user/backups/fabdash-$(date +%Y%m%d-%H%M).db
2026-03-05 11:59:14 -06:00
```
2026-03-05 11:53:28 -06:00
2026-03-05 16:13:12 -06:00
**Automated:** Use the **Appdata Backup/Restore** plugin from Unraid Community Applications.
2026-03-05 12:20:22 -06:00
2026-03-05 11:53:28 -06:00
---
2026-03-05 11:59:14 -06:00
## Local Development
2026-03-05 15:24:34 -06:00
**Terminal 1 — Flask backend:**
2026-03-05 11:59:14 -06:00
```bash
2026-03-05 11:53:28 -06:00
cd backend
2026-03-05 11:59:14 -06:00
python -m venv venv
2026-03-05 16:13:12 -06:00
source venv/bin/activate
2026-03-05 11:59:14 -06:00
pip install -r requirements.txt
2026-03-05 12:13:22 -06:00
export FLASK_ENV=development
2026-03-05 11:53:28 -06:00
flask run --port 5000
2026-03-05 11:59:14 -06:00
```
2026-03-05 11:53:28 -06:00
2026-03-05 15:24:34 -06:00
**Terminal 2 — React frontend:**
2026-03-05 11:59:14 -06:00
```bash
2026-03-05 11:53:28 -06:00
cd frontend
2026-03-05 15:24:34 -06:00
npm install --legacy-peer-deps
2026-03-05 16:13:12 -06:00
npm run dev # http://localhost:5173
2026-03-05 11:59:14 -06:00
```
2026-03-05 11:53:28 -06:00
2026-03-05 16:13:12 -06:00
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.
2026-03-05 11:53:28 -06:00
---
## Environment Variables
2026-03-05 12:20:22 -06:00
| Variable | Required | Default | Description |
|---|---|---|---|
2026-03-05 15:24:34 -06:00
| `SECRET_KEY` | **Yes** | — | Flask session secret. Use a long random string |
2026-03-05 16:13:12 -06:00
| `FLASK_ENV` | No | `production` | Set to `development` for debug mode |
| `DATABASE_URL` | No | `sqlite:////app/data/fabdash.db` | SQLite path (4 slashes = absolute path) |
2026-03-05 11:53:28 -06:00
---
## Database Schema
2026-03-05 11:59:14 -06:00
```sql
2026-03-05 11:53:28 -06:00
CREATE TABLE projects (
2026-03-05 16:13:12 -06:00
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
color TEXT NOT NULL DEFAULT '#C9A84C',
2026-03-05 11:53:28 -06:00
description TEXT,
2026-03-05 15:24:34 -06:00
drive_url VARCHAR(500),
2026-03-05 16:13:12 -06:00
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
2026-03-05 11:53:28 -06:00
);
CREATE TABLE deliverables (
2026-03-05 11:59:14 -06:00
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')),
2026-03-05 11:53:28 -06:00
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
2026-03-05 11:59:14 -06:00
CREATE INDEX idx_deliverables_project ON deliverables(project_id);
2026-03-05 11:53:28 -06:00
CREATE INDEX idx_deliverables_due_date ON deliverables(due_date);
2026-03-05 11:59:14 -06:00
```
2026-03-05 11:53:28 -06:00
2026-03-05 16:13:12 -06:00
> `drive_url` is added to existing databases automatically at startup via `_run_migrations()` in `app/__init__.py`.
2026-03-05 15:24:34 -06:00
### Auto-Overdue Logic
```python
def effective_status(self):
2026-03-05 16:13:12 -06:00
"""Computed at read time — never modifies the database."""
2026-03-05 15:24:34 -06:00
if self.status != 'completed' and self.due_date < date.today():
return 'overdue'
return self.status
```
2026-03-05 16:13:12 -06:00
- 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:
```python
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.
2026-03-05 15:24:34 -06:00
2026-03-05 11:53:28 -06:00
---
## Roadmap
2026-03-05 12:13:22 -06:00
### v1.0 — Core Release *(current)*
2026-03-05 11:59:14 -06:00
2026-03-05 12:13:22 -06:00
- [x] Dark/gold Tailwind design system
2026-03-05 15:24:34 -06:00
- [x] FullCalendar — Month, Week, and Day views
2026-03-05 11:53:28 -06:00
- [x] Drag-and-drop deliverable rescheduling
2026-03-05 12:13:22 -06:00
- [x] Multi-deliverable project creation with inline rows
2026-03-05 11:59:14 -06:00
- [x] Add / Edit / Delete for projects and deliverables
2026-03-05 15:24:34 -06:00
- [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
2026-03-05 16:13:12 -06:00
- [x] Click any Focus View card to activate; scale-105 clip fix
- [x] Active deliverable gold highlight with "Selected" badge
2026-03-05 15:24:34 -06:00
- [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
2026-03-05 16:13:12 -06:00
- [x] 30-second undo toast with circular countdown ring after drag-and-drop
- [x] Hover tooltip on calendar events (project, title, date, status)
- [x] Collapsible sidebar with branded logo tile when collapsed
- [x] Custom logo support — `frontend/public/logo.png`
- [x] Animated modal enter transitions (backdrop + scale + translateY)
- [x] Keyboard shortcuts — N, B, ← →, T, Esc
- [x] Keyboard shortcut legend pinned to sidebar footer
- [x] Workload heatmap — 20-week density grid with stat cards
- [x] Date range drag-select on calendar
- [x] ISO week numbers on calendar
- [x] Agenda / Upcoming panel in sidebar (next 60 days)
- [x] Empty state SVG illustration with keyboard hint
2026-03-05 12:13:22 -06:00
- [x] Flask REST API + SQLite persistence
2026-03-05 11:53:28 -06:00
- [x] Cascade delete (project → deliverables)
2026-03-05 16:13:12 -06:00
- [x] Inline schema migrations (auto-applied on startup)
- [x] Multi-stage Docker build (Node → Python, single container)
2026-03-05 12:20:22 -06:00
- [x] Unraid installation — local build + Docker GUI management
2026-03-05 11:53:28 -06:00
2026-03-05 16:13:12 -06:00
### v1.1 — Polish & UX *(in progress)*
2026-03-05 11:59:14 -06:00
2026-03-05 16:13:12 -06:00
- [ ] Animated modal and drawer exit transitions
- [ ] Drag-and-drop between projects (reassign deliverable)
- [ ] Mini calendar in sidebar for quick date navigation
- [ ] Responsive mobile layout
2026-03-05 11:53:28 -06:00
2026-03-05 12:13:22 -06:00
### v1.2 — Calendar Enhancements
2026-03-05 11:59:14 -06:00
2026-03-05 16:13:12 -06:00
- [ ] 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)
2026-03-05 11:53:28 -06:00
2026-03-05 12:13:22 -06:00
### v2.0 — Auth & Multi-user
2026-03-05 11:59:14 -06:00
2026-03-05 12:13:22 -06:00
- [ ] User login (Flask-Login + JWT)
- [ ] Multi-user support with project ownership
2026-03-05 11:59:14 -06:00
- [ ] Role-based access per project (Owner / Editor / Viewer)
2026-03-05 15:24:34 -06:00
- [ ] Activity log per project
- [ ] Comment threads on deliverables
2026-03-05 11:53:28 -06:00
### v2.1 — Notifications & Integrations
2026-03-05 11:59:14 -06:00
2026-03-05 11:53:28 -06:00
- [ ] In-app notification center for approaching due dates
2026-03-05 12:13:22 -06:00
- [ ] Email reminders at configurable intervals (Flask-Mail)
2026-03-05 15:24:34 -06:00
- [ ] iCal / Google Calendar export per project
2026-03-05 12:13:22 -06:00
- [ ] Slack webhook for deliverable status changes
2026-03-05 15:24:34 -06:00
- [ ] CSV import/export for bulk project setup
2026-03-05 11:53:28 -06:00
### v2.2 — Advanced Views
2026-03-05 11:59:14 -06:00
2026-03-05 16:13:12 -06:00
- [ ] Gantt view
2026-03-05 12:13:22 -06:00
- [ ] Kanban board (columns by status)
2026-03-05 16:13:12 -06:00
- [ ] Cross-project timeline
- [ ] Heatmap drill-down — click cell to expand day detail panel
2026-03-05 15:24:34 -06:00
- [ ] Archived projects with searchable history
2026-03-05 11:53:28 -06:00
### v3.0 — Intelligence Layer
2026-03-05 11:59:14 -06:00
2026-03-05 16:13:12 -06:00
- [ ] AI-assisted scheduling suggestions
2026-03-05 15:24:34 -06:00
- [ ] Conflict detection — flag overloaded days
2026-03-05 16:13:12 -06:00
- [ ] Natural language deliverable input
2026-03-05 11:53:28 -06:00
---
2026-03-05 16:52:31 -06:00
*Built with intention. No subscriptions. No bloat. Just your fabrication workflow.*