89 lines
4.2 KiB
JavaScript
89 lines
4.2 KiB
JavaScript
import { useState, useEffect } from 'react'
|
|
import Modal from '../UI/Modal'
|
|
import Button from '../UI/Button'
|
|
import useProjectStore from '../../store/useProjectStore'
|
|
import { STATUS_OPTIONS } from '../../utils/statusHelpers'
|
|
|
|
export default function DeliverableModal({ isOpen, onClose, deliverable, projectId, defaultDate }) {
|
|
const { addDeliverable, updateDeliverable: storeUpdate, removeDeliverable, projects } = useProjectStore()
|
|
const [title, setTitle] = useState('')
|
|
const [dueDate, setDueDate] = useState('')
|
|
const [status, setStatus] = useState('upcoming')
|
|
const [pid, setPid] = useState('')
|
|
const [saving, setSaving] = useState(false)
|
|
const isEditing = !!deliverable
|
|
|
|
useEffect(() => {
|
|
if (deliverable) {
|
|
setTitle(deliverable.title || ''); setDueDate(deliverable.due_date?.substring(0,10) || '')
|
|
setStatus(deliverable.status || 'upcoming'); setPid(deliverable.project_id || '')
|
|
} else {
|
|
setTitle(''); setDueDate(defaultDate || ''); setStatus('upcoming'); setPid(projectId || '')
|
|
}
|
|
}, [deliverable, isOpen, projectId, defaultDate])
|
|
|
|
const handleDelete = async () => {
|
|
if (!window.confirm('Delete this deliverable?')) return
|
|
await removeDeliverable(deliverable.id)
|
|
onClose()
|
|
}
|
|
|
|
const handleSubmit = async () => {
|
|
if (!title.trim() || !dueDate || !pid) return
|
|
setSaving(true)
|
|
try {
|
|
if (isEditing) {
|
|
await storeUpdate(deliverable.id, { title, due_date: dueDate, status })
|
|
} else {
|
|
await addDeliverable({ title, due_date: dueDate, status, project_id: Number(pid) })
|
|
}
|
|
onClose()
|
|
} finally { setSaving(false) }
|
|
}
|
|
|
|
return (
|
|
<Modal isOpen={isOpen} onClose={onClose} title={isEditing ? 'Edit Deliverable' : 'Add Deliverable'}>
|
|
<div className="space-y-4">
|
|
{!isEditing && (
|
|
<div>
|
|
<label className="block text-xs text-text-muted mb-1 font-medium">Project *</label>
|
|
<select className="w-full bg-surface border border-surface-border rounded-lg px-3 py-2 text-sm text-text-primary focus:outline-none focus:border-gold"
|
|
value={pid} onChange={e => setPid(e.target.value)}>
|
|
<option value="">Select a project...</option>
|
|
{projects.map(p => <option key={p.id} value={p.id}>{p.name}</option>)}
|
|
</select>
|
|
</div>
|
|
)}
|
|
<div>
|
|
<label className="block text-xs text-text-muted mb-1 font-medium">Title *</label>
|
|
<input className="w-full bg-surface border border-surface-border rounded-lg px-3 py-2 text-sm text-text-primary focus:outline-none focus:border-gold transition-colors"
|
|
value={title} onChange={e => setTitle(e.target.value)} placeholder="Deliverable title..." />
|
|
</div>
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<div>
|
|
<label className="block text-xs text-text-muted mb-1 font-medium">Due Date *</label>
|
|
<input type="date" className="w-full bg-surface border border-surface-border rounded-lg px-3 py-2 text-sm text-text-primary focus:outline-none focus:border-gold transition-colors"
|
|
value={dueDate} onChange={e => setDueDate(e.target.value)} />
|
|
</div>
|
|
<div>
|
|
<label className="block text-xs text-text-muted mb-1 font-medium">Status</label>
|
|
<select className="w-full bg-surface border border-surface-border rounded-lg px-3 py-2 text-sm text-text-primary focus:outline-none focus:border-gold"
|
|
value={status} onChange={e => setStatus(e.target.value)}>
|
|
{STATUS_OPTIONS.map(s => <option key={s.value} value={s.value}>{s.label}</option>)}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div className="flex justify-between pt-2 border-t border-surface-border">
|
|
{isEditing ? <Button variant="danger" onClick={handleDelete}>Delete</Button> : <div />}
|
|
<div className="flex gap-2">
|
|
<Button variant="secondary" onClick={onClose}>Cancel</Button>
|
|
<Button onClick={handleSubmit} disabled={saving || !title.trim() || !dueDate || !pid}>
|
|
{saving ? 'Saving...' : isEditing ? 'Save Changes' : 'Add Deliverable'}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Modal>
|
|
)
|
|
}
|