Skip to content

Commit

Permalink
allow multiple subtasks within projects in view
Browse files Browse the repository at this point in the history
  • Loading branch information
hussaino03 committed Dec 31, 2024
1 parent 1700e24 commit 629fba6
Show file tree
Hide file tree
Showing 2 changed files with 197 additions and 3 deletions.
2 changes: 1 addition & 1 deletion client/src/components/Modal Management/Layout/View.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ const View = ({
showDetails ? 'max-h-[500px] py-3' : 'max-h-0'
} overflow-hidden`}
>
<div className="px-4 space-y-1.5 text-sm text-gray-600 dark:text-gray-300">
<div className="px-4 space-y-1.5 text-sm text-gray-600 dark:text-gray-300 max-h-[450px] overflow-y-auto">
{!isCompleted && (
<div className="flex items-center justify-between border-b border-gray-200 dark:border-gray-700 pb-2 mb-2">
<button
Expand Down
198 changes: 196 additions & 2 deletions client/src/components/Modal Management/Projects/ProjectView.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import React, { useState, useEffect } from 'react';
import { formatDeadline, isOverdue, calculateOverduePenalty } from '../../../utils/tasks/tasksUtils';
import ShareCode from './ShareCode';
import { Users } from 'lucide-react';
import { Users, Plus, X, Dumbbell, Target } from 'lucide-react';

const ProjectView = ({ task, isCompleted, updateTask, collaborationManager, userId }) => {
const [collaborators, setCollaborators] = useState([]);
const [showAddSubtask, setShowAddSubtask] = useState(false);
const [newSubtask, setNewSubtask] = useState({
name: '',
difficulty: 50,
importance: 50,
completed: false
});
const [error, setError] = useState('');

useEffect(() => {
if (task.isShared && task.sharedWith) {
Expand Down Expand Up @@ -49,6 +57,189 @@ const ProjectView = ({ task, isCompleted, updateTask, collaborationManager, user
});
};

const handleAddSubtask = async () => {
if (!newSubtask.name.trim()) {
setError('Task name is required');
return;
}

const updatedTask = {
...task,
subtasks: [...task.subtasks, newSubtask],
experience: task.experience + calculateSubtaskXP(newSubtask)
};

try {
if (task.isShared) {
// Sync with server first
await collaborationManager.updateSharedProjectDetails(task.id, updatedTask);
}

// Update local state after successful sync (or if not shared)
updateTask(task.id, updatedTask);
setNewSubtask({ name: '', difficulty: 50, importance: 50, completed: false });
setShowAddSubtask(false);
setError('');
} catch (error) {
console.error('[ERROR] Failed to add subtask:', error);
setError('Failed to add subtask. Please try again.');
}
};

const calculateSubtaskXP = (subtask) => {
return ((parseInt(subtask.difficulty) + parseInt(subtask.importance) + 20) * 5 +
parseInt((parseInt(subtask.difficulty) * parseInt(subtask.importance)) / 20));
};

const renderAddSubtaskInterface = () => {
if (!showAddSubtask) {
return (
<button
onClick={() => setShowAddSubtask(true)}
className="w-full py-2 flex items-center justify-center gap-2
text-sm text-gray-500 dark:text-gray-400
hover:bg-gray-50 dark:hover:bg-gray-700/50
rounded-lg transition-colors"
>
<Plus className="w-4 h-4" />
<span>Add Subtask</span>
</button>
);
}

return (
<div className="p-3">
<div className="flex flex-col sm:flex-row items-start sm:items-center gap-3">
{/* Task Name Input */}
<div className="flex-1 w-full sm:w-auto">
<input
type="text"
placeholder="New subtask name"
value={newSubtask.name}
onChange={(e) => {
setNewSubtask({ ...newSubtask, name: e.target.value });
setError('');
}}
className="w-full px-3 py-1.5 bg-white dark:bg-gray-700
border border-gray-300 dark:border-gray-600
rounded-lg text-sm text-gray-900 dark:text-gray-200"
/>
{error && (
<p className="absolute text-xs text-red-600 dark:text-red-400 mt-1">
{error}
</p>
)}
</div>

{/* Mobile: Single Combined Slider */}
<div className="sm:hidden w-full bg-gray-50 dark:bg-gray-800/50 p-2 rounded-lg">
<div className="flex items-center gap-2">
<Dumbbell className="w-3.5 h-3.5 text-gray-400 dark:text-gray-500" />
<input
type="range"
min="0"
max="100"
value={(parseInt(newSubtask.difficulty) + parseInt(newSubtask.importance)) / 2}
onChange={(e) => {
const value = Number(e.target.value);
setNewSubtask({
...newSubtask,
difficulty: value,
importance: value
});
}}
className="flex-1 h-1 appearance-none bg-gray-200 dark:bg-gray-700 rounded-full
[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-2.5
[&::-webkit-slider-thumb]:h-2.5 [&::-webkit-slider-thumb]:rounded-full
[&::-webkit-slider-thumb]:bg-blue-500 dark:[&::-webkit-slider-thumb]:bg-blue-400"
/>
<span className="text-sm font-medium text-blue-600 dark:text-blue-400 min-w-[32px] text-right">
{calculateSubtaskXP(newSubtask)}xp
</span>
</div>
</div>

{/* Desktop: Separate Sliders */}
<div className="hidden sm:flex items-center gap-4 bg-gray-50 dark:bg-gray-800/50 px-3 rounded-lg w-[300px] h-[34px]">
<div className="flex items-center gap-2">
<Dumbbell className="w-3.5 h-3.5 text-gray-400 dark:text-gray-500" />
<input
type="range"
min="0"
max="100"
value={newSubtask.difficulty}
onChange={(e) => setNewSubtask({ ...newSubtask, difficulty: Number(e.target.value) })}
className="w-16 h-1 appearance-none bg-gray-200 dark:bg-gray-700 rounded-full
[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-2.5
[&::-webkit-slider-thumb]:h-2.5 [&::-webkit-slider-thumb]:rounded-full
[&::-webkit-slider-thumb]:bg-blue-500 dark:[&::-webkit-slider-thumb]:bg-blue-400"
/>
<span className="text-[10px] font-medium text-gray-500 dark:text-gray-400 w-4 text-right">
{Math.round(newSubtask.difficulty/10)}
</span>
</div>

<div className="flex items-center gap-2">
<Target className="w-3.5 h-3.5 text-gray-400 dark:text-gray-500" />
<input
type="range"
min="0"
max="100"
value={newSubtask.importance}
onChange={(e) => setNewSubtask({ ...newSubtask, importance: Number(e.target.value) })}
className="w-16 h-1 appearance-none bg-gray-200 dark:bg-gray-700 rounded-full
[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-2.5
[&::-webkit-slider-thumb]:h-2.5 [&::-webkit-slider-thumb]:rounded-full
[&::-webkit-slider-thumb]:bg-blue-500 dark:[&::-webkit-slider-thumb]:bg-blue-400"
/>
<span className="text-[10px] font-medium text-gray-500 dark:text-gray-400 w-4 text-right">
{Math.round(newSubtask.importance/10)}
</span>
</div>

<div className="ml-1 flex items-center">
<span className="text-sm font-medium text-blue-600 dark:text-blue-400">
{calculateSubtaskXP(newSubtask)}
</span>
</div>
</div>

{/* Action Buttons */}
<div className="flex gap-2 w-full sm:w-auto justify-end">
<button
onClick={handleAddSubtask}
className="flex-1 sm:flex-initial p-1.5 rounded-lg bg-blue-500/10 hover:bg-blue-500/20
text-blue-600 dark:text-blue-400 transition-colors flex items-center justify-center"
>
<Plus className="w-4 h-4" />
</button>
<button
onClick={() => {
setShowAddSubtask(false);
setError('');
}}
className="flex-1 sm:flex-initial p-1.5 rounded-lg bg-red-500/10 hover:bg-red-500/20
text-red-600 dark:text-red-400 transition-colors flex items-center justify-center"
>
<X className="w-4 h-4" />
</button>
</div>
</div>
</div>
);
};

useEffect(() => {
let cleanup;
if (task.isShared) {
// Start syncing the project
cleanup = collaborationManager.startProjectSync(task.id);
}
return () => {
if (cleanup) cleanup();
};
}, [task.id, task.isShared, collaborationManager]);

return (
<div className="space-y-4">
{/* Progress Bar and Share Button */}
Expand Down Expand Up @@ -108,7 +299,7 @@ const ProjectView = ({ task, isCompleted, updateTask, collaborationManager, user
</div>
)}

{/* Subtasks List */}
{/* Subtasks List with Add Button */}
<div className="border dark:border-gray-700 rounded-lg overflow-hidden">
<div className="divide-y divide-gray-200 dark:divide-gray-700">
{task.subtasks.map((subtask, index) => (
Expand Down Expand Up @@ -149,6 +340,9 @@ const ProjectView = ({ task, isCompleted, updateTask, collaborationManager, user
</div>
</div>
))}

{/* Subtask Interface */}
{!isCompleted && renderAddSubtaskInterface()}
</div>
</div>

Expand Down

0 comments on commit 629fba6

Please sign in to comment.