add subcontractor task assign/list for admin and owner feature

This commit is contained in:
Satyam Rastogi
2026-05-18 17:16:11 +05:30
parent 686325dedb
commit c7a9849d1f
7 changed files with 1724 additions and 1 deletions
@@ -0,0 +1,89 @@
import React, { useEffect, useMemo, useState } from 'react';
import { createPortal } from 'react-dom';
import { X, Repeat, User, Loader2 } from 'lucide-react';
import { useMockStore } from '../../data/mockStore';
const inputBase = "w-full bg-white dark:bg-[#18181b] border border-zinc-200 dark:border-white/10 rounded-xl px-4 py-2.5 text-sm text-zinc-900 dark:text-white focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-all";
const ReassignTaskModal = ({ isOpen, onClose, task }) => {
const { subcontractors, reassignSubcontractorTask } = useMockStore();
const [selected, setSelected] = useState('');
const [submitting, setSubmitting] = useState(false);
const activeSubs = useMemo(
() => subcontractors.filter(s => s.status === 'active' && s.id !== task?.subcontractorId),
[subcontractors, task],
);
useEffect(() => {
if (isOpen) {
setSelected('');
setSubmitting(false);
}
}, [isOpen]);
useEffect(() => {
const handler = (e) => { if (e.key === 'Escape') onClose(); };
if (isOpen) window.addEventListener('keydown', handler);
return () => window.removeEventListener('keydown', handler);
}, [isOpen, onClose]);
if (!isOpen || !task) return null;
const handleConfirm = async () => {
if (!selected) return;
setSubmitting(true);
await new Promise(r => setTimeout(r, 300));
reassignSubcontractorTask(task.id, selected);
setSubmitting(false);
onClose();
};
return createPortal(
<div className="fixed inset-0 z-[9999] flex items-center justify-center p-4" role="dialog" aria-modal="true">
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={submitting ? undefined : onClose} />
<div className="relative w-full max-w-md bg-white dark:bg-[#121214] rounded-2xl shadow-2xl border border-zinc-200 dark:border-white/10 overflow-hidden animate-in zoom-in-95 duration-200">
<div className="px-5 py-4 border-b border-zinc-200 dark:border-white/10 flex items-center justify-between bg-zinc-50/50 dark:bg-white/5">
<div className="flex items-center gap-3">
<div className="p-2 rounded-xl bg-purple-500/10 text-purple-500"><Repeat size={18} /></div>
<h2 className="text-base font-bold text-zinc-900 dark:text-white uppercase tracking-wider">Reassign Task</h2>
</div>
<button onClick={onClose} disabled={submitting} className="p-2 rounded-lg bg-zinc-100 dark:bg-white/10 text-zinc-500 hover:text-red-500 transition-colors disabled:opacity-50">
<X size={18} />
</button>
</div>
<div className="p-5 space-y-4">
<div className="text-sm text-zinc-600 dark:text-zinc-400">
Reassign <span className="font-bold text-zinc-900 dark:text-white">{task.title}</span> currently assigned to <span className="font-bold text-zinc-900 dark:text-white">{task.subcontractorName}</span>.
</div>
<div className="space-y-1.5">
<label htmlFor="reassign-sub" className="text-xs font-bold uppercase tracking-wider text-zinc-600 dark:text-zinc-400 flex items-center gap-1.5">
<User size={12} /> New Subcontractor
</label>
<select id="reassign-sub" value={selected} onChange={(e) => setSelected(e.target.value)} className={inputBase}>
<option value="" disabled>Select a subcontractor</option>
{activeSubs.map(s => (
<option key={s.id} value={s.id}>{s.name} {s.tradeType}</option>
))}
</select>
{activeSubs.length === 0 && (
<p className="text-xs text-zinc-500 mt-1">No other active subcontractors available.</p>
)}
</div>
</div>
<div className="px-5 py-4 border-t border-zinc-200 dark:border-white/10 bg-zinc-50 dark:bg-white/5 flex justify-end gap-2">
<button type="button" onClick={onClose} disabled={submitting} className="px-4 py-2 text-sm font-bold rounded-xl border border-zinc-200 dark:border-white/10 text-zinc-600 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-white/5 transition-colors disabled:opacity-50">
Cancel
</button>
<button type="button" onClick={handleConfirm} disabled={!selected || submitting} className="inline-flex items-center gap-2 px-5 py-2 text-sm font-bold rounded-xl bg-purple-600 hover:bg-purple-500 text-white shadow-lg shadow-purple-500/20 transition-all disabled:opacity-50">
{submitting && <Loader2 size={14} className="animate-spin" />}
Reassign
</button>
</div>
</div>
</div>,
document.body,
);
};
export default ReassignTaskModal;