Fix 5 testing bugs: priority badge width, recurring event errors, nested subtask UI, comment timestamps, close button
- Widen priority badge from w-10 to w-14 to fit "medium" text, add "none" case - Guard against null end_datetime in event update validation - Exclude current event from this_and_future DELETE to prevent 404 - Use Python-side datetime.now for comment timestamps (avoids UTC offset) - Hide "Add subtask" button when viewing a subtask (prevents nested nesting) - Add X close button to TaskDetailPanel header on desktop Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6e50089201
commit
bfe97fd749
@ -1,4 +1,4 @@
|
||||
from sqlalchemy import Text, Integer, ForeignKey, func
|
||||
from sqlalchemy import Text, Integer, ForeignKey
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship as sa_relationship
|
||||
from datetime import datetime
|
||||
from app.database import Base
|
||||
@ -12,7 +12,7 @@ class TaskComment(Base):
|
||||
Integer, ForeignKey("project_tasks.id", ondelete="CASCADE"), nullable=False, index=True
|
||||
)
|
||||
content: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
created_at: Mapped[datetime] = mapped_column(server_default=func.now())
|
||||
created_at: Mapped[datetime] = mapped_column(default=datetime.now)
|
||||
|
||||
# Relationships
|
||||
task: Mapped["ProjectTask"] = sa_relationship(back_populates="comments")
|
||||
|
||||
@ -287,7 +287,7 @@ async def update_event(
|
||||
|
||||
start = update_data.get("start_datetime", event.start_datetime)
|
||||
end_dt = update_data.get("end_datetime", event.end_datetime)
|
||||
if end_dt < start:
|
||||
if end_dt is not None and end_dt < start:
|
||||
raise HTTPException(status_code=400, detail="End datetime must be after start datetime")
|
||||
|
||||
if scope == "this":
|
||||
@ -316,6 +316,7 @@ async def update_event(
|
||||
delete(CalendarEvent).where(
|
||||
CalendarEvent.parent_event_id == parent_id,
|
||||
CalendarEvent.original_start >= this_original_start,
|
||||
CalendarEvent.id != event_id,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@ -57,10 +57,11 @@ export default function UpcomingWidget({ items, days = 7 }: UpcomingWidgetProps)
|
||||
{config.label}
|
||||
</span>
|
||||
<span className={cn(
|
||||
'text-[9px] font-semibold px-1.5 py-0.5 rounded shrink-0 w-10 text-center',
|
||||
'text-[9px] font-semibold px-1.5 py-0.5 rounded shrink-0 w-14 text-center',
|
||||
item.priority === 'high' ? 'bg-red-500/10 text-red-400' :
|
||||
item.priority === 'medium' ? 'bg-yellow-500/10 text-yellow-400' :
|
||||
item.priority === 'low' ? 'bg-green-500/10 text-green-400' :
|
||||
item.priority === 'none' ? 'bg-gray-500/10 text-gray-400' :
|
||||
'invisible'
|
||||
)}>
|
||||
{item.priority || ''}
|
||||
|
||||
@ -575,6 +575,7 @@ export default function ProjectDetail() {
|
||||
onEdit={(task) => openTaskForm(task, null)}
|
||||
onDelete={handleDeleteTask}
|
||||
onAddSubtask={(parentId) => openTaskForm(null, parentId)}
|
||||
onClose={() => setSelectedTaskId(null)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -603,6 +604,7 @@ export default function ProjectDetail() {
|
||||
onEdit={(task) => openTaskForm(task, null)}
|
||||
onDelete={handleDeleteTask}
|
||||
onAddSubtask={(parentId) => openTaskForm(null, parentId)}
|
||||
onClose={() => setSelectedTaskId(null)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -4,7 +4,7 @@ import { toast } from 'sonner';
|
||||
import { format, formatDistanceToNow, parseISO } from 'date-fns';
|
||||
import {
|
||||
Pencil, Trash2, Plus, MessageSquare, ClipboardList,
|
||||
Calendar, User, Flag, Activity, Send,
|
||||
Calendar, User, Flag, Activity, Send, X,
|
||||
} from 'lucide-react';
|
||||
import api, { getErrorMessage } from '@/lib/api';
|
||||
import type { ProjectTask, TaskComment, Person } from '@/types';
|
||||
@ -37,6 +37,7 @@ interface TaskDetailPanelProps {
|
||||
onEdit: (task: ProjectTask) => void;
|
||||
onDelete: (taskId: number) => void;
|
||||
onAddSubtask: (parentId: number) => void;
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
export default function TaskDetailPanel({
|
||||
@ -45,6 +46,7 @@ export default function TaskDetailPanel({
|
||||
onEdit,
|
||||
onDelete,
|
||||
onAddSubtask,
|
||||
onClose,
|
||||
}: TaskDetailPanelProps) {
|
||||
const queryClient = useQueryClient();
|
||||
const [commentText, setCommentText] = useState('');
|
||||
@ -145,6 +147,17 @@ export default function TaskDetailPanel({
|
||||
>
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
{onClose && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-7 w-7"
|
||||
onClick={onClose}
|
||||
title="Close panel"
|
||||
>
|
||||
<X className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -222,6 +235,7 @@ export default function TaskDetailPanel({
|
||||
</span>
|
||||
)}
|
||||
</h4>
|
||||
{!task.parent_task_id && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
@ -231,6 +245,7 @@ export default function TaskDetailPanel({
|
||||
<Plus className="h-3 w-3 mr-1" />
|
||||
Add
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{task.subtasks.length > 0 ? (
|
||||
<div className="space-y-1">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user