# FabDash **Fabrication Dashboard** — A sleek, modern project management & scheduling application built for fabrication workflows. ![Version](https://img.shields.io/badge/version-1.0.0--alpha-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) ![License](https://img.shields.io/badge/license-MIT-green) --- ## Table of Contents - [Overview](#overview) - [Core Philosophy](#core-philosophy) - [Tech Stack](#tech-stack) - [Project Structure](#project-structure) - [Data Architecture](#data-architecture) - [Features](#features) - [API Reference](#api-reference) - [Component Architecture](#component-architecture) - [Docker Deployment](#docker-deployment) - [Local Development](#local-development) - [Environment Variables](#environment-variables) - [Database Schema](#database-schema) - [Roadmap](#roadmap) --- ## Overview **FabDash** is a self-hosted, full-stack project management and scheduling application built for fabrication teams who need a clean, fast, and visually intuitive way to manage multi-deliverable projects across time. It combines a large interactive calendar with a per-project timeline focus system — all wrapped in a dark, modern UI with gold accents. Deployed as a **single Docker container**, FabDash runs anywhere Docker runs with zero external dependencies. --- ## Core Philosophy | Principle | Implementation | |---|---| | **Clarity over clutter** | One focused view at a time — calendar or timeline, never both fighting for space | | **Speed of interaction** | Drag, click, and edit without losing context | | **Data ownership** | Local SQLite persistence, no cloud dependency | | **Easy deployment** | Single Docker container — one command to run anywhere | | **Visual hierarchy** | Gold accents guide the eye to what matters most | --- ## Tech Stack ### Frontend | Package | Version | Purpose | |---|---|---| | React | ^18.x | UI component framework | | Vite | ^5.x | Build tool and dev server | | Tailwind CSS | ^3.x | Utility-first styling with custom tokens | | @fullcalendar/react | ^6.x | Main calendar view | | @fullcalendar/daygrid | ^6.x | Month/week/day grid views | | @fullcalendar/interaction | ^6.x | Drag-and-drop and click events | | @fullcalendar/timegrid | ^6.x | Time-slot grid view | | react-chrono | ^2.x | Deliverable Focus View timeline | | Zustand | ^4.x | Global state management | | Axios | ^1.x | HTTP client for Flask API | | React Router | ^6.x | Client-side routing | | date-fns | ^3.x | Date formatting and manipulation | ### Backend | Package | Version | Purpose | |---|---|---| | Flask | ^3.x | REST API server + static file serving | | Flask-SQLAlchemy | ^3.x | ORM for SQLite | | Flask-CORS | ^4.x | Cross-origin support for React dev server | | Flask-Migrate | ^4.x | Database schema migrations | | Gunicorn | ^21.x | Production WSGI server (inside Docker) | ### Database - **SQLite** — Zero-config, file-based persistence at `/app/data/fabdash.db` - Mounted as a Docker volume so data survives container restarts --- ## Project Structure ``` fabdash/ ├── Dockerfile ├── docker-compose.yml ├── .env.example ├── .gitignore ├── README.md │ ├── backend/ │ ├── run.py │ ├── config.py │ ├── requirements.txt │ └── app/ │ ├── __init__.py │ ├── extensions.py │ ├── models.py │ └── routes/ │ ├── projects.py │ └── deliverables.py │ └── frontend/ ├── package.json ├── vite.config.js ├── tailwind.config.js ├── index.html └── src/ ├── main.jsx ├── App.jsx ├── api/ │ ├── projects.js │ └── deliverables.js ├── components/ │ ├── Calendar/ │ │ ├── MainCalendar.jsx │ │ ├── EventChip.jsx │ │ └── CalendarToolbar.jsx │ ├── Projects/ │ │ ├── ProjectList.jsx │ │ ├── ProjectCard.jsx │ │ └── ProjectModal.jsx │ ├── Deliverables/ │ │ ├── DeliverableModal.jsx │ │ └── DeliverableChip.jsx │ ├── FocusView/ │ │ ├── FocusDrawer.jsx │ │ ├── FocusTimeline.jsx │ │ └── DeliverableCard.jsx │ └── UI/ │ ├── Button.jsx │ ├── Badge.jsx │ ├── Modal.jsx │ └── Drawer.jsx ├── store/ │ ├── useProjectStore.js │ └── useFocusStore.js ├── hooks/ │ ├── useProjects.js │ └── useDeliverables.js ├── utils/ │ ├── dateHelpers.js │ └── statusHelpers.js └── styles/ └── globals.css ``` --- ## Data Architecture ### Relationships ``` Project (1) ─────────────── (many) Deliverable │ │ ├── id (PK) ├── id (PK) ├── name ├── project_id (FK → projects.id) ├── color (hex) ├── title ├── description ├── due_date └── created_at ├── status (enum) └── created_at ``` Each **Project** owns one or more **Deliverables**. Every deliverable inherits the parent project's color when rendered on the calendar. Deleting a project cascades to remove all its deliverables. --- ## Features ### Main Calendar View The primary interface is a large, full-width FullCalendar grid with three switchable view modes: - **Month View** — Full month overview, all deliverables visible as colored event chips - **Week View** — Focused 7-day view with time slots - **Day View** — Single-day granularity for heavy scheduling days **Interactions:** - Drag-and-drop any deliverable to a new date — backend is patched immediately - Click any event to open the Deliverable Focus View - Right-click an event for quick actions: Edit, Delete, Open Project - Click an empty date cell to open the Add Deliverable modal pre-filled with that date --- ### Project & Deliverable Management **Adding a Project:** 1. Click **"+ New Project"** in the sidebar 2. Enter project name, optional description, and choose a color swatch 3. Add one or more deliverables inline before saving 4. Submit — project and all deliverables persist in a single transaction **Editing & Deleting:** - Edit any deliverable from the sidebar, calendar event click, or Focus View - Delete a project via the sidebar (confirmation dialog warns of cascade delete) - Delete individual deliverables from their edit modal or via right-click context menu --- ### Deliverable Focus View Clicking any calendar event opens a **slide-up drawer** with the full project timeline. - All deliverables render as a horizontal timeline via `react-chrono` in `HORIZONTAL_ALL` mode - The clicked deliverable is highlighted with a gold glow and elevated scale - Other deliverables appear as dimmed context nodes - Drawer dismisses on outside click, `Escape` key, or close button - Edit Deliverable and Edit Project buttons are available inline ``` [ Deliverable 1 ]────────[ Deliverable 2 ]────────[ ★ Deliverable 3 ★ ] Jan 15, 2026 Feb 28, 2026 Mar 28, 2026 Completed In Progress ← ACTIVE / FOCUSED ``` --- ### Color Coding System Each project is assigned a hex color used across: - Background of event chips on the calendar grid - Left border accent on sidebar project cards - Timeline node color in the Focus View for non-active deliverables Colors are selectable from a curated 12-swatch palette (all readable on dark backgrounds) plus a custom hex input. --- ### Theme & Design System ```js // tailwind.config.js theme: { extend: { colors: { gold: '#C9A84C', 'gold-light': '#E8C96A', 'gold-muted': '#8A6E2F', surface: '#111111', 'surface-raised': '#1A1A1A', 'surface-elevated': '#242424', 'surface-border': '#2E2E2E', 'text-primary': '#F5F5F5', 'text-muted': '#888888', }, boxShadow: { gold: '0 0 12px rgba(201, 168, 76, 0.4)', }, fontFamily: { sans: ['Inter', 'sans-serif'], mono: ['JetBrains Mono', 'monospace'], }, }, } ``` --- ## API Reference ### Projects | Method | Endpoint | Description | |---|---|---| | `GET` | `/api/projects` | Fetch all projects with nested deliverables | | `POST` | `/api/projects` | Create a new project | | `GET` | `/api/projects/:id` | Fetch a single project | | `PATCH` | `/api/projects/:id` | Update project name, color, or description | | `DELETE` | `/api/projects/:id` | Delete project and cascade deliverables | ### Deliverables | Method | Endpoint | Description | |---|---|---| | `GET` | `/api/deliverables?project_id=:id` | Fetch deliverables for a project | | `POST` | `/api/deliverables` | Create a new deliverable | | `PATCH` | `/api/deliverables/:id` | Update title, date, or status | | `DELETE` | `/api/deliverables/:id` | Delete a single deliverable | **Example — Create Project with Deliverables:** ```json POST /api/projects { "name": "CODA", "color": "#4A90D9", "description": "Example fabrication project", "deliverables": [ { "title": "Deliverable 1 – Concept Brief", "due_date": "2026-01-15" }, { "title": "Deliverable 2 – Draft Review", "due_date": "2026-02-28" }, { "title": "Deliverable 3 – Final Submission", "due_date": "2026-03-28" } ] } ``` --- ## Component Architecture ``` App ├── Sidebar │ ├── ProjectList │ │ └── ProjectCard (× N) │ │ └── DeliverableChip (× N) │ └── Button ["+ New Project"] ├── MainCalendar │ ├── CalendarToolbar │ └── FullCalendar │ └── EventChip (× N) ├── ProjectModal ├── DeliverableModal └── FocusDrawer └── FocusTimeline └── DeliverableCard (× N, one highlighted) ``` --- ## Docker Deployment FabDash ships as a **single Docker container**. The build process compiles the React frontend with Vite, copies the static output into Flask's `static/` folder, and serves everything through Gunicorn. No separate web server or container needed. ### How It Works ``` Docker Build ├── Stage 1 (node:18-alpine) │ └── npm run build → frontend/dist/ └── Stage 2 (python:3.12-slim) ├── Copy dist/ → backend/app/static/ ├── pip install -r requirements.txt ├── Flask serves API at /api/* ├── Flask serves React at /* (catch-all → index.html) └── Gunicorn runs on port 8080 ``` ### Dockerfile ```dockerfile # ── Stage 1: Build React frontend ────────────────────────────────────── FROM node:18-alpine AS frontend-build WORKDIR /app/frontend COPY frontend/package*.json ./ RUN npm ci COPY frontend/ ./ RUN npm run build # ── Stage 2: Flask + Gunicorn production image ───────────────────────── FROM python:3.12-slim WORKDIR /app # Install Python dependencies COPY backend/requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt # Copy Flask app COPY backend/ ./ # Copy compiled React build into Flask static folder COPY --from=frontend-build /app/frontend/dist ./app/static # Persistent volume mount point for SQLite database RUN mkdir -p /app/data EXPOSE 8080 CMD ["gunicorn", "--bind", "0.0.0.0:8080", "--workers", "2", "run:app"] ``` ### docker-compose.yml ```yaml version: "3.9" services: fabdash: build: . container_name: fabdash ports: - "8080:8080" volumes: - fabdash-data:/app/data # Persists SQLite database across restarts environment: - FLASK_ENV=production - DATABASE_URL=sqlite:////app/data/fabdash.db - SECRET_KEY=${SECRET_KEY} restart: unless-stopped volumes: fabdash-data: ``` ### Flask Catch-All Route (SPA Support) Add this to `backend/app/__init__.py` so React Router handles all non-API routes: ```python import os from flask import send_from_directory @app.route("/", defaults={"path": ""}) @app.route("/") def serve_react(path): static_folder = os.path.join(app.root_path, "static") if path and os.path.exists(os.path.join(static_folder, path)): return send_from_directory(static_folder, path) return send_from_directory(static_folder, "index.html") ``` ### Deploy (One Command) ```bash # Clone and run git clone https://github.com/yourname/fabdash.git cd fabdash cp .env.example .env # Add your SECRET_KEY docker compose up -d --build # FabDash is live at http://localhost:8080 ``` ### Useful Docker Commands ```bash docker compose logs -f fabdash # Stream logs docker compose down # Stop container docker compose up -d --build # Rebuild and restart docker exec -it fabdash flask db upgrade # Run migrations ``` --- ## Local Development For active development, run frontend and backend separately so Vite HMR works: **Terminal 1 — Flask backend:** ```bash cd backend python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate pip install -r requirements.txt flask db upgrade flask run --port 5000 ``` **Terminal 2 — React frontend:** ```bash cd frontend npm install npm run dev # Vite dev server at http://localhost:5173 ``` Set `VITE_API_BASE_URL=http://localhost:5000/api` in `frontend/.env` during development. In production (Docker), the frontend calls `/api/*` relative to the same origin — no CORS needed. --- ## Environment Variables ```env # .env (used by docker-compose.yml) SECRET_KEY=replace-with-a-strong-random-key FLASK_ENV=production DATABASE_URL=sqlite:////app/data/fabdash.db # frontend/.env (development only) VITE_API_BASE_URL=http://localhost:5000/api ``` --- ## Database Schema ```sql CREATE TABLE projects ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, color TEXT NOT NULL DEFAULT '#C9A84C', description TEXT, 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); ``` --- ## Roadmap ### v1.0 — Core Release *(current scope)* - [x] Dark/gold design system with Tailwind custom tokens - [x] FullCalendar main view with Month / Week / Day modes - [x] Drag-and-drop deliverable rescheduling - [x] Project creation with multiple deliverables and color selection - [x] Add / Edit / Delete for projects and deliverables - [x] Deliverable Focus View — slide-up drawer with horizontal timeline - [x] Active deliverable highlighting in Focus View - [x] Status badges (Upcoming / In Progress / Completed / Overdue) - [x] Flask REST API with SQLite persistence - [x] Cascade delete (project → deliverables) - [x] Right-click context menu on calendar events - [x] Single Docker container deployment with persistent volume --- ### v1.1 — Polish & UX - [ ] Keyboard shortcuts (`N` = new project, `Esc` = close modal, arrow keys = calendar nav) - [ ] Undo/redo for drag-and-drop with 30-second undo toast - [ ] Animated transitions on drawer and modal open/close - [ ] Deliverable sorting in Focus View (by date, by status) - [ ] Empty state illustrations for no projects or no deliverables - [ ] Responsive layout with collapsible sidebar for smaller screens ### v1.2 — Enhanced Calendar - [ ] Agenda sidebar showing all upcoming deliverables across projects - [ ] Week numbers in calendar header - [ ] Hover tooltip previewing deliverable details without opening Focus View - [ ] Date range selection for bulk deliverable creation - [ ] "Today" jump button with smooth scroll ### v2.0 — Collaboration & Auth - [ ] User authentication (Flask-Login + JWT) - [ ] Multi-user support with project ownership and sharing - [ ] Role-based access per project (Owner / Editor / Viewer) - [ ] Activity log per project (who changed what, when) - [ ] Comment threads on individual deliverables ### v2.1 — Notifications & Integrations - [ ] In-app notification center for approaching due dates - [ ] Email reminders at configurable intervals before due date - [ ] iCal / Google Calendar export per project - [ ] Slack webhook integration for deliverable status changes - [ ] CSV import/export for bulk project setup ### v2.2 — Advanced Views - [ ] Gantt view as an alternate layout - [ ] Kanban board (columns: Upcoming / In Progress / Completed) - [ ] Cross-project timeline showing all projects on one horizontal axis - [ ] Workload heatmap showing deliverable density per day - [ ] Archived projects with searchable history ### v3.0 — Intelligence Layer - [ ] AI-assisted scheduling suggestions based on project cadence - [ ] Auto-detect and surface overdue deliverables on dashboard load - [ ] Conflict detection — flag days with too many concurrent deliverables - [ ] Natural language input ("Add final draft due next Friday to CODA") --- *Built with intention. No subscriptions. No bloat. Just your fabrication workflow.*