Add files via upload
This commit is contained in:
356
README.md
356
README.md
@@ -1,6 +1,6 @@
|
|||||||
# FabDash
|
# FabDash
|
||||||
|
|
||||||
**Fabrication Dashboard** — A sleek, modern project management & scheduling application.
|
**Fabrication Dashboard** — A sleek, modern project management & scheduling application built for fabrication workflows.
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
@@ -15,7 +15,9 @@
|
|||||||
- [Tech Stack](#tech-stack)
|
- [Tech Stack](#tech-stack)
|
||||||
- [Project Structure](#project-structure)
|
- [Project Structure](#project-structure)
|
||||||
- [Features](#features)
|
- [Features](#features)
|
||||||
|
- [Interaction Reference](#interaction-reference)
|
||||||
- [API Reference](#api-reference)
|
- [API Reference](#api-reference)
|
||||||
|
- [Component Architecture](#component-architecture)
|
||||||
- [Unraid Installation](#unraid-installation)
|
- [Unraid Installation](#unraid-installation)
|
||||||
- [Updating FabDash on Unraid](#updating-fabdash-on-unraid)
|
- [Updating FabDash on Unraid](#updating-fabdash-on-unraid)
|
||||||
- [Local Development](#local-development)
|
- [Local Development](#local-development)
|
||||||
@@ -27,22 +29,46 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
FabDash is a self-hosted project management dashboard built for fabrication teams. It features a large interactive calendar, multi-deliverable project tracking, drag-and-drop scheduling, and a per-project timeline Focus View — all wrapped in a dark/gold UI and deployed as a single Docker container.
|
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
|
## Tech Stack
|
||||||
|
|
||||||
| Layer | Technology |
|
### Frontend
|
||||||
|---|---|
|
|
||||||
| Frontend | React 18, Vite, Tailwind CSS |
|
| Package | Version | Purpose |
|
||||||
| Calendar | FullCalendar v6 (daygrid, timegrid, interaction) |
|
|---|---|---|
|
||||||
| Focus View | Custom horizontal timeline (react-chrono) |
|
| React | ^18.3.1 | UI framework |
|
||||||
| State | Zustand |
|
| Vite | ^5.4.11 | Build tool and dev server |
|
||||||
| HTTP | Axios |
|
| Tailwind CSS | ^3.4.15 | Utility-first styling |
|
||||||
| Backend | Flask 3, Flask-SQLAlchemy, Flask-Migrate, Flask-CORS |
|
| @fullcalendar/core | 6.1.15 | FullCalendar core (pinned) |
|
||||||
| Database | SQLite (persisted via Docker volume) |
|
| @fullcalendar/react | 6.1.15 | Calendar component |
|
||||||
| Server | Gunicorn (production) |
|
| @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
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -61,9 +87,9 @@ fabdash/
|
|||||||
│ ├── config.py
|
│ ├── config.py
|
||||||
│ ├── requirements.txt
|
│ ├── requirements.txt
|
||||||
│ └── app/
|
│ └── app/
|
||||||
│ ├── __init__.py
|
│ ├── __init__.py # App factory + inline migrations
|
||||||
│ ├── extensions.py
|
│ ├── extensions.py
|
||||||
│ ├── models.py
|
│ ├── models.py # Project + Deliverable models
|
||||||
│ └── routes/
|
│ └── routes/
|
||||||
│ ├── __init__.py
|
│ ├── __init__.py
|
||||||
│ ├── projects.py
|
│ ├── projects.py
|
||||||
@@ -71,7 +97,7 @@ fabdash/
|
|||||||
│
|
│
|
||||||
└── frontend/
|
└── frontend/
|
||||||
├── package.json
|
├── package.json
|
||||||
├── vite.config.js
|
├── vite.config.js # optimizeDeps for FullCalendar
|
||||||
├── tailwind.config.js
|
├── tailwind.config.js
|
||||||
├── postcss.config.js
|
├── postcss.config.js
|
||||||
├── index.html
|
├── index.html
|
||||||
@@ -83,25 +109,26 @@ fabdash/
|
|||||||
│ └── deliverables.js
|
│ └── deliverables.js
|
||||||
├── components/
|
├── components/
|
||||||
│ ├── Calendar/
|
│ ├── Calendar/
|
||||||
│ │ └── MainCalendar.jsx
|
│ │ └── MainCalendar.jsx # dblclick + right-click on events
|
||||||
│ ├── Projects/
|
│ ├── Projects/
|
||||||
│ │ ├── ProjectList.jsx
|
│ │ ├── ProjectList.jsx
|
||||||
│ │ ├── ProjectCard.jsx
|
│ │ ├── ProjectCard.jsx # dblclick + right-click, Drive link
|
||||||
│ │ └── ProjectModal.jsx
|
│ │ └── ProjectModal.jsx # Drive URL input field
|
||||||
│ ├── Deliverables/
|
│ ├── Deliverables/
|
||||||
│ │ └── DeliverableModal.jsx
|
│ │ └── DeliverableModal.jsx
|
||||||
│ ├── FocusView/
|
│ ├── FocusView/
|
||||||
│ │ ├── FocusDrawer.jsx
|
│ │ ├── FocusDrawer.jsx
|
||||||
│ │ ├── FocusTimeline.jsx
|
│ │ ├── FocusTimeline.jsx # Click any card to activate
|
||||||
│ │ └── DeliverableCard.jsx
|
│ │ └── DeliverableCard.jsx # dblclick + right-click, status menu
|
||||||
│ └── UI/
|
│ └── UI/
|
||||||
│ ├── Button.jsx
|
│ ├── Button.jsx
|
||||||
│ ├── Badge.jsx
|
│ ├── Badge.jsx
|
||||||
│ ├── Modal.jsx
|
│ ├── Modal.jsx
|
||||||
│ └── Drawer.jsx
|
│ ├── Drawer.jsx
|
||||||
|
│ └── ContextMenu.jsx # Floating right-click menu
|
||||||
├── store/
|
├── store/
|
||||||
│ ├── useProjectStore.js
|
│ ├── useProjectStore.js
|
||||||
│ └── useFocusStore.js
|
│ └── useFocusStore.js # setActiveDeliverable action
|
||||||
├── utils/
|
├── utils/
|
||||||
│ ├── dateHelpers.js
|
│ ├── dateHelpers.js
|
||||||
│ └── statusHelpers.js
|
│ └── statusHelpers.js
|
||||||
@@ -113,14 +140,82 @@ fabdash/
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Large calendar view** — Month, Week, and Day modes via FullCalendar
|
### Calendar
|
||||||
- **Drag-and-drop** — Move deliverables to new dates; backend updates instantly
|
|
||||||
- **Multi-deliverable projects** — Unlimited deliverables per project, each with its own due date and status
|
- **Large full-width calendar** — Month, Week, and Day views via FullCalendar
|
||||||
- **Color-coded projects** — 12-swatch palette + custom hex input
|
- **Drag-and-drop rescheduling** — Move any deliverable to a new date; backend patches immediately
|
||||||
- **Deliverable Focus View** — Click any calendar event to open a slide-up drawer with the full project timeline; clicked deliverable highlighted in gold
|
- **Click empty date cell** — Opens Add Deliverable modal pre-filled with that date
|
||||||
- **Status tracking** — Upcoming / In Progress / Completed / Overdue badges
|
- **Single-click event** — Opens Deliverable Focus View
|
||||||
- **Dark/gold theme** — Dark surfaces throughout with gold as the primary accent
|
- **Double-click event** — Opens Edit Deliverable modal directly
|
||||||
- **Full persistence** — SQLite via Flask-SQLAlchemy, mounted as a Docker volume
|
- **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 | — | — |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -133,7 +228,7 @@ fabdash/
|
|||||||
| `GET` | `/api/projects` | List all projects with nested deliverables |
|
| `GET` | `/api/projects` | List all projects with nested deliverables |
|
||||||
| `POST` | `/api/projects` | Create project (with optional inline deliverables) |
|
| `POST` | `/api/projects` | Create project (with optional inline deliverables) |
|
||||||
| `GET` | `/api/projects/:id` | Get single project |
|
| `GET` | `/api/projects/:id` | Get single project |
|
||||||
| `PATCH` | `/api/projects/:id` | Update name, color, or description |
|
| `PATCH` | `/api/projects/:id` | Update name, color, description, or drive_url |
|
||||||
| `DELETE` | `/api/projects/:id` | Delete project + cascade deliverables |
|
| `DELETE` | `/api/projects/:id` | Delete project + cascade deliverables |
|
||||||
|
|
||||||
### Deliverables
|
### Deliverables
|
||||||
@@ -145,31 +240,67 @@ fabdash/
|
|||||||
| `PATCH` | `/api/deliverables/:id` | Update title, due_date, or status |
|
| `PATCH` | `/api/deliverables/:id` | Update title, due_date, or status |
|
||||||
| `DELETE` | `/api/deliverables/:id` | Delete deliverable |
|
| `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
|
## Unraid Installation
|
||||||
|
|
||||||
FabDash is installed by cloning the repo and building the Docker image locally on your Unraid server via SSH, then registering and managing the container through the Unraid Docker GUI. This gives you full GUI control (start, stop, logs, edit) while keeping the image local.
|
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
|
### Step 1 — Enable SSH on Unraid
|
||||||
|
|
||||||
Go to **Settings → Management Access** and confirm SSH is enabled. Note your Unraid server's local IP address (visible on the Unraid dashboard header).
|
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
|
### Step 2 — SSH into Unraid and Clone the Repo
|
||||||
|
|
||||||
Open a terminal on your local machine and connect:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh root@YOUR_UNRAID_IP
|
ssh root@YOUR_UNRAID_IP
|
||||||
```
|
|
||||||
|
|
||||||
Clone FabDash into your appdata share:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /mnt/user/appdata
|
cd /mnt/user/appdata
|
||||||
git clone https://github.com/yourname/fabdash.git
|
git clone https://github.com/yourname/fabdash.git
|
||||||
cd fabdash
|
cd fabdash
|
||||||
@@ -177,28 +308,25 @@ cd fabdash
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Step 3 — Build the Docker Image Locally
|
### Step 3 — Build the Docker Image
|
||||||
|
|
||||||
This command builds the image on your Unraid server and tags it as `fabdash:latest`.
|
Builds the image on your Unraid server and tags it `fabdash:latest`.
|
||||||
The first build takes 3–5 minutes (downloads Node + Python layers, compiles React).
|
First build takes 3–5 minutes (downloads Node + Python layers, compiles React).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker build -t fabdash:latest .
|
docker build -t fabdash:latest .
|
||||||
```
|
```
|
||||||
|
|
||||||
When complete, verify the image exists:
|
Verify the image exists:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker images | grep fabdash
|
docker images | grep fabdash
|
||||||
# fabdash latest abc123def456 1 minute ago ...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Step 4 — Create the Data Directory
|
### Step 4 — Create the Data Directory
|
||||||
|
|
||||||
Create the persistent directory where SQLite will store your database:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir -p /mnt/user/appdata/fabdash/data
|
mkdir -p /mnt/user/appdata/fabdash/data
|
||||||
```
|
```
|
||||||
@@ -207,11 +335,9 @@ mkdir -p /mnt/user/appdata/fabdash/data
|
|||||||
|
|
||||||
### Step 5 — Add the Container via Unraid Docker GUI
|
### Step 5 — Add the Container via Unraid Docker GUI
|
||||||
|
|
||||||
1. In the Unraid web UI, go to the **Docker** tab
|
1. Go to the **Docker** tab in Unraid
|
||||||
2. At the bottom, click **Add Container**
|
2. Click **Add Container** at the bottom
|
||||||
3. In the top-right of the form, toggle to **Advanced View**
|
3. Toggle **Advanced View** in the top-right of the form
|
||||||
|
|
||||||
Fill in the fields exactly as follows:
|
|
||||||
|
|
||||||
#### Basic Settings
|
#### Basic Settings
|
||||||
|
|
||||||
@@ -226,7 +352,7 @@ Fill in the fields exactly as follows:
|
|||||||
|
|
||||||
#### Port Mapping
|
#### Port Mapping
|
||||||
|
|
||||||
Click **Add another Path, Port, Variable, Label or Device** → select **Port**:
|
Click **Add another Path, Port, Variable, Label or Device** → **Port**:
|
||||||
|
|
||||||
| Field | Value |
|
| Field | Value |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -235,12 +361,11 @@ Click **Add another Path, Port, Variable, Label or Device** → select **Port**:
|
|||||||
| **Host Port** | `8080` |
|
| **Host Port** | `8080` |
|
||||||
| **Connection Type** | `TCP` |
|
| **Connection Type** | `TCP` |
|
||||||
|
|
||||||
> Access FabDash at `http://YOUR_UNRAID_IP:8080` after starting the container.
|
> Change Host Port if `8080` is already in use. Access FabDash at `http://YOUR_UNRAID_IP:8080`
|
||||||
> Change Host Port if `8080` is already in use on your server.
|
|
||||||
|
|
||||||
#### Volume / Path Mapping
|
#### Volume / Path Mapping
|
||||||
|
|
||||||
Click **Add another Path, Port, Variable, Label or Device** → select **Path**:
|
Click **Add another Path, Port, Variable, Label or Device** → **Path**:
|
||||||
|
|
||||||
| Field | Value |
|
| Field | Value |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -249,24 +374,25 @@ Click **Add another Path, Port, Variable, Label or Device** → select **Path**:
|
|||||||
| **Host Path** | `/mnt/user/appdata/fabdash/data` |
|
| **Host Path** | `/mnt/user/appdata/fabdash/data` |
|
||||||
| **Access Mode** | `Read/Write` |
|
| **Access Mode** | `Read/Write` |
|
||||||
|
|
||||||
> This is where `fabdash.db` will live. Data persists across all container restarts and rebuilds.
|
> Your database (`fabdash.db`) lives here and survives all rebuilds and reboots.
|
||||||
|
|
||||||
#### Environment Variables
|
#### Environment Variables
|
||||||
|
|
||||||
Click **Add another Path, Port, Variable, Label or Device** → select **Variable** for each:
|
Add each as **Variable**:
|
||||||
|
|
||||||
| Name | Key | Value |
|
| Name | Key | Value |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Secret Key | `SECRET_KEY` | `your-strong-random-secret-key-here` |
|
| Secret Key | `SECRET_KEY` | *(generate below)* |
|
||||||
| Flask Environment | `FLASK_ENV` | `production` |
|
| Flask Environment | `FLASK_ENV` | `production` |
|
||||||
| Database URL | `DATABASE_URL` | `sqlite:////app/data/fabdash.db` |
|
| Database URL | `DATABASE_URL` | `sqlite:////app/data/fabdash.db` |
|
||||||
|
|
||||||
> **SECRET_KEY** — Use a long random string. Generate one with:
|
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 (optional)
|
```bash
|
||||||
|
cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 48 | head -n 1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Extra Parameters
|
||||||
|
|
||||||
| Field | Value |
|
| Field | Value |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -274,23 +400,17 @@ Click **Add another Path, Port, Variable, Label or Device** → select **Variabl
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Step 6 — Apply and Start
|
### Step 6 — Apply and Launch
|
||||||
|
|
||||||
Click **Apply** at the bottom of the form. Unraid will register and start the container using your locally built image.
|
Click **Apply**. The container starts using your locally built image.
|
||||||
|
|
||||||
Navigate to the **Docker** tab — you should see `fabdash` running with a green indicator.
|
Open your browser: **`http://YOUR_UNRAID_IP:8080`**
|
||||||
|
|
||||||
Open your browser:
|
|
||||||
|
|
||||||
```
|
|
||||||
http://YOUR_UNRAID_IP:8080
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Step 7 — Verify (Optional)
|
### Step 7 — Verify (Optional)
|
||||||
|
|
||||||
Check container logs from the Unraid Docker tab by clicking the `fabdash` container icon → **Logs**, or via SSH:
|
Check logs from the Unraid Docker tab → click `fabdash` → **Logs**, or via SSH:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker logs fabdash --tail 50
|
docker logs fabdash --tail 50
|
||||||
@@ -308,11 +428,7 @@ A healthy startup looks like:
|
|||||||
|
|
||||||
## Updating FabDash on Unraid
|
## Updating FabDash on Unraid
|
||||||
|
|
||||||
When a new version is pushed to GitHub, update FabDash in three steps:
|
### Step 1 — Pull latest code and rebuild
|
||||||
|
|
||||||
### Step 1 — Pull the latest code and rebuild
|
|
||||||
|
|
||||||
SSH into Unraid:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh root@YOUR_UNRAID_IP
|
ssh root@YOUR_UNRAID_IP
|
||||||
@@ -321,13 +437,12 @@ git pull
|
|||||||
docker build -t fabdash:latest .
|
docker build -t fabdash:latest .
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 2 — Restart the container via Unraid GUI
|
### Step 2 — Restart via Unraid Docker GUI
|
||||||
|
|
||||||
1. Go to the **Docker** tab in Unraid
|
Go to **Docker** tab → click the `fabdash` container icon → **Restart**
|
||||||
2. Click the `fabdash` container icon
|
|
||||||
3. Select **Restart**
|
|
||||||
|
|
||||||
The container will stop, reload with the newly built image, and come back up. Your database is untouched.
|
The container reloads with the new image. Your database is untouched.
|
||||||
|
New schema columns (if any) are applied automatically on startup.
|
||||||
|
|
||||||
### Step 3 — Verify
|
### Step 3 — Verify
|
||||||
|
|
||||||
@@ -352,15 +467,13 @@ cp /mnt/user/appdata/fabdash/data/fabdash.db \
|
|||||||
/mnt/user/backups/fabdash-$(date +%Y%m%d-%H%M).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/`.
|
**Automated backup:** Use the **Appdata Backup/Restore** plugin from Unraid Community Applications to schedule regular backups of `/mnt/user/appdata/fabdash/`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Local Development
|
## Local Development
|
||||||
|
|
||||||
Run backend and frontend separately for Vite hot-module reloading:
|
**Terminal 1 — Flask backend:**
|
||||||
|
|
||||||
**Terminal 1 — Flask:**
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd backend
|
cd backend
|
||||||
@@ -371,15 +484,15 @@ export FLASK_ENV=development
|
|||||||
flask run --port 5000
|
flask run --port 5000
|
||||||
```
|
```
|
||||||
|
|
||||||
**Terminal 2 — React:**
|
**Terminal 2 — React frontend:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd frontend
|
cd frontend
|
||||||
npm install
|
npm install --legacy-peer-deps
|
||||||
npm run dev # http://localhost:5173
|
npm run dev # http://localhost:5173
|
||||||
```
|
```
|
||||||
|
|
||||||
Vite proxies all `/api/*` calls to Flask on port 5000 automatically via `vite.config.js`. No CORS issues in dev.
|
Vite proxies all `/api/*` requests to Flask on port 5000 via `vite.config.js`. No CORS configuration needed in development.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -387,9 +500,9 @@ Vite proxies all `/api/*` calls to Flask on port 5000 automatically via `vite.co
|
|||||||
|
|
||||||
| Variable | Required | Default | Description |
|
| Variable | Required | Default | Description |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `SECRET_KEY` | Yes | — | Flask session secret. Use a long random string |
|
| `SECRET_KEY` | **Yes** | — | Flask session secret. Use a long random string |
|
||||||
| `FLASK_ENV` | No | `production` | Set to `development` for debug mode |
|
| `FLASK_ENV` | No | `production` | Set to `development` for debug mode and auto-reload |
|
||||||
| `DATABASE_URL` | No | `sqlite:////app/data/fabdash.db` | SQLite path (4 slashes = absolute path) |
|
| `DATABASE_URL` | No | `sqlite:////app/data/fabdash.db` | Full SQLite path (4 slashes = absolute path) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -401,6 +514,7 @@ CREATE TABLE projects (
|
|||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
color TEXT NOT NULL DEFAULT '#C9A84C',
|
color TEXT NOT NULL DEFAULT '#C9A84C',
|
||||||
description TEXT,
|
description TEXT,
|
||||||
|
drive_url VARCHAR(500),
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -418,6 +532,23 @@ CREATE INDEX idx_deliverables_project ON deliverables(project_id);
|
|||||||
CREATE INDEX idx_deliverables_due_date ON deliverables(due_date);
|
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
|
## Roadmap
|
||||||
@@ -425,23 +556,31 @@ CREATE INDEX idx_deliverables_due_date ON deliverables(due_date);
|
|||||||
### v1.0 — Core Release *(current)*
|
### v1.0 — Core Release *(current)*
|
||||||
|
|
||||||
- [x] Dark/gold Tailwind design system
|
- [x] Dark/gold Tailwind design system
|
||||||
- [x] FullCalendar with Month / Week / Day views
|
- [x] FullCalendar — Month, Week, and Day views
|
||||||
- [x] Drag-and-drop deliverable rescheduling
|
- [x] Drag-and-drop deliverable rescheduling
|
||||||
- [x] Multi-deliverable project creation with inline rows
|
- [x] Multi-deliverable project creation with inline rows
|
||||||
- [x] Add / Edit / Delete for projects and deliverables
|
- [x] Add / Edit / Delete for projects and deliverables
|
||||||
- [x] Deliverable Focus View — slide-up drawer with horizontal timeline
|
- [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] Active deliverable gold highlight in Focus View
|
||||||
- [x] Status badges (Upcoming / In Progress / Completed / Overdue)
|
- [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] Flask REST API + SQLite persistence
|
||||||
- [x] Cascade delete (project → deliverables)
|
- [x] Cascade delete (project → deliverables)
|
||||||
- [x] Single Docker container deployment
|
- [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
|
- [x] Unraid installation — local build + Docker GUI management
|
||||||
|
|
||||||
### v1.1 — Polish & UX
|
### v1.1 — Polish & UX
|
||||||
|
|
||||||
- [ ] Keyboard shortcuts (`N` = new project, `Esc` = close, arrow keys = calendar nav)
|
- [ ] Keyboard shortcuts (`N` = new project, `Esc` = close modal/drawer, arrow keys = calendar nav)
|
||||||
- [ ] 30-second undo toast for drag-and-drop moves
|
- [ ] 30-second undo toast for drag-and-drop moves
|
||||||
- [ ] Animated modal/drawer transitions
|
- [ ] Animated modal and drawer enter/exit transitions
|
||||||
- [ ] Hover tooltip on calendar events (preview without opening Focus View)
|
- [ ] Hover tooltip on calendar events (preview without opening Focus View)
|
||||||
- [ ] Responsive layout with collapsible sidebar
|
- [ ] Responsive layout with collapsible sidebar
|
||||||
- [ ] Empty state illustrations
|
- [ ] Empty state illustrations
|
||||||
@@ -452,34 +591,37 @@ CREATE INDEX idx_deliverables_due_date ON deliverables(due_date);
|
|||||||
- [ ] Date range selection for bulk deliverable creation
|
- [ ] Date range selection for bulk deliverable creation
|
||||||
- [ ] "Today" jump button
|
- [ ] "Today" jump button
|
||||||
- [ ] Week numbers in calendar header
|
- [ ] Week numbers in calendar header
|
||||||
|
- [ ] Mini calendar thumbnail for quick date navigation
|
||||||
|
|
||||||
### v2.0 — Auth & Multi-user
|
### v2.0 — Auth & Multi-user
|
||||||
|
|
||||||
- [ ] User login (Flask-Login + JWT)
|
- [ ] User login (Flask-Login + JWT)
|
||||||
- [ ] Multi-user support with project ownership
|
- [ ] Multi-user support with project ownership
|
||||||
- [ ] Role-based access per project (Owner / Editor / Viewer)
|
- [ ] Role-based access per project (Owner / Editor / Viewer)
|
||||||
- [ ] Activity log and comment threads per deliverable
|
- [ ] Activity log per project
|
||||||
|
- [ ] Comment threads on deliverables
|
||||||
|
|
||||||
### v2.1 — Notifications & Integrations
|
### v2.1 — Notifications & Integrations
|
||||||
|
|
||||||
- [ ] In-app notification center for approaching due dates
|
- [ ] In-app notification center for approaching due dates
|
||||||
- [ ] Email reminders at configurable intervals (Flask-Mail)
|
- [ ] Email reminders at configurable intervals (Flask-Mail)
|
||||||
- [ ] iCal / Google Calendar export
|
- [ ] iCal / Google Calendar export per project
|
||||||
- [ ] Slack webhook for deliverable status changes
|
- [ ] Slack webhook for deliverable status changes
|
||||||
- [ ] CSV import/export
|
- [ ] CSV import/export for bulk project setup
|
||||||
|
|
||||||
### v2.2 — Advanced Views
|
### v2.2 — Advanced Views
|
||||||
|
|
||||||
- [ ] Gantt view alternate layout
|
- [ ] Gantt view alternate layout
|
||||||
- [ ] Kanban board (columns by status)
|
- [ ] Kanban board (columns by status)
|
||||||
- [ ] Cross-project timeline view
|
- [ ] Cross-project timeline view
|
||||||
- [ ] Workload heatmap
|
- [ ] Workload heatmap — deliverable density per day
|
||||||
|
- [ ] Archived projects with searchable history
|
||||||
|
|
||||||
### v3.0 — Intelligence Layer
|
### v3.0 — Intelligence Layer
|
||||||
|
|
||||||
- [ ] AI scheduling suggestions
|
- [ ] AI-assisted scheduling suggestions based on project cadence
|
||||||
- [ ] Conflict detection for overloaded days
|
- [ ] Conflict detection — flag overloaded days
|
||||||
- [ ] Natural language deliverable input
|
- [ ] Natural language deliverable input ("Add final draft due next Friday to CODA")
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user