feat: enhance GlobalTaskDetailDialog and TaskDetailDialog with loading state management

- Added a loading state to the TaskDetailDialog to display a loading indicator while fetching team data.
- Updated GlobalTaskDetailDialog to pass the loading state to TaskDetailDialog.
- Modified the selectTeam function in teamSlice to accept options for skipping project auto-selection, improving team data handling.
This commit is contained in:
iliya 2026-02-28 21:47:44 +02:00
parent b2e48c6966
commit ae2f7d9570
3 changed files with 27 additions and 4 deletions

View file

@ -69,7 +69,8 @@ export const GlobalTaskDetailDialog = (): React.JSX.Element | null => {
return (
<TaskDetailDialog
open
task={selectedTeamLoading ? null : task}
loading={selectedTeamLoading}
task={task}
teamName={teamName}
kanbanTaskState={kanbanTaskState}
taskMap={taskMap}

View file

@ -61,6 +61,7 @@ import type { KanbanTaskState, ResolvedTeamMember, TeamTaskWithKanban } from '@s
interface TaskDetailDialogProps {
open: boolean;
loading?: boolean;
task: TeamTaskWithKanban | null;
teamName: string;
kanbanTaskState?: KanbanTaskState;
@ -77,6 +78,7 @@ interface TaskDetailDialogProps {
export const TaskDetailDialog = ({
open,
loading = false,
task,
teamName,
kanbanTaskState,
@ -225,6 +227,22 @@ export const TaskDetailDialog = ({
onScrollToTask?.(taskId);
};
if (loading) {
return (
<Dialog open={open} onOpenChange={(v) => !v && onClose()}>
<DialogContent className="sm:max-w-4xl">
<DialogHeader>
<DialogTitle>Loading task</DialogTitle>
</DialogHeader>
<div className="flex items-center gap-2 text-sm text-[var(--color-text-muted)]">
<Loader2 className="size-4 animate-spin" />
<span>Fetching team data</span>
</div>
</DialogContent>
</Dialog>
);
}
if (!currentTask) {
return (
<Dialog open={open} onOpenChange={(v) => !v && handleClose()}>

View file

@ -126,7 +126,7 @@ export interface TeamSlice {
openTeamsTab: () => void;
openTeamTab: (teamName: string, projectPath?: string, taskId?: string) => void;
clearKanbanFilter: () => void;
selectTeam: (teamName: string) => Promise<void>;
selectTeam: (teamName: string, opts?: { skipProjectAutoSelect?: boolean }) => Promise<void>;
refreshTeamData: (teamName: string) => Promise<void>;
sendTeamMessage: (teamName: string, request: SendMessageRequest) => Promise<void>;
requestReview: (teamName: string, taskId: string) => Promise<void>;
@ -207,7 +207,7 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
// Ensure team data is loaded for the dialog
const state = get();
if (state.selectedTeamName !== teamName || !state.selectedTeamData) {
void state.selectTeam(teamName);
void state.selectTeam(teamName, { skipProjectAutoSelect: true });
}
},
closeGlobalTaskDetail: () => set({ globalTaskDetail: null }),
@ -358,7 +358,7 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
set({ kanbanFilterQuery: null });
},
selectTeam: async (teamName: string) => {
selectTeam: async (teamName: string, opts) => {
// Clear stale data immediately to prevent flash of previous team's content
const prev = get().selectedTeamName;
set({
@ -407,6 +407,10 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
get().updateTabLabel(teamTab.id, displayName);
}
if (opts?.skipProjectAutoSelect) {
return;
}
// Auto-select the project associated with this team's cwd/projectPath.
// Must search both flat projects and grouped repositoryGroups/worktrees
// because the default viewMode is 'grouped' and flat projects may be empty.