Files
kandev/apps/web/components/task-create-dialog-prop-builders.ts
T
2026-08-18 22:30:38 +01:00

124 lines
5.5 KiB
TypeScript

/**
* Pure prop-assembly helpers for the task-create dialog. Extracted from
* task-create-dialog.tsx so the orchestrator stays under the per-file line
* cap — these are non-React, no-JSX projections from the setup hook's
* result + dialog props, so they belong outside the component module.
*/
// Both names are used only in type positions (interface + `typeof` in
// ReturnType<>), so the `import type` form makes the otherwise-circular
// dependency with task-create-dialog.tsx explicitly type-only — bundlers
// and analysis tools won't treat it as a real runtime cycle.
import type { TaskCreateDialogProps } from "@/components/task-create-dialog";
import type { useTaskCreateDialogSetup } from "@/components/task-create-dialog-setup";
import type { DialogFormBodyProps, DialogFormState } from "@/components/task-create-dialog-types";
export function computeHasAllBranches(fs: DialogFormState): boolean {
if (fs.noRepository) return true;
if (fs.useRemote) {
const rows = fs.remoteRepos.filter((r) => r.url.trim() !== "");
return rows.length > 0 && rows.every((r) => !!r.branch);
}
return fs.repositories.length > 0 && fs.repositories.every((r) => !!r.branch);
}
export function localRepositoryCreationEnabled(isCreateMode: boolean, repoLocked: boolean) {
return isCreateMode && !repoLocked;
}
export function buildDialogFormBodyProps(
setup: ReturnType<typeof useTaskCreateDialogSetup>,
props: TaskCreateDialogProps,
): DialogFormBodyProps {
const { fs, computed, handlers } = setup;
const repoLocked = !!props.lockedFields?.repository;
return {
isSessionMode: setup.isSessionMode,
isCreateMode: setup.isCreateMode,
isEditMode: setup.isEditMode,
autoTitle: setup.autoTitle,
isTaskStarted: setup.isTaskStarted,
onTaskNameChange: handlers.handleTaskNameChange,
onRowRepositoryChange: handlers.handleRowRepositoryChange,
onRowBranchChange: handlers.handleRowBranchChange,
initialDescription: fs.currentDefaults.description,
workspaceId: props.workspaceId,
onJiraImport: setup.handleJiraImport,
onLinearImport: setup.handleLinearImport,
agentProfileOptions: computed.agentProfileOptions,
executorProfileOptions: computed.executorProfileOptions,
agentProfiles: setup.agentProfiles,
agentProfilesLoading: computed.agentProfilesLoading,
executorsLoading: computed.executorsLoading,
isCreatingSession: fs.isCreatingSession,
workflows: setup.workflows,
snapshots: setup.snapshots,
effectiveWorkflowId: computed.effectiveWorkflowId ?? null,
fs,
handleKeyDown: setup.handleKeyDown,
onAgentProfileChange: handlers.handleAgentProfileChange,
onExecutorProfileChange: handlers.handleExecutorProfileChange,
onWorkflowChange: handlers.handleWorkflowChange,
onToggleRemote: repoLocked ? undefined : handlers.handleToggleRemote,
onToggleFreshBranch: handlers.handleToggleFreshBranch,
onToggleNoRepository: repoLocked ? undefined : handlers.handleToggleNoRepository,
onWorkspacePathChange: handlers.handleWorkspacePathChange,
localRepositoryCreation: localRepositoryCreationEnabled(setup.isCreateMode, repoLocked)
? {
executorSelection: handlers.directLocalExecutorSelection,
onCreated: handlers.handleLocalRepositoryCreated,
}
: undefined,
enhance: setup.enhance,
workflowAgentLocked: computed.workflowAgentLocked,
repositories: setup.repositories,
onRefreshRepositories: setup.refreshRepositories,
repositoriesRefreshing: setup.repositoriesLoading,
lastUsedBranch: setup.taskCreateLastUsed.branch,
userSettingsLoaded: setup.userSettingsLoaded,
freshBranchAvailable: setup.freshBranchAvailable,
// The same lock disables the source-mode and local-repository controls, and
// applying a set writes fs.repositories just as they would.
repositorySets: repoLocked ? undefined : setup.repositorySets,
isLocalExecutor: computed.isLocalExecutor,
noCompatibleAgent: computed.noCompatibleAgent,
executorProfileName: computed.selectedExecutorProfileName,
extraFormSlot: props.extraFormSlot,
aboveDescriptionSlot: props.aboveDescriptionSlot,
bottomSlot: props.bottomSlot,
descriptionPlaceholder: props.descriptionPlaceholder,
workflowLocked: props.lockedFields?.workflow,
};
}
export function buildDialogFooterProps(
setup: ReturnType<typeof useTaskCreateDialogSetup>,
props: TaskCreateDialogProps,
pendingAttachmentUploadReason?: string | null,
) {
const { fs, computed, submitHandlers } = setup;
return {
isSessionMode: setup.isSessionMode,
isCreateMode: setup.isCreateMode,
isEditMode: setup.isEditMode,
autoTitle: setup.autoTitle,
isTaskStarted: setup.isTaskStarted,
isCreatingSession: fs.isCreatingSession,
isCreatingTask: fs.isCreatingTask,
hasTitle: fs.hasTitle,
hasDescription: fs.hasDescription,
hasRepositorySelection: computed.hasRepositorySelection,
hasAllBranches: computeHasAllBranches(fs),
agentProfileId: computed.effectiveAgentProfileId,
workspaceId: props.workspaceId,
effectiveWorkflowId: computed.effectiveWorkflowId ?? null,
executorHint: computed.executorHint,
noCompatibleAgent: computed.noCompatibleAgent,
executorProfileName: computed.selectedExecutorProfileName,
onCancel: submitHandlers.handleCancel,
onUpdateWithoutAgent: submitHandlers.handleUpdateWithoutAgent,
onCreateWithoutAgent: submitHandlers.handleCreateWithoutAgent,
onCreateWithPlanMode: submitHandlers.handleCreateWithPlanMode,
submitBlockedReason: props.submitBlockedReason ?? pendingAttachmentUploadReason,
};
}