feat: Implement task addition modal with escape key handling and z-index adjustments
This commit is contained in:
@@ -12,6 +12,7 @@ export default function ProjectTasksSection({ projectId }) {
|
|||||||
const [taskNotes, setTaskNotes] = useState({});
|
const [taskNotes, setTaskNotes] = useState({});
|
||||||
const [newNote, setNewNote] = useState({});
|
const [newNote, setNewNote] = useState({});
|
||||||
const [loadingNotes, setLoadingNotes] = useState({});
|
const [loadingNotes, setLoadingNotes] = useState({});
|
||||||
|
const [showAddTaskModal, setShowAddTaskModal] = useState(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchProjectTasks = async () => {
|
const fetchProjectTasks = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -46,6 +47,67 @@ export default function ProjectTasksSection({ projectId }) {
|
|||||||
|
|
||||||
fetchProjectTasks();
|
fetchProjectTasks();
|
||||||
}, [projectId]);
|
}, [projectId]);
|
||||||
|
// Handle escape key to close modal
|
||||||
|
useEffect(() => {
|
||||||
|
const handleEscape = (e) => {
|
||||||
|
if (e.key === "Escape" && showAddTaskModal) {
|
||||||
|
setShowAddTaskModal(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("keydown", handleEscape);
|
||||||
|
return () => document.removeEventListener("keydown", handleEscape);
|
||||||
|
}, [showAddTaskModal]);
|
||||||
|
// Prevent body scroll when modal is open and handle map z-index
|
||||||
|
useEffect(() => {
|
||||||
|
if (showAddTaskModal) {
|
||||||
|
// Prevent body scroll
|
||||||
|
document.body.style.overflow = "hidden";
|
||||||
|
|
||||||
|
// Find and temporarily lower z-index of leaflet containers
|
||||||
|
const leafletContainers = document.querySelectorAll(".leaflet-container");
|
||||||
|
leafletContainers.forEach((container) => {
|
||||||
|
container.style.zIndex = "1";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also handle navigation and other potential high z-index elements
|
||||||
|
const navElements = document.querySelectorAll("nav");
|
||||||
|
navElements.forEach((nav) => {
|
||||||
|
nav.style.position = "relative";
|
||||||
|
nav.style.zIndex = "1";
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Restore body scroll
|
||||||
|
document.body.style.overflow = "unset";
|
||||||
|
|
||||||
|
// Restore leaflet container z-index
|
||||||
|
const leafletContainers = document.querySelectorAll(".leaflet-container");
|
||||||
|
leafletContainers.forEach((container) => {
|
||||||
|
container.style.zIndex = "";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Restore navigation z-index
|
||||||
|
const navElements = document.querySelectorAll("nav");
|
||||||
|
navElements.forEach((nav) => {
|
||||||
|
nav.style.position = "";
|
||||||
|
nav.style.zIndex = "";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup function
|
||||||
|
return () => {
|
||||||
|
document.body.style.overflow = "unset";
|
||||||
|
const leafletContainers = document.querySelectorAll(".leaflet-container");
|
||||||
|
leafletContainers.forEach((container) => {
|
||||||
|
container.style.zIndex = "";
|
||||||
|
});
|
||||||
|
const navElements = document.querySelectorAll("nav");
|
||||||
|
navElements.forEach((nav) => {
|
||||||
|
nav.style.position = "";
|
||||||
|
nav.style.zIndex = "";
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}, [showAddTaskModal]);
|
||||||
const refetchTasks = async () => {
|
const refetchTasks = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/project-tasks?project_id=${projectId}`);
|
const res = await fetch(`/api/project-tasks?project_id=${projectId}`);
|
||||||
@@ -76,6 +138,7 @@ export default function ProjectTasksSection({ projectId }) {
|
|||||||
};
|
};
|
||||||
const handleTaskAdded = () => {
|
const handleTaskAdded = () => {
|
||||||
refetchTasks(); // Refresh the list
|
refetchTasks(); // Refresh the list
|
||||||
|
setShowAddTaskModal(false); // Close the modal
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleStatusChange = async (taskId, newStatus) => {
|
const handleStatusChange = async (taskId, newStatus) => {
|
||||||
@@ -197,24 +260,79 @@ export default function ProjectTasksSection({ projectId }) {
|
|||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h2 className="text-xl font-semibold text-gray-900">Project Tasks</h2>
|
<h2 className="text-xl font-semibold text-gray-900">Project Tasks</h2>
|
||||||
<Badge variant="default" className="text-sm">
|
<div className="flex items-center gap-3">
|
||||||
{projectTasks.length} {projectTasks.length === 1 ? "task" : "tasks"}
|
<Badge variant="default" className="text-sm">
|
||||||
</Badge>
|
{projectTasks.length} {projectTasks.length === 1 ? "task" : "tasks"}
|
||||||
</div>
|
</Badge>
|
||||||
|
<Button
|
||||||
{/* Add New Task Card */}
|
variant="primary"
|
||||||
<Card>
|
size="sm"
|
||||||
<CardHeader>
|
onClick={() => setShowAddTaskModal(true)}
|
||||||
<h3 className="text-lg font-medium text-gray-900">Add New Task</h3>
|
>
|
||||||
</CardHeader>
|
<svg
|
||||||
<CardContent>
|
className="w-4 h-4 mr-2"
|
||||||
<ProjectTaskForm
|
fill="none"
|
||||||
projectId={projectId}
|
stroke="currentColor"
|
||||||
onTaskAdded={handleTaskAdded}
|
viewBox="0 0 24 24"
|
||||||
/>
|
>
|
||||||
</CardContent>
|
<path
|
||||||
</Card>
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M12 4v16m8-8H4"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Add Task
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>{" "}
|
||||||
|
{/* Add Task Modal */}
|
||||||
|
{showAddTaskModal && (
|
||||||
|
<div
|
||||||
|
className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center"
|
||||||
|
style={{ zIndex: 99999 }}
|
||||||
|
onClick={(e) => {
|
||||||
|
if (e.target === e.currentTarget) {
|
||||||
|
setShowAddTaskModal(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="bg-white rounded-lg shadow-xl max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto"
|
||||||
|
style={{ zIndex: 100000 }}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between p-6 border-b">
|
||||||
|
<h3 className="text-lg font-semibold text-gray-900">
|
||||||
|
Add New Task
|
||||||
|
</h3>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowAddTaskModal(false)}
|
||||||
|
className="text-gray-400 hover:text-gray-600 transition-colors"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
className="w-6 h-6"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M6 18L18 6M6 6l12 12"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="p-6">
|
||||||
|
<ProjectTaskForm
|
||||||
|
projectId={projectId}
|
||||||
|
onTaskAdded={handleTaskAdded}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{/* Current Tasks */}
|
{/* Current Tasks */}
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
|
|||||||
Reference in New Issue
Block a user