From 77df004e4e713491c3f3fbcbb1ff3999d45d3d6c Mon Sep 17 00:00:00 2001 From: jasonMPM Date: Thu, 5 Mar 2026 16:44:36 -0600 Subject: [PATCH] Add files via upload --- README.md | 5 +- frontend/index.html | 2 + .../src/components/Calendar/MainCalendar.jsx | 184 +----------------- 3 files changed, 6 insertions(+), 185 deletions(-) diff --git a/README.md b/README.md index 134e361..8ad933c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ -# FabDash +# FABDASH **Fabrication Dashboard** — A sleek, modern project management & scheduling application built for fabrication workflows. +Repo: https://github.com/jasonMPM/fabdash ![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) @@ -747,4 +748,4 @@ To add a new column in a future version, append its `ALTER TABLE` statement to t --- -*Built with intention. No subscriptions. No bloat. Just your fabrication workflow.* +*Built with intention. No subscriptions. No bloat. Just your fabrication workflow.* \ No newline at end of file diff --git a/frontend/index.html b/frontend/index.html index 4537fe6..2cd8bc8 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -5,6 +5,8 @@ FABDASH + +
diff --git a/frontend/src/components/Calendar/MainCalendar.jsx b/frontend/src/components/Calendar/MainCalendar.jsx index 2e90d98..636aeef 100644 --- a/frontend/src/components/Calendar/MainCalendar.jsx +++ b/frontend/src/components/Calendar/MainCalendar.jsx @@ -1,183 +1 @@ -import { useRef, useState, useCallback, useEffect } from 'react' -import FullCalendar from '@fullcalendar/react' -import dayGridPlugin from '@fullcalendar/daygrid' -import timeGridPlugin from '@fullcalendar/timegrid' -import interactionPlugin from '@fullcalendar/interaction' -import useProjectStore from '../../store/useProjectStore' -import useFocusStore from '../../store/useFocusStore' -import useUIStore from '../../store/useUIStore' -import useToastStore from '../../store/useToastStore' -import { updateDeliverable, deleteDeliverable } from '../../api/deliverables' -import DeliverableModal from '../Deliverables/DeliverableModal' -import ContextMenu from '../UI/ContextMenu' -import EventTooltip from './EventTooltip' -import WorkloadHeatmap from './WorkloadHeatmap' - -export default function MainCalendar({ onCalendarReady }) { - const calRef = useRef(null) - const { projects, updateDeliverable: storeUpdate, removeDeliverable } = useProjectStore() - const openFocus = useFocusStore(s => s.openFocus) - const { showHeatmap, toggleHeatmap } = useUIStore() - const addToast = useToastStore(s => s.addToast) - - const [modal, setModal] = useState({ open: false, deliverable: null, defaultDate: '' }) - const [contextMenu, setContextMenu] = useState(null) - const [tooltip, setTooltip] = useState(null) - - // Expose calendar API to App.jsx for keyboard shortcuts - useEffect(() => { - if (calRef.current && onCalendarReady) { - onCalendarReady(calRef.current.getApi()) - } - }, []) - - const events = projects.flatMap(p => - (p.deliverables || []).map(d => ({ - id: String(d.id), - title: `${p.name}: ${d.title}`, - start: d.due_date, - allDay: true, - backgroundColor: p.color, - borderColor: p.color, - extendedProps: { deliverableId: d.id, projectId: p.id }, - })) - ) - - const getCtx = (projectId, deliverableId) => { - const project = projects.find(p => p.id === projectId) - const deliverable = project?.deliverables.find(d => d.id === deliverableId) - return { project, deliverable } - } - - const handleEventClick = useCallback(({ event }) => { - const { deliverableId, projectId } = event.extendedProps - openFocus(projectId, deliverableId) - }, [openFocus]) - - // Drag-and-drop with 30-second undo toast - const handleEventDrop = useCallback(async ({ event, oldEvent }) => { - const { deliverableId } = event.extendedProps - const newDate = event.startStr.substring(0, 10) - const oldDate = oldEvent.startStr.substring(0, 10) - storeUpdate(await updateDeliverable(deliverableId, { due_date: newDate })) - addToast({ - message: `Moved to ${newDate}`, - duration: 30, - undoFn: async () => { - storeUpdate(await updateDeliverable(deliverableId, { due_date: oldDate })) - }, - }) - }, [storeUpdate, addToast]) - - // Click empty date — open add modal - const handleDateClick = useCallback(({ dateStr }) => { - setModal({ open: true, deliverable: null, defaultDate: dateStr.substring(0, 10) }) - }, []) - - // Date range drag-select — pre-fill modal with start date - const handleSelect = useCallback(({ startStr }) => { - setModal({ open: true, deliverable: null, defaultDate: startStr.substring(0, 10) }) - }, []) - - // Attach dblclick + contextmenu + tooltip via eventDidMount - const handleEventDidMount = useCallback(({ event, el }) => { - const { deliverableId, projectId } = event.extendedProps - - el.addEventListener('mouseenter', (e) => { - const { project, deliverable } = getCtx(projectId, deliverableId) - setTooltip({ x: e.clientX, y: e.clientY, project, deliverable }) - }) - el.addEventListener('mouseleave', () => setTooltip(null)) - el.addEventListener('mousemove', (e) => { - setTooltip(prev => prev ? { ...prev, x: e.clientX, y: e.clientY } : null) - }) - - el.addEventListener('dblclick', (e) => { - e.preventDefault(); e.stopPropagation() - setTooltip(null) - const { deliverable } = getCtx(projectId, deliverableId) - if (deliverable) setModal({ open: true, deliverable, defaultDate: '' }) - }) - - el.addEventListener('contextmenu', (e) => { - e.preventDefault(); e.stopPropagation() - setTooltip(null) - const { project, deliverable } = getCtx(projectId, deliverableId) - if (!deliverable) return - setContextMenu({ - x: e.clientX, y: e.clientY, - items: [ - { icon: '✎', label: 'Edit Deliverable', action: () => setModal({ open: true, deliverable, defaultDate: '' }) }, - { icon: '◎', label: 'Open Focus View', action: () => openFocus(projectId, deliverableId) }, - ...(project?.drive_url ? [{ icon: '⬡', label: 'Open Drive Folder', action: () => window.open(project.drive_url, '_blank') }] : []), - { separator: true }, - { icon: '✕', label: 'Delete Deliverable', danger: true, - action: async () => { - if (window.confirm(`Delete "${deliverable.title}"?`)) { - await deleteDeliverable(deliverableId); removeDeliverable(deliverableId) - } - } - }, - ], - }) - }) - }, [projects, openFocus]) - - return ( -
e.preventDefault()}> - {/* View toggle toolbar */} -
- -
- - {/* Main content area */} -
- {showHeatmap ? ( - - ) : ( -
- -
- )} -
- - setModal({ open: false, deliverable: null, defaultDate: '' })} - deliverable={modal.deliverable} - defaultDate={modal.defaultDate} - /> - - {contextMenu && ( - setContextMenu(null)} /> - )} - - -
- ) -} +// ...file truncated in this snippet — only the toolbar area is shown changed