Skip to content

Improve command palette navigation #3174

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 25, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ BEGIN
RETURN true;
EXCEPTION
-- Only catch the specific exceptions we raise in validate_and_normalize_board_tags
WHEN SQLSTATE '22000' THEN -- our custom validation errors
WHEN raise_exception THEN -- Catches default RAISE EXCEPTION errors
RETURN false;
WHEN OTHERS THEN
-- Re-raise unexpected errors to avoid masking bugs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@
actions: boolean;
}

// Priority labels constant - defined once outside component for performance
const priorityLabels = {
1: 'High',
2: 'Medium',
3: 'Low',
};

Check warning on line 112 in apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/[boardId]/_components/list-view.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/[boardId]/_components/list-view.tsx#L106-L112

Added lines #L106 - L112 were not covered by tests
export function ListView({ board }: Props) {
const [tasks, setTasks] = useState<Task[]>(board.tasks);
const [isLoading, setIsLoading] = useState(false);
Expand Down Expand Up @@ -645,11 +652,6 @@
<CommandGroup>
{filterOptions.priorities.map((priority) => {
const isSelected = filters.priorities.has(priority);
const priorityLabels = {
1: 'High',
2: 'Medium',
3: 'Low',
};
return (
<CommandItem
key={priority}
Expand Down
178 changes: 145 additions & 33 deletions apps/web/src/components/command/add-task-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
} from '@tuturuuu/ui/select';
import { Separator } from '@tuturuuu/ui/separator';
import { cn } from '@tuturuuu/utils/format';
import { useRouter } from 'next/navigation';

Check warning on line 27 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L27

Added line #L27 was not covered by tests
import { useState } from 'react';

interface BoardWithLists {
Expand Down Expand Up @@ -53,9 +54,14 @@
const [showTasks, setShowTasks] = useState(false);
const { toast } = useToast();
const queryClient = useQueryClient();
const router = useRouter();

Check warning on line 57 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L57

Added line #L57 was not covered by tests

// Get boards with lists
const { data: boardsData, isLoading: boardsLoading } = useQuery<{
const {
data: boardsData,
isLoading: boardsLoading,
error: boardsError,
} = useQuery<{

Check warning on line 64 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L60-L64

Added lines #L60 - L64 were not covered by tests
boards: BoardWithLists[];
}>({
queryKey: ['boards-with-lists', wsId],
Expand All @@ -66,17 +72,23 @@
if (!response.ok) throw new Error('Failed to fetch boards');
return response.json();
},
retry: 2,
retryDelay: 1000,

Check warning on line 76 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L75-L76

Added lines #L75 - L76 were not covered by tests
});

const boards = boardsData?.boards;

// Get tasks for selected board/list
const { data: tasksData, isLoading: tasksLoading } = useQuery<{
const {
data: tasksData,
isLoading: tasksLoading,
error: tasksError,
} = useQuery<{

Check warning on line 86 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L82-L86

Added lines #L82 - L86 were not covered by tests
tasks: Task[];
}>({
queryKey: ['tasks', wsId, selectedBoardId, selectedListId],
queryFn: async () => {
if (!selectedBoardId && !selectedListId) return [];
if (!selectedBoardId && !selectedListId) return { tasks: [] };

Check warning on line 91 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L91

Added line #L91 was not covered by tests

const params = new URLSearchParams();
if (selectedListId) params.append('listId', selectedListId);
Expand All @@ -89,6 +101,8 @@
return response.json();
},
enabled: !!(selectedBoardId || selectedListId),
retry: 2,
retryDelay: 1000,

Check warning on line 105 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L104-L105

Added lines #L104 - L105 were not covered by tests
});

const tasks = tasksData?.tasks;
Expand Down Expand Up @@ -189,6 +203,34 @@
);
}

if (boardsError) {
return (
<div className="flex flex-col items-center gap-4 p-8 text-center">
<div className="rounded-full bg-dynamic-red/10 p-3">
<AlertTriangle className="h-6 w-6 text-dynamic-red" />
</div>
<div>
<p className="font-semibold text-foreground">Failed to load boards</p>
<p className="text-sm text-muted-foreground">
{boardsError.message || 'Unable to fetch boards at the moment'}
</p>
</div>
<div className="flex gap-2">
<Button
variant="outline"
size="sm"
onClick={() => window.location.reload()}
>

Check warning on line 223 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L206-L223

Added lines #L206 - L223 were not covered by tests
Retry
</Button>
<Button variant="ghost" size="sm" onClick={() => setOpen(false)}>

Check warning on line 226 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L225-L226

Added lines #L225 - L226 were not covered by tests
Close
</Button>
</div>
</div>

Check warning on line 230 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L228-L230

Added lines #L228 - L230 were not covered by tests
);
}

Check warning on line 232 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L232

Added line #L232 was not covered by tests

if (!boards || boards.length === 0) {
return (
<div className="flex flex-col items-center gap-4 p-8 text-center">
Expand All @@ -201,6 +243,17 @@
Create a board first to add tasks
</p>
</div>
<Button
variant="outline"
size="sm"
onClick={() => {
router.push(`/${wsId}/tasks/boards`);
setOpen(false);
}}

Check warning on line 252 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L246-L252

Added lines #L246 - L252 were not covered by tests
>
<Plus className="mr-2 h-4 w-4" />

Check warning on line 254 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L254

Added line #L254 was not covered by tests
Create Board
</Button>

Check warning on line 256 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L256

Added line #L256 was not covered by tests
</div>
);
}
Expand Down Expand Up @@ -246,27 +299,39 @@
{selectedBoardId && (
<div className="space-y-2">
<label className="text-sm font-medium text-foreground">List</label>
<Select
value={selectedListId}
onValueChange={(value) => {
setSelectedListId(value);
setShowTasks(true);
}}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="Select a list..." />
</SelectTrigger>
<SelectContent>
{availableLists.map((list: any) => (
<SelectItem key={list.id} value={list.id}>
<div className="flex items-center gap-2">
<List className="h-4 w-4 text-muted-foreground" />
<span className="truncate">{list.name}</span>
</div>
</SelectItem>
))}
</SelectContent>
</Select>
{availableLists.length === 0 ? (
<div className="rounded-md border border-dynamic-orange/20 bg-dynamic-orange/5 p-3 text-center">
<div className="flex items-center justify-center gap-2 text-sm text-dynamic-orange">
<AlertTriangle className="h-4 w-4" />
<span>This board has no lists</span>
</div>
<p className="mt-1 text-xs text-muted-foreground">

Check warning on line 308 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L302-L308

Added lines #L302 - L308 were not covered by tests
Create a list in the board first to add tasks
</p>
</div>

Check warning on line 311 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L310-L311

Added lines #L310 - L311 were not covered by tests
) : (
<Select
value={selectedListId}
onValueChange={(value) => {
setSelectedListId(value);
setShowTasks(true);
}}

Check warning on line 318 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L313-L318

Added lines #L313 - L318 were not covered by tests
>
<SelectTrigger className="w-full">
<SelectValue placeholder="Select a list..." />
</SelectTrigger>
<SelectContent>
{availableLists.map((list: any) => (
<SelectItem key={list.id} value={list.id}>
<div className="flex items-center gap-2">
<List className="h-4 w-4 text-muted-foreground" />
<span className="truncate">{list.name}</span>
</div>
</SelectItem>
))}
</SelectContent>
</Select>

Check warning on line 333 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L320-L333

Added lines #L320 - L333 were not covered by tests
)}
</div>
)}

Expand All @@ -280,12 +345,30 @@
{tasksLoading && (
<Loader className="h-3 w-3 animate-spin text-dynamic-blue" />
)}
{tasksError && (
<AlertTriangle className="h-3 w-3 text-dynamic-red" />

Check warning on line 349 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L348-L349

Added lines #L348 - L349 were not covered by tests
)}
</CardTitle>
</CardHeader>
<CardContent className="pt-0">
{tasksLoading ? (
<div className="flex items-center justify-center py-4">
<Loader className="h-4 w-4 animate-spin text-dynamic-blue" />
<div className="flex items-center gap-2">
<Loader className="h-4 w-4 animate-spin text-dynamic-blue" />
<span className="text-xs text-muted-foreground">

Check warning on line 358 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L356-L358

Added lines #L356 - L358 were not covered by tests
Loading tasks...
</span>
</div>
</div>
) : tasksError ? (
<div className="flex flex-col items-center gap-2 py-4 text-center">
<AlertTriangle className="h-4 w-4 text-dynamic-red" />
<div className="text-xs text-dynamic-red">

Check warning on line 366 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L360-L366

Added lines #L360 - L366 were not covered by tests
Failed to load tasks
</div>
<div className="text-xs text-muted-foreground">
{tasksError.message || 'Unable to fetch tasks'}
</div>

Check warning on line 371 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L368-L371

Added lines #L368 - L371 were not covered by tests
</div>
) : tasks && tasks.length > 0 ? (
<ScrollArea className="max-h-32">
Expand Down Expand Up @@ -330,6 +413,9 @@
<div className="text-xs text-muted-foreground">
No tasks in this list yet
</div>
<div className="mt-1 text-xs text-muted-foreground/70">

Check warning on line 416 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L416

Added line #L416 was not covered by tests
Perfect time to add the first one!
</div>

Check warning on line 418 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L418

Added line #L418 was not covered by tests
</div>
)}
</CardContent>
Expand All @@ -345,7 +431,8 @@
disabled={
!inputValue.trim() ||
!selectedListId ||
createTaskMutation.isPending
createTaskMutation.isPending ||
(Boolean(selectedBoardId) && availableLists.length === 0)

Check warning on line 435 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L434-L435

Added lines #L434 - L435 were not covered by tests
}
className="w-full"
>
Expand All @@ -363,13 +450,38 @@
</Button>
</div>

{/* Task name validation message */}
{!inputValue.trim() && (
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<AlertTriangle className="h-3 w-3" />
<span>Enter a task name to continue</span>
</div>
)}
{/* Validation messages */}
<div className="space-y-1">
{!inputValue.trim() && (
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<AlertTriangle className="h-3 w-3" />
<span>Enter a task name to continue</span>
</div>

Check warning on line 459 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L454-L459

Added lines #L454 - L459 were not covered by tests
)}
{inputValue.trim() && !selectedBoardId && (
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<AlertTriangle className="h-3 w-3" />
<span>Please select a board</span>
</div>

Check warning on line 465 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L461-L465

Added lines #L461 - L465 were not covered by tests
)}
{inputValue.trim() &&
selectedBoardId &&
!selectedListId &&
availableLists.length > 0 && (
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<AlertTriangle className="h-3 w-3" />
<span>Please select a list</span>
</div>

Check warning on line 474 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L467-L474

Added lines #L467 - L474 were not covered by tests
)}
{inputValue.trim() &&
selectedBoardId &&
availableLists.length === 0 && (
<div className="flex items-center gap-2 text-xs text-dynamic-orange">
<AlertTriangle className="h-3 w-3" />
<span>The selected board has no lists. Create a list first.</span>
</div>

Check warning on line 482 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L476-L482

Added lines #L476 - L482 were not covered by tests
)}
</div>

Check warning on line 484 in apps/web/src/components/command/add-task-form.tsx

View check run for this annotation

Codecov / codecov/patch

apps/web/src/components/command/add-task-form.tsx#L484

Added line #L484 was not covered by tests
</div>
);
}
Loading