164 lines
5.4 KiB
TypeScript
164 lines
5.4 KiB
TypeScript
"use client";
|
|
|
|
import { DialogTitle } from "@kandev/ui/dialog";
|
|
import { RepositorySelector, InlineTaskName } from "@/components/task-create-dialog-selectors";
|
|
import type { useRepositoryOptions } from "@/components/task-create-dialog-options";
|
|
|
|
function getRepositoryPlaceholder(
|
|
workspaceId: string | null,
|
|
repositoriesLoading: boolean,
|
|
discoverReposLoading: boolean,
|
|
) {
|
|
if (!workspaceId) return "Select workspace first";
|
|
if (repositoriesLoading || discoverReposLoading) return "Loading...";
|
|
return "Select repository";
|
|
}
|
|
|
|
export type DialogHeaderContentProps = {
|
|
isCreateMode: boolean;
|
|
isEditMode: boolean;
|
|
isTaskStarted: boolean;
|
|
sessionRepoName?: string;
|
|
initialTitle?: string;
|
|
taskName: string;
|
|
repositoryId: string;
|
|
discoveredRepoPath: string;
|
|
workspaceId: string | null;
|
|
repositoriesLoading: boolean;
|
|
discoverReposLoading: boolean;
|
|
headerRepositoryOptions: ReturnType<typeof useRepositoryOptions>["headerRepositoryOptions"];
|
|
onRepositoryChange: (v: string) => void;
|
|
onTaskNameChange: (v: string) => void;
|
|
useGitHubUrl: boolean;
|
|
githubUrl: string;
|
|
githubUrlError: string | null;
|
|
onToggleGitHubUrl: () => void;
|
|
onGitHubUrlChange: (v: string) => void;
|
|
};
|
|
|
|
function RepoSourceInput({
|
|
useGitHubUrl,
|
|
githubUrl,
|
|
githubUrlError,
|
|
onGitHubUrlChange,
|
|
isTaskStarted,
|
|
headerRepositoryOptions,
|
|
repositoryId,
|
|
discoveredRepoPath,
|
|
onRepositoryChange,
|
|
workspaceId,
|
|
repositoriesLoading,
|
|
discoverReposLoading,
|
|
}: Pick<
|
|
DialogHeaderContentProps,
|
|
| "useGitHubUrl"
|
|
| "githubUrl"
|
|
| "githubUrlError"
|
|
| "onGitHubUrlChange"
|
|
| "isTaskStarted"
|
|
| "headerRepositoryOptions"
|
|
| "repositoryId"
|
|
| "discoveredRepoPath"
|
|
| "onRepositoryChange"
|
|
| "workspaceId"
|
|
| "repositoriesLoading"
|
|
| "discoverReposLoading"
|
|
>): React.ReactNode {
|
|
if (useGitHubUrl) {
|
|
return (
|
|
<div className="relative">
|
|
<input
|
|
type="text"
|
|
value={githubUrl}
|
|
onChange={(e) => onGitHubUrlChange(e.target.value)}
|
|
placeholder="github.com/owner/repo"
|
|
data-testid="github-url-input"
|
|
aria-label="GitHub repository URL"
|
|
aria-invalid={!!githubUrlError}
|
|
aria-describedby={githubUrlError ? "github-url-error" : undefined}
|
|
size={Math.max((githubUrl || "").length + 1, "github.com/owner/repo".length)}
|
|
className={`bg-transparent border-none outline-none focus:ring-0 text-sm font-medium min-w-0 h-7 rounded-md px-2 hover:bg-muted focus:bg-muted transition-colors placeholder:text-muted-foreground ${githubUrlError ? "text-destructive" : ""}`}
|
|
disabled={isTaskStarted}
|
|
autoFocus
|
|
/>
|
|
{githubUrlError && (
|
|
<div
|
|
id="github-url-error"
|
|
role="alert"
|
|
className="absolute left-0 top-full mt-1 z-50 rounded-md border bg-popover px-2 py-1 text-[11px] text-destructive shadow-md whitespace-nowrap"
|
|
data-testid="github-url-error"
|
|
>
|
|
{githubUrlError}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
return (
|
|
<RepositorySelector
|
|
options={headerRepositoryOptions}
|
|
value={repositoryId || discoveredRepoPath}
|
|
onValueChange={onRepositoryChange}
|
|
placeholder={getRepositoryPlaceholder(workspaceId, repositoriesLoading, discoverReposLoading)}
|
|
searchPlaceholder="Search repositories..."
|
|
emptyMessage={
|
|
repositoriesLoading || discoverReposLoading
|
|
? "Loading repositories..."
|
|
: "No repositories found."
|
|
}
|
|
disabled={isTaskStarted || !workspaceId || repositoriesLoading || discoverReposLoading}
|
|
triggerClassName="w-auto text-sm"
|
|
/>
|
|
);
|
|
}
|
|
|
|
export function DialogHeaderContent(props: DialogHeaderContentProps) {
|
|
const { isCreateMode, isEditMode, isTaskStarted, sessionRepoName, initialTitle } = props;
|
|
const { taskName, onTaskNameChange, useGitHubUrl, onToggleGitHubUrl } = props;
|
|
|
|
if (isCreateMode || isEditMode) {
|
|
return (
|
|
<>
|
|
<DialogTitle asChild>
|
|
<div className="flex items-center gap-1 text-sm font-medium min-w-0">
|
|
<RepoSourceInput {...props} />
|
|
<span className="text-muted-foreground mr-2">/</span>
|
|
<InlineTaskName
|
|
value={taskName}
|
|
onChange={onTaskNameChange}
|
|
autoFocus={!isEditMode && !useGitHubUrl}
|
|
/>
|
|
</div>
|
|
</DialogTitle>
|
|
{!isTaskStarted && (
|
|
<div className="flex items-center gap-2 pl-2">
|
|
<button
|
|
type="button"
|
|
onClick={onToggleGitHubUrl}
|
|
className="text-xs text-muted-foreground hover:text-foreground cursor-pointer transition-colors"
|
|
data-testid="toggle-github-url"
|
|
>
|
|
{useGitHubUrl ? "or select a repository" : "or paste a GitHub URL"}
|
|
</button>
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
return (
|
|
<DialogTitle asChild>
|
|
<div className="flex items-center gap-1 min-w-0 text-sm font-medium">
|
|
{sessionRepoName && (
|
|
<>
|
|
<span className="truncate text-muted-foreground">{sessionRepoName}</span>
|
|
<span className="text-muted-foreground mx-0.5">/</span>
|
|
</>
|
|
)}
|
|
<span className="truncate">{initialTitle || "Task"}</span>
|
|
<span className="text-muted-foreground mx-0.5">/</span>
|
|
<span className="text-muted-foreground whitespace-nowrap">new session</span>
|
|
</div>
|
|
</DialogTitle>
|
|
);
|
|
}
|