Skip to content
Merged
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 @@ -17,21 +17,23 @@ import {
DialogHeader,
DialogTitle,
} from "@superset/ui/dialog";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@superset/ui/dropdown-menu";
import { Input } from "@superset/ui/input";
import { Popover, PopoverContent, PopoverTrigger } from "@superset/ui/popover";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@superset/ui/select";
import { toast } from "@superset/ui/sonner";
import { useEffect, useMemo, useRef, useState } from "react";
import { GoGitBranch } from "react-icons/go";
import { HiCheck, HiChevronDown, HiChevronUpDown } from "react-icons/hi2";
import { LuFolderOpen } from "react-icons/lu";
import { electronTrpc } from "renderer/lib/electron-trpc";
import { formatRelativeTime } from "renderer/lib/formatRelativeTime";
import { useOpenNew } from "renderer/react-query/projects";
import { useCreateWorkspace } from "renderer/react-query/workspaces";
import {
useCloseNewWorkspaceModal,
Expand Down Expand Up @@ -83,6 +85,7 @@ export function NewWorkspaceModal() {
{ enabled: !!selectedProjectId },
);
const createWorkspace = useCreateWorkspace();
const openNew = useOpenNew();

// Filter branches based on search (for base branch selector)
const filteredBranches = useMemo(() => {
Expand Down Expand Up @@ -162,6 +165,31 @@ export function NewWorkspaceModal() {
setBranchNameEdited(true);
};

const handleImportRepo = async () => {
try {
const result = await openNew.mutateAsync(undefined);
if (result.canceled) return;
if ("error" in result) {
toast.error("Failed to open project", { description: result.error });
return;
}
if ("needsGitInit" in result) {
toast.error("Selected folder is not a git repository");
return;
}
setSelectedProjectId(result.project.id);
} catch (error) {
toast.error("Failed to open project", {
description:
error instanceof Error ? error.message : "An unknown error occurred",
});
}
};
Comment on lines +168 to +187
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Race condition exists: imported project won't appear in dropdown until getRecents query refetches.

After setSelectedProjectId(result.project.id), the selectedProject derivation (which looks up selectedProjectId in recentProjects) will return undefined until the getRecents query refetches. The mutation handler in apps/desktop/src/main/ipc/trpc/procedures/workspace/openNew.ts does not invalidate the getRecents query, and the component has no onSuccess handler to trigger a refetch.

This causes the button to display "Select project" immediately after import, even though selectedProjectId is set.

Recommend either:

  1. Invalidating the getRecents query in the mutation's onSuccess handler, or
  2. Displaying result.project.name as a fallback when selectedProject is undefined but selectedProjectId is set.
🤖 Prompt for AI Agents
In `@apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx`
around lines 168 - 187, The component's handleImportRepo
setsSelectedProjectId(result.project.id) but recentProjects (used by
selectedProject derivation) isn't updated yet, causing selectedProject to be
undefined; fix by either invalidating/refetching the getRecents query after a
successful import (add an onSuccess in the openNew mutation or call
trpc.getRecents.invalidate/refetch from the workspace/openNew.ts procedure) or,
simpler in the component, render a fallback using result.project.name when
selectedProject is undefined but selectedProjectId matches result.project.id;
update handleImportRepo and/or the openNew mutation onSuccess to ensure
getRecents is refreshed or show result.project.name as a temporary display until
recents refetches.


const selectedProject = recentProjects.find(
(p) => p.id === selectedProjectId,
);

const handleCreateWorkspace = async () => {
if (!selectedProjectId) return;

Expand Down Expand Up @@ -205,23 +233,44 @@ export function NewWorkspaceModal() {
</DialogHeader>

<div className="px-4 pb-3">
<Select
value={selectedProjectId ?? ""}
onValueChange={setSelectedProjectId}
>
<SelectTrigger className="w-full h-8 text-sm">
<SelectValue placeholder="Select project" />
</SelectTrigger>
<SelectContent>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
className="w-full h-8 text-sm justify-between font-normal"
>
<span
className={selectedProject ? "" : "text-muted-foreground"}
>
{selectedProject?.name ?? "Select project"}
</span>
<HiChevronDown className="size-4 text-muted-foreground" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="start"
className="w-[--radix-dropdown-menu-trigger-width]"
>
{recentProjects
.filter((project) => project.id)
.map((project) => (
<SelectItem key={project.id} value={project.id}>
<DropdownMenuItem
key={project.id}
onClick={() => setSelectedProjectId(project.id)}
>
{project.name}
</SelectItem>
{project.id === selectedProjectId && (
<HiCheck className="ml-auto size-4" />
)}
</DropdownMenuItem>
))}
</SelectContent>
</Select>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={handleImportRepo}>
<LuFolderOpen className="size-4" />
Import repo
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>

{selectedProjectId && (
Expand Down
Loading