Merge pull request #4 from jasonMPM/fix-3

Add files via upload
This commit is contained in:
jasonMPM
2026-03-05 13:18:02 -06:00
committed by GitHub
4 changed files with 46 additions and 19 deletions

View File

@@ -1,18 +1,19 @@
import Badge from '../UI/Badge'
import { formatDate } from '../../utils/dateHelpers'
export default function DeliverableCard({ deliverable, isActive, index, projectColor, onEdit }) {
export default function DeliverableCard({ deliverable, isActive, index, projectColor, onSelect, onEdit }) {
return (
<div
onClick={() => isActive && onEdit && onEdit(deliverable)}
className={`relative flex flex-col gap-2 rounded-xl border p-4 min-w-[190px] max-w-[230px] flex-shrink-0 transition-all duration-300 select-none
onClick={() => onSelect(deliverable.id)}
className={`relative flex flex-col gap-2 rounded-xl border p-4 min-w-[190px] max-w-[230px] flex-shrink-0 cursor-pointer
transition-all duration-200 select-none mt-4
${isActive
? 'border-gold bg-surface-elevated shadow-gold scale-105 ring-2 ring-gold/30 cursor-pointer'
: 'border-surface-border bg-surface cursor-default'
? 'border-gold bg-surface-elevated shadow-gold scale-105 ring-2 ring-gold/30'
: 'border-surface-border bg-surface hover:border-gold/40 hover:bg-surface-elevated/60'
}`}
>
{isActive && (
<div className="absolute -top-3 left-1/2 -translate-x-1/2 bg-gold text-surface text-[9px] font-black px-2.5 py-0.5 rounded-full tracking-widest uppercase">
<div className="absolute -top-5 left-1/2 -translate-x-1/2 bg-gold text-surface text-[9px] font-black px-2.5 py-0.5 rounded-full tracking-widest uppercase whitespace-nowrap">
Selected
</div>
)}
@@ -29,6 +30,14 @@ export default function DeliverableCard({ deliverable, isActive, index, projectC
{formatDate(deliverable.due_date)}
</p>
<Badge status={deliverable.status} />
{isActive && (
<button
onClick={(e) => { e.stopPropagation(); onEdit(deliverable) }}
className="mt-1 text-[10px] text-gold/70 hover:text-gold border border-gold/20 hover:border-gold/50 rounded px-2 py-0.5 transition-all text-center"
>
Edit
</button>
)}
</div>
)
}

View File

@@ -1,18 +1,25 @@
import DeliverableCard from './DeliverableCard'
import useFocusStore from '../../store/useFocusStore'
export default function FocusTimeline({ project, activeDeliverableId, onEditDeliverable }) {
const setActiveDeliverable = useFocusStore(s => s.setActiveDeliverable)
const sorted = [...(project.deliverables || [])].sort((a, b) => new Date(a.due_date) - new Date(b.due_date))
return (
<div className="px-6 pb-6 pt-5">
<div className="flex items-center gap-2 mb-5">
<div className="px-6 pb-6 pt-4">
<div className="flex items-center gap-2 mb-2">
<div className="w-3 h-3 rounded-full flex-shrink-0" style={{ backgroundColor: project.color }} />
<h3 className="text-gold font-bold text-base tracking-wide">{project.name}</h3>
{project.description && (
<span className="text-text-muted text-xs"> {project.description}</span>
)}
<span className="ml-auto text-text-muted/50 text-xs">{sorted.length} deliverable{sorted.length !== 1 ? 's' : ''}</span>
<span className="ml-auto text-text-muted/50 text-xs flex-shrink-0">
{sorted.length} deliverable{sorted.length !== 1 ? 's' : ''}
</span>
</div>
<div className="flex items-center overflow-x-auto pb-3 gap-0">
{/* pt-6 gives the "SELECTED" badge room above the card without clipping */}
<div className="flex items-center overflow-x-auto pb-3 pt-6 gap-0 min-h-0">
{sorted.map((d, i) => (
<div key={d.id} className="flex items-center flex-shrink-0">
<DeliverableCard
@@ -20,10 +27,11 @@ export default function FocusTimeline({ project, activeDeliverableId, onEditDeli
isActive={d.id === activeDeliverableId}
index={i}
projectColor={project.color}
onSelect={setActiveDeliverable}
onEdit={onEditDeliverable}
/>
{i < sorted.length - 1 && (
<div className="flex items-center flex-shrink-0 px-1">
<div className="flex items-center flex-shrink-0 px-1 mt-4">
<div className="h-px w-6 bg-surface-border" />
<span className="text-surface-border text-xs"></span>
</div>
@@ -31,7 +39,7 @@ export default function FocusTimeline({ project, activeDeliverableId, onEditDeli
</div>
))}
{sorted.length === 0 && (
<p className="text-text-muted text-sm italic">No deliverables yet.</p>
<p className="text-text-muted text-sm italic mt-0">No deliverables yet.</p>
)}
</div>
</div>

View File

@@ -1,23 +1,32 @@
import { useEffect } from 'react'
export default function Drawer({ isOpen, onClose, children }) {
useEffect(() => {
const h = (e) => { if (e.key === 'Escape') onClose() }
if (isOpen) document.addEventListener('keydown', h)
return () => document.removeEventListener('keydown', h)
}, [isOpen, onClose])
return (
<>
{isOpen && <div className="fixed inset-0 z-40 bg-black/50 backdrop-blur-sm" onClick={onClose} />}
{isOpen && (
<div className="fixed inset-0 z-40 bg-black/50 backdrop-blur-sm" onClick={onClose} />
)}
<div
className={`fixed bottom-0 left-0 right-0 z-50 bg-surface-raised border-t border-surface-border rounded-t-2xl shadow-2xl transition-transform duration-300 ease-in-out ${isOpen ? 'translate-y-0' : 'translate-y-full'}`}
style={{ maxHeight: '65vh' }}
style={{ maxHeight: '72vh' }}
>
<div className="relative flex items-center justify-between px-6 py-3 border-b border-surface-border">
{/* Handle bar + close */}
<div className="relative flex items-center justify-between px-6 py-3 border-b border-surface-border flex-shrink-0">
<div className="absolute left-1/2 -translate-x-1/2 top-2 w-10 h-1 bg-surface-border rounded-full" />
<div className="flex-1" />
<button onClick={onClose} className="text-text-muted hover:text-gold transition-colors text-lg"></button>
<button onClick={onClose} className="text-text-muted hover:text-gold transition-colors text-lg leading-none"></button>
</div>
{/* Scrollable content — overflow-visible on x so badge doesn't clip */}
<div className="overflow-y-auto overflow-x-hidden" style={{ maxHeight: 'calc(72vh - 52px)' }}>
{children}
</div>
<div className="overflow-y-auto" style={{ maxHeight: 'calc(65vh - 52px)' }}>{children}</div>
</div>
</>
)

View File

@@ -5,8 +5,9 @@ const useFocusStore = create((set) => ({
projectId: null,
activeDeliverableId: null,
openFocus: (projectId, deliverableId) => set({ isOpen: true, projectId, activeDeliverableId: deliverableId }),
closeFocus: () => set({ isOpen: false, projectId: null, activeDeliverableId: null }),
openFocus: (projectId, deliverableId) => set({ isOpen: true, projectId, activeDeliverableId: deliverableId }),
closeFocus: () => set({ isOpen: false, projectId: null, activeDeliverableId: null }),
setActiveDeliverable: (id) => set({ activeDeliverableId: id }),
}))
export default useFocusStore