Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,26 @@
import { useParams } from "next/navigation";

import { SprintBoard } from "@/features/sprints/components/sprint-board";
import { ProjectPermissionGuard } from "@/components/project-permission-guard";
import { ProjectPermissionKey } from "@/lib/permissions/types";

export const SprintClient = () => {
const params = useParams();
const workspaceId = params.workspaceId as string;
const projectId = params.projectId as string;

return <SprintBoard workspaceId={workspaceId} projectId={projectId} />;
return (
<ProjectPermissionGuard
permission={ProjectPermissionKey.VIEW_SPRINTS}
projectId={projectId}
workspaceId={workspaceId}
fallback={
<div className="flex items-center justify-center h-[50vh]">
<p className="text-muted-foreground text-sm">You don&apos;t have permission to view the Sprint Board.</p>
</div>
}
>
<SprintBoard workspaceId={workspaceId} projectId={projectId} />
</ProjectPermissionGuard>
);
};
9 changes: 5 additions & 4 deletions src/components/navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,9 @@ export const Navigation = ({
});

return (
<div className="p-3 border-sidebar-border flex-shrink-0">
<ul className="flex flex-col ">
<div className="px-3 pt-4 pb-2 flex-shrink-0">
<p className="text-[11px] font-semibold tracking-wider uppercase text-sidebar-foreground/50 pl-2.5 mb-2">Core Pages</p>
<ul className="flex flex-col gap-0.5">
{visibleRoutes.map((item) => {
// Determine the correct href based on route type
let fullHref: string;
Expand Down Expand Up @@ -219,11 +220,11 @@ export const Navigation = ({
<Link href={fullHref}>
<div
className={cn(
"flex items-center gap-2.5 p-2.5 rounded-md font-medium hover:bg-sidebar-accent hover:text-sidebar-foreground transition text-sidebar-foreground/70",
"flex items-center gap-2.5 px-2.5 py-2 rounded-md font-medium hover:bg-sidebar-accent hover:text-sidebar-foreground transition text-sidebar-foreground/70",
isActive && "bg-sidebar-accent shadow-sm hover:opacity-100 text-sidebar-foreground"
)}
>
<Icon className={cn("size-5 ", isActive && "text-primary")} />
<Icon className={cn("size-[17px]", isActive && "text-primary")} />
<p className="text-[12px] tracking-tight font-medium">
{item.label}
</p>
Expand Down
31 changes: 12 additions & 19 deletions src/components/project-tools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -265,25 +265,18 @@ export const ProjectTools = () => {
};

return (
<div className="flex flex-col px-3 py-2 border-t border-sidebar-border">
<div className="flex flex-col px-3 py-3 border-t border-sidebar-border">
{/* Project Header with Toggle */}
<div className="flex items-center mt-2 justify-between">
<div className="flex items-center justify-between">
<button
onClick={() => setIsExpanded(!isExpanded)}
className="flex items-center gap-2 text-[13px] tracking-normal font-medium pl-2 text-sidebar-foreground/90 hover:text-sidebar-foreground w-full"
className="flex items-center gap-1.5 text-[11px] tracking-wider uppercase font-semibold pl-2.5 text-sidebar-foreground/50 hover:text-sidebar-foreground/70 transition-colors w-full"
>
<span className="flex items-center gap-1">
{isExpanded ? (
<ChevronDown className="size-3" />
) : (
<ChevronRight className="size-3" />
)}
</span>
{/* <ProjectAvatar
name={project.name}
image={project.imageUrl}
className="size-5"
/> */}
{isExpanded ? (
<ChevronDown className="size-3" />
) : (
<ChevronRight className="size-3" />
)}
<span className="truncate max-w-[170px]" title={project.name}>
{project.name} Project Tools
</span>
Expand All @@ -297,23 +290,23 @@ export const ProjectTools = () => {
isExpanded ? "max-h-[500px] opacity-100" : "max-h-0 opacity-0"
)}
>
<div className="space-y-0.5 mt-2 pl-4 pt-1">
<div className="space-y-0.5 mt-2 pl-3">
{visibleTools.map((tool) => {
const isActive = isToolActive(tool);
return (
<button
key={tool.id}
onClick={() => handleToolClick(tool)}
className={cn(
"flex items-center gap-2 px-2 py-1.5 rounded-md cursor-pointer text-sm transition-colors w-full",
"flex items-center gap-2.5 px-2.5 py-2 rounded-md cursor-pointer transition-colors w-full",
isActive
? "bg-sidebar-accent text-sidebar-foreground font-medium"
: "hover:bg-sidebar-accent/50 text-sidebar-foreground/70"
)}
>
<span className={cn(
"transition-colors",
isActive && "text-primary"
"flex-shrink-0 transition-colors",
isActive ? "text-primary" : "text-sidebar-foreground/60"
)}>
{tool.icon}
</span>
Expand Down
6 changes: 3 additions & 3 deletions src/components/projects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ export const Projects = () => {
};

return (
<div className="flex flex-col px-3 py-2 border-t border-sidebar-border">
<div className="flex items-center justify-between ">
<div className="flex flex-col px-3 py-3 border-t border-sidebar-border">
<div className="flex items-center justify-between">
<button
onClick={() => setIsExpanded(!isExpanded)}
className="flex items-center gap-1 text-[13px] tracking-normal font-medium pl-2 text-sidebar-foreground/80 hover:text-sidebar-foreground"
className="flex items-center gap-1.5 text-[11px] tracking-wider uppercase font-semibold pl-2.5 text-sidebar-foreground/50 hover:text-sidebar-foreground/70 transition-colors"
>
{isExpanded ? (
<ChevronDown className="size-3" />
Expand Down
6 changes: 3 additions & 3 deletions src/components/spaces.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ export const Spaces = () => {
}, {} as Record<string, typeof spaces>);

return (
<div className="flex flex-col px-3 py-4 border-t border-sidebar-border">
<div className="flex items-center justify-between ">
<div className="flex flex-col px-3 py-3 border-t border-sidebar-border">
<div className="flex items-center justify-between">
<button
onClick={() => setIsExpanded(!isExpanded)}
className="flex items-center gap-1 text-[13px] tracking-normal font-medium pl-2 text-sidebar-foreground/90 hover:text-sidebar-foreground"
className="flex items-center gap-1.5 text-[11px] tracking-wider uppercase font-semibold pl-2.5 text-sidebar-foreground/50 hover:text-sidebar-foreground/70 transition-colors"
>
{isExpanded ? (
<ChevronDown className="size-3" />
Expand Down
14 changes: 7 additions & 7 deletions src/components/tools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,11 @@ export const Tools = () => {
};

return (
<div className="flex flex-col px-3 py-4 border-t border-sidebar-border">
<div className="flex flex-col px-3 py-3 border-t border-sidebar-border">
<div className="flex items-center justify-between">
<button
onClick={() => setIsExpanded(!isExpanded)}
className="flex items-center gap-1 text-[13px] tracking-normal font-medium pl-2 text-sidebar-foreground/90 hover:text-sidebar-foreground"
className="flex items-center gap-1.5 text-[11px] tracking-wider uppercase font-semibold pl-2.5 text-sidebar-foreground/50 hover:text-sidebar-foreground/70 transition-colors"
>
{isExpanded ? (
<ChevronDown className="size-3" />
Expand All @@ -142,22 +142,22 @@ export const Tools = () => {
</button>
</div>

<div className={`transition-all duration-1000 overflow-hidden ${isExpanded ? 'max-h-96' : 'max-h-0'}`}>
<div className="space-y-1 mt-2 pl-4 pt-2">
<div className={`transition-all duration-300 overflow-hidden ${isExpanded ? 'max-h-96' : 'max-h-0'}`}>
<div className="space-y-0.5 mt-2 pl-3">
{visibleTools.map((tool) => {
const isActive = isToolActive(tool);
return (
<button
key={tool.label}
onClick={() => handleToolClick(tool)}
className={cn(
"flex items-center gap-2 px-2 py-1.5 rounded-md cursor-pointer text-sm transition-colors w-full",
"flex items-center gap-2.5 px-2.5 py-2 rounded-md cursor-pointer transition-colors w-full",
isActive
? "bg-sidebar-accent text-sidebar-foreground"
: "hover:bg-sidebar-accent text-sidebar-foreground/70"
: "hover:bg-sidebar-accent/50 text-sidebar-foreground/70"
)}
>
<span className={cn(isActive ? "text-primary" : "text-sidebar-foreground/70")}>
<span className={cn("flex-shrink-0", isActive ? "text-primary" : "text-sidebar-foreground/60")}>
{tool.icon}
</span>
<span className="truncate text-xs font-medium">{tool.label}</span>
Expand Down
10 changes: 5 additions & 5 deletions src/components/ui/badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ const badgeVariants = cva(
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
outline: "text-foreground",
[TaskStatus.TODO]:
"border-transparent bg-gray-400 text-primary hover:bg-gray-400/80",
"border-transparent bg-gray-400 text-gray-900 dark:bg-gray-500 dark:text-gray-50 hover:bg-gray-400/80 dark:hover:bg-gray-500/80",
[TaskStatus.ASSIGNED]:
"border-transparent bg-red-400 text-primary hover:bg-red-400/80",
"border-transparent bg-red-400 text-red-900 dark:bg-red-500 dark:text-red-50 hover:bg-red-400/80 dark:hover:bg-red-500/80",
[TaskStatus.IN_PROGRESS]:
"border-transparent bg-yellow-400 text-primary hover:bg-yellow-400/80",
"border-transparent bg-yellow-400 text-yellow-900 dark:bg-yellow-500 dark:text-yellow-50 hover:bg-yellow-400/80 dark:hover:bg-yellow-500/80",
[TaskStatus.IN_REVIEW]:
"border-transparent bg-blue-400 text-primary hover:bg-blue-400/80",
"border-transparent bg-blue-400 text-blue-900 dark:bg-blue-500 dark:text-blue-50 hover:bg-blue-400/80 dark:hover:bg-blue-500/80",
[TaskStatus.DONE]:
"border-transparent bg-emerald-400 text-primary hover:bg-emerald-400/80",
"border-transparent bg-emerald-400 text-emerald-900 dark:bg-emerald-500 dark:text-emerald-50 hover:bg-emerald-400/80 dark:hover:bg-emerald-500/80",
},
},
defaultVariants: {
Expand Down
7 changes: 5 additions & 2 deletions src/features/attachments/components/attachment-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ const AttachmentItem = ({ attachment, workspaceId }: AttachmentItemProps) => {
<Card className="hover:bg-gray-50 transition-colors">
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-3 flex-1 min-w-0">
<button
onClick={isPreviewable ? handlePreview : handleDownload}
className="flex items-center space-x-3 flex-1 min-w-0 text-left cursor-pointer hover:opacity-80 transition-opacity"
>
<div className="flex-shrink-0">
{getFileIcon(attachment.mimeType)}
</div>
Expand All @@ -101,7 +104,7 @@ const AttachmentItem = ({ attachment, workspaceId }: AttachmentItemProps) => {
</Badge>
</div>
</div>
</div>
</button>
<div className="flex items-center space-x-2">
{isPreviewable && (
<Button
Expand Down
27 changes: 16 additions & 11 deletions src/features/attachments/components/task-attachments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,17 +121,22 @@ const AttachmentItem = ({ attachment, workspaceId, onPreview }: AttachmentItemPr
<>
<ConfirmDialog />
<div className="group flex items-center gap-2 px-2 py-1.5 hover:bg-accent rounded-md transition-colors">
<div className="flex-shrink-0">
{getFileIcon(attachment.mimeType)}
</div>
<div className="flex-1 min-w-0">
<p className="text-[13px] font-normal text-foreground truncate leading-tight">
{attachment.name}
</p>
<p className="text-[11px] text-muted-foreground leading-tight">
{formatFileSize(attachment.size)} · {getFileExtension(attachment.name)}
</p>
</div>
<button
onClick={isPreviewable ? handlePreview : handleDownload}
className="flex items-center gap-2 flex-1 min-w-0 text-left cursor-pointer hover:opacity-80 transition-opacity"
>
<div className="flex-shrink-0">
{getFileIcon(attachment.mimeType)}
</div>
<div className="flex-1 min-w-0">
<p className="text-[13px] font-normal text-foreground truncate leading-tight">
{attachment.name}
</p>
<p className="text-[11px] text-muted-foreground leading-tight">
{formatFileSize(attachment.size)} · {getFileExtension(attachment.name)}
</p>
</div>
</button>
<div className="flex items-center opactiy-100">
<DropdownMenu>
<DropdownMenuTrigger asChild>
Expand Down
24 changes: 15 additions & 9 deletions src/features/custom-columns/components/enhanced-data-kanban.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -356,11 +356,8 @@ export const EnhancedDataKanban = ({
const sourceColumnId = source.droppableId;
const destColumnId = destination.droppableId;

// If dropped in the same position, do nothing
if (
sourceColumnId === destColumnId &&
source.index === destination.index
) {
// Early return if no actual movement
if (sourceColumnId === destColumnId && source.index === destination.index) {
return;
}

Expand All @@ -370,10 +367,13 @@ export const EnhancedDataKanban = ({
position: number;
}[] = [];

// Store previous state for potential rollback
const previousTasks = tasks;

setTasks((prevTasks) => {
const newTasks = { ...prevTasks };

const sourceColumn = [...newTasks[sourceColumnId]];
const sourceColumn = [...(newTasks[sourceColumnId] || [])];
const [movedTask] = sourceColumn.splice(source.index, 1);

if (!movedTask) {
Expand All @@ -387,7 +387,7 @@ export const EnhancedDataKanban = ({

newTasks[sourceColumnId] = sourceColumn;

const destColumn = [...newTasks[destColumnId]];
const destColumn = [...(newTasks[destColumnId] || [])];
destColumn.splice(destination.index, 0, updatedMovedTask);
newTasks[destColumnId] = destColumn;

Expand Down Expand Up @@ -432,10 +432,16 @@ export const EnhancedDataKanban = ({

// Only call onChange if we have valid updates
if (Array.isArray(updatesPayload) && updatesPayload.length > 0) {
onChange(updatesPayload);
try {
onChange(updatesPayload);
} catch (error) {
// Rollback on error
console.error('Failed to update task positions:', error);
setTasks(previousTasks);
}
}
},
[orderedColumns, onChange, updateColumnOrder, workspaceId, projectId]
[orderedColumns, onChange, updateColumnOrder, workspaceId, projectId, tasks]
);

// Derive body content states (keep hooks above regardless of state)
Expand Down
50 changes: 34 additions & 16 deletions src/features/sprints/components/enhanced-backlog-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,7 @@ export default function EnhancedBacklogScreen({ workspaceId, projectId }: Enhanc
) : (
<div className="divide-y divide-border">
{/* Column Header Row */}
<div className="px-4 py-2 bg-muted/50 border-b border-border">
{/* <div className="px-4 py-2 bg-muted/50 border-b border-border">
<div className="flex items-center gap-4">
<div className="flex items-center gap-3">
<div className="w-5" />
Expand All @@ -889,7 +889,7 @@ export default function EnhancedBacklogScreen({ workspaceId, projectId }: Enhanc
<span className="text-xs font-semibold text-muted-foreground uppercase tracking-wider w-[120px]">Assignee</span>
</div>
</div>
</div>
</div> */}
{sprintItems.map((item, index) => (
<Draggable key={item.$id} draggableId={item.$id} index={index}>
{(provided, snapshot) => (
Expand Down Expand Up @@ -1191,13 +1191,22 @@ export default function EnhancedBacklogScreen({ workspaceId, projectId }: Enhanc
</div>
) : (
canCreateWorkItems && (
<button
onClick={() => setIsCreatingInSprint(sprint.$id)}
className="w-full px-4 py-3 text-left text-sm text-muted-foreground hover:bg-accent transition-colors flex items-center gap-2 border-t border-border"
>
<Plus className="size-4" />
Create work item
</button>
<div className="flex items-center gap-0 border-t border-border">
<button
onClick={() => setIsCreatingInSprint(sprint.$id)}
className="w-1/2 px-4 py-3 text-left text-sm text-muted-foreground hover:bg-accent transition-colors flex items-center gap-2 border-r border-border"
>
<Plus className="size-4" />
Create work item
</button>
<button
onClick={openCreateTaskModal}
className="w-1/2 px-4 py-3 text-left text-sm text-muted-foreground hover:bg-accent transition-colors flex items-center gap-2"
>
<Plus className="size-4" />
Create full work item
</button>
</div>
)
)}

Expand Down Expand Up @@ -1590,13 +1599,22 @@ export default function EnhancedBacklogScreen({ workspaceId, projectId }: Enhanc
</div>
) : (
canCreateWorkItems && (
<button
onClick={() => setIsCreatingInBacklog(true)}
className="w-full px-4 py-3 text-left text-sm text-muted-foreground hover:bg-muted transition-colors flex items-center gap-2 border-t border-border"
>
<Plus className="size-4" />
Create work item
</button>
<div className="flex items-center gap-0 border-t border-border">
<button
onClick={() => setIsCreatingInBacklog(true)}
className="w-1/2 px-4 py-3 text-left text-sm text-muted-foreground hover:bg-muted transition-colors flex items-center gap-2 border-r border-border"
>
<Plus className="size-4" />
Create work item
</button>
<button
onClick={openCreateTaskModal}
className="w-1/2 px-4 py-3 text-left text-sm text-muted-foreground hover:bg-muted transition-colors flex items-center gap-2"
>
<Plus className="size-4" />
Create full work item
</button>
</div>
)
)}

Expand Down
Loading
Loading