217 lines
13 KiB
TypeScript
217 lines
13 KiB
TypeScript
import React from 'react';
|
|
import { CardHeader, CardTitle, CardDescription } from '@/Components/ui/card';
|
|
import { Button } from '@/Components/ui/button';
|
|
import { Input } from '@/Components/ui/input';
|
|
import { Label } from '@/Components/ui/label';
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/Components/ui/select';
|
|
import { Plus, Trash2, ChevronLeft, ChevronRight } from 'lucide-react';
|
|
|
|
interface Step2TasksProps {
|
|
localMilestones: any[];
|
|
localTasks: any[];
|
|
prepopulateMilestones: boolean;
|
|
setPrepopulateMilestones: (v: boolean) => void;
|
|
addMilestone: () => void;
|
|
removeMilestone: (idx: number) => void;
|
|
updateMilestone: (idx: number, field: string, val: string) => void;
|
|
addTask: () => void;
|
|
removeTask: (idx: number) => void;
|
|
updateTask: (idx: number, field: string, val: string) => void;
|
|
handleBack: () => void;
|
|
handleSaveTasks: () => void;
|
|
}
|
|
|
|
export default function Step2Tasks({
|
|
localMilestones,
|
|
localTasks,
|
|
prepopulateMilestones,
|
|
setPrepopulateMilestones,
|
|
addMilestone,
|
|
removeMilestone,
|
|
updateMilestone,
|
|
addTask,
|
|
removeTask,
|
|
updateTask,
|
|
handleBack,
|
|
handleSaveTasks
|
|
}: Step2TasksProps) {
|
|
return (
|
|
<div className="p-6 space-y-6">
|
|
<CardHeader className="p-0 pb-4 border-b border-slate-100 flex flex-row items-center justify-between">
|
|
<div>
|
|
<CardTitle className="text-md font-semibold text-slate-800">Step 2: Project Tasks & Milestones</CardTitle>
|
|
<CardDescription className="text-xs">Schedule the project milestones and assign tasks under them.</CardDescription>
|
|
</div>
|
|
<div className="flex items-center gap-2 bg-slate-50 p-2 rounded-lg border border-slate-150">
|
|
<input
|
|
id="prepopulate"
|
|
type="checkbox"
|
|
checked={prepopulateMilestones}
|
|
onChange={e => setPrepopulateMilestones(e.target.checked)}
|
|
className="rounded border-slate-300 text-emerald-600 focus:ring-emerald-500 h-4 w-4"
|
|
/>
|
|
<Label htmlFor="prepopulate" className="text-xs font-semibold text-slate-700 cursor-pointer">Pre-populate Defaults</Label>
|
|
</div>
|
|
</CardHeader>
|
|
|
|
{/* Milestones Editor */}
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between">
|
|
<h4 className="text-xs font-bold uppercase tracking-wider text-slate-500">1. Milestones</h4>
|
|
<Button variant="outline" size="sm" onClick={addMilestone} className="h-8 border-slate-200 text-slate-600 hover:bg-slate-50">
|
|
<Plus className="mr-1.5 h-3.5 w-3.5" /> Add Milestone
|
|
</Button>
|
|
</div>
|
|
|
|
{localMilestones.length === 0 ? (
|
|
<p className="text-xs italic text-slate-400 py-2">No milestones defined yet. Toggle pre-populate defaults or add custom ones.</p>
|
|
) : (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
{localMilestones.map((m, idx) => (
|
|
<div key={idx} className="flex flex-col sm:flex-row gap-2 items-start sm:items-center bg-slate-50/50 p-3 rounded-lg border border-slate-200/60">
|
|
<div className="flex-1 space-y-1 w-full sm:w-auto">
|
|
<Label className="text-[10px] font-bold text-slate-600">Milestone Name <span className="text-red-500 font-bold">*</span></Label>
|
|
<Input
|
|
placeholder="Milestone Name"
|
|
value={m.name}
|
|
onChange={e => updateMilestone(idx, 'name', e.target.value)}
|
|
className="h-8 text-xs border-slate-200"
|
|
/>
|
|
</div>
|
|
<div className="w-full sm:w-24">
|
|
<Label className="text-[10px] font-bold text-slate-600">Weight % <span className="text-red-500 font-bold">*</span></Label>
|
|
<Input
|
|
type="number"
|
|
placeholder="Weight %"
|
|
value={m.weight_percentage}
|
|
onChange={e => updateMilestone(idx, 'weight_percentage', e.target.value)}
|
|
className="h-8 text-xs border-slate-200 text-right font-medium"
|
|
/>
|
|
</div>
|
|
<div className="w-full sm:w-32">
|
|
<Label className="text-[10px] font-bold text-slate-600">Target Date <span className="text-red-500 font-bold">*</span></Label>
|
|
<Input
|
|
type="date"
|
|
value={m.target_date || ''}
|
|
onChange={e => updateMilestone(idx, 'target_date', e.target.value)}
|
|
className="h-8 text-xs border-slate-200"
|
|
/>
|
|
</div>
|
|
<div className="sm:pt-4">
|
|
<Button variant="ghost" size="icon" onClick={() => removeMilestone(idx)} className="h-8 w-8 text-rose-500 hover:text-rose-700 hover:bg-rose-50">
|
|
<Trash2 className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Tasks Editor */}
|
|
<div className="space-y-4 pt-4 border-t border-slate-100">
|
|
<div className="flex items-center justify-between">
|
|
<h4 className="text-xs font-bold uppercase tracking-wider text-slate-500">2. Scheduled Tasks</h4>
|
|
<Button variant="outline" size="sm" onClick={addTask} className="h-8 border-slate-200 text-slate-600 hover:bg-slate-50">
|
|
<Plus className="mr-1.5 h-3.5 w-3.5" /> Add Task
|
|
</Button>
|
|
</div>
|
|
|
|
{localTasks.length === 0 ? (
|
|
<p className="text-xs italic text-slate-400 py-2">No tasks defined yet. Add tasks and link them to milestones.</p>
|
|
) : (
|
|
<div className="space-y-3">
|
|
{localTasks.map((t, idx) => (
|
|
<div key={idx} className="bg-slate-50/50 p-4 rounded-xl border border-slate-200/60 space-y-3 relative">
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
|
<div className="md:col-span-2">
|
|
<Label className="text-[10px] font-bold text-slate-600">Task Name <span className="text-red-500 font-bold">*</span></Label>
|
|
<Input
|
|
placeholder="Task Name"
|
|
value={t.name}
|
|
onChange={e => updateTask(idx, 'name', e.target.value)}
|
|
className="h-8 text-xs border-slate-200 mt-1"
|
|
/>
|
|
</div>
|
|
<div className="flex items-end gap-2">
|
|
<div className="min-w-0 flex-1">
|
|
<Label className="text-[10px] font-bold text-slate-600">Milestone <span className="text-red-500 font-bold">*</span></Label>
|
|
<Select
|
|
value={t.milestone_ulid}
|
|
onValueChange={val => updateTask(idx, 'milestone_ulid', val || '')}
|
|
items={localMilestones.map((m, mIdx) => ({ value: m.ulid || String(mIdx), label: m.name || `Milestone ${mIdx+1}` }))}
|
|
>
|
|
<SelectTrigger className="h-8 text-xs border-slate-200 mt-1">
|
|
<SelectValue placeholder="Select Milestone" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{localMilestones.map((m, mIdx) => (
|
|
<SelectItem key={mIdx} value={m.ulid || String(mIdx)}>
|
|
{m.name || `Milestone ${mIdx+1}`}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon"
|
|
aria-label={`Delete task ${idx + 1}`}
|
|
title="Delete task"
|
|
onClick={() => removeTask(idx)}
|
|
className="mb-0 h-8 w-8 shrink-0 rounded-lg p-2 text-rose-500 hover:bg-rose-50 hover:text-rose-700"
|
|
>
|
|
<Trash2 className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
|
<div className="md:col-span-2">
|
|
<Label className="text-[10px] font-bold text-slate-600">Task Description / Scope <span className="text-red-500 font-bold">*</span></Label>
|
|
<Input
|
|
placeholder="Enter detailed task description..."
|
|
value={t.description}
|
|
onChange={e => updateTask(idx, 'description', e.target.value)}
|
|
className="h-8 text-xs border-slate-200 mt-1"
|
|
/>
|
|
</div>
|
|
<div className="grid grid-cols-2 gap-2">
|
|
<div>
|
|
<Label className="text-[10px] text-slate-500">Start Date</Label>
|
|
<Input
|
|
type="date"
|
|
value={t.start_date}
|
|
onChange={e => updateTask(idx, 'start_date', e.target.value)}
|
|
className="h-8 text-xs border-slate-200 mt-1"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<Label className="text-[10px] text-slate-500">Target End</Label>
|
|
<Input
|
|
type="date"
|
|
value={t.end_date}
|
|
onChange={e => updateTask(idx, 'end_date', e.target.value)}
|
|
className="h-8 text-xs border-slate-200 mt-1"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex justify-between pt-6 border-t border-slate-100">
|
|
<Button variant="outline" onClick={handleBack}>
|
|
<ChevronLeft className="mr-2 h-4 w-4" /> Back
|
|
</Button>
|
|
<Button onClick={handleSaveTasks} className="bg-emerald-600 hover:bg-emerald-700 text-white font-medium">
|
|
Save & Next <ChevronRight className="ml-2 h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|