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
179 changes: 146 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,15 @@
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,
refetch: refetchBoards,
} = useQuery<{

Check warning on line 65 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-L65

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

Check warning on line 77 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#L76-L77

Added lines #L76 - L77 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 87 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#L83-L87

Added lines #L83 - L87 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 92 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#L92

Added line #L92 was not covered by tests

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

Check warning on line 106 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#L105-L106

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

const tasks = tasksData?.tasks;
Expand Down Expand Up @@ -189,6 +204,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={() => refetchBoards()}
>

Check warning on line 224 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#L207-L224

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

Check warning on line 227 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#L226-L227

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

Check warning on line 231 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#L229-L231

Added lines #L229 - L231 were not covered by tests
);
}

Check warning on line 233 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#L233

Added line #L233 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 +244,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 253 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#L247-L253

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

Check warning on line 255 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#L255

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

Check warning on line 257 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#L257

Added line #L257 was not covered by tests
</div>
);
}
Expand Down Expand Up @@ -246,27 +300,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 309 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#L303-L309

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

Check warning on line 312 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#L311-L312

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

Check warning on line 319 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#L314-L319

Added lines #L314 - L319 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 334 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#L321-L334

Added lines #L321 - L334 were not covered by tests
)}
</div>
)}

Expand All @@ -280,12 +346,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 350 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#L349-L350

Added lines #L349 - L350 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 359 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#L357-L359

Added lines #L357 - L359 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 367 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#L361-L367

Added lines #L361 - L367 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 372 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#L369-L372

Added lines #L369 - L372 were not covered by tests
</div>
) : tasks && tasks.length > 0 ? (
<ScrollArea className="max-h-32">
Expand Down Expand Up @@ -330,6 +414,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 417 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#L417

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

Check warning on line 419 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#L419

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

Check warning on line 436 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#L435-L436

Added lines #L435 - L436 were not covered by tests
}
className="w-full"
>
Expand All @@ -363,13 +451,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 460 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#L455-L460

Added lines #L455 - L460 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 466 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#L462-L466

Added lines #L462 - L466 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 475 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#L468-L475

Added lines #L468 - L475 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 483 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#L477-L483

Added lines #L477 - L483 were not covered by tests
)}
</div>

Check warning on line 485 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#L485

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