"use client"; import { useState, useEffect, useCallback, useMemo } from "react"; import { Button } from "@kandev/ui/button"; import { Switch } from "@kandev/ui/switch"; import { Label } from "@kandev/ui/label"; import { Input } from "@kandev/ui/input"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, } from "@kandev/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@kandev/ui/select"; import { Textarea } from "@kandev/ui/textarea"; import { IconInfoCircle } from "@tabler/icons-react"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@kandev/ui/tooltip"; import { CliModeIcon } from "@/components/cli-mode-icon"; import { useAppStore } from "@/components/state-provider"; import { useSettingsData } from "@/hooks/domains/settings/use-settings-data"; import { useWorkflows } from "@/hooks/use-workflows"; import { useWorkflowSteps, stepPlaceholder } from "@/hooks/use-workflow-steps"; import { ScriptEditor, computeEditorHeight, } from "@/components/settings/profile-edit/script-editor"; import { ISSUE_WATCH_PLACEHOLDERS, DEFAULT_ISSUE_WATCH_PROMPT, } from "@/components/github/issue-watch-placeholders"; import { RepoFilterSelector } from "@/components/github/repo-filter-selector"; import { STEP_DEFAULT, STEP_DEFAULT_LABEL, resolveProfileId } from "@/lib/watcher-profile-default"; import type { RepoFilter, IssueWatch, CreateIssueWatchRequest, UpdateIssueWatchRequest, CleanupPolicy, } from "@/lib/types/github"; type IssueWatchDialogProps = { open: boolean; onOpenChange: (open: boolean) => void; watch: IssueWatch | null; // Pre-binds the dialog to one workspace. Omit on the install-wide settings // page so the create flow shows a workspace picker. workspaceId?: string; onCreate: (req: CreateIssueWatchRequest) => Promise; onUpdate: (id: string, req: UpdateIssueWatchRequest) => Promise; }; type FormState = { workspaceId: string; selectedRepos: RepoFilter[]; allRepos: boolean; workflowId: string; workflowStepId: string; agentProfileId: string; executorProfileId: string; prompt: string; labels: string; customQuery: string; enabled: boolean; pollInterval: number; cleanupPolicy: CleanupPolicy; }; const DEFAULT_QUERY = "type:issue state:open"; const CLEANUP_POLICY_OPTIONS: Array<{ id: CleanupPolicy; label: string; description: string }> = [ { id: "auto", label: "Auto (recommended)", description: "Delete closed-issue tasks unless you typed a message in them.", }, { id: "always", label: "Always delete", description: "Delete on close even if you engaged with the task.", }, { id: "never", label: "Never auto-delete", description: "Keep all tasks. Delete them manually from the task list.", }, ]; function makeDefaultForm(workspaceId: string): FormState { return { workspaceId, selectedRepos: [], allRepos: false, workflowId: "", workflowStepId: "", agentProfileId: "", executorProfileId: "", prompt: DEFAULT_ISSUE_WATCH_PROMPT, labels: "", customQuery: DEFAULT_QUERY, enabled: true, pollInterval: 300, cleanupPolicy: "auto", }; } function formStateFromWatch(watch: IssueWatch): FormState { const hasRepos = watch.repos && watch.repos.length > 0; return { workspaceId: watch.workspace_id, selectedRepos: hasRepos ? watch.repos : [], allRepos: !hasRepos, workflowId: watch.workflow_id, workflowStepId: watch.workflow_step_id, agentProfileId: watch.agent_profile_id, executorProfileId: watch.executor_profile_id, prompt: watch.prompt || DEFAULT_ISSUE_WATCH_PROMPT, labels: (watch.labels ?? []).join(", "), customQuery: watch.custom_query || DEFAULT_QUERY, enabled: watch.enabled, pollInterval: watch.poll_interval_seconds, cleanupPolicy: watch.cleanup_policy ?? "auto", }; } function useWatchFormData(workspaceId: string) { useSettingsData(true); useWorkflows(workspaceId, true); const allWorkflows = useAppStore((state) => state.workflows.items); const workflows = useMemo(() => allWorkflows.filter((w) => !w.hidden), [allWorkflows]); const agentProfiles = useAppStore((state) => state.agentProfiles.items); const executors = useAppStore((state) => state.executors.items); const allExecutorProfiles = useMemo( () => executors .filter((e) => e.type !== "local" && e.type !== "local_pc") .flatMap((e) => e.profiles ?? []), [executors], ); return { workflows, agentProfiles, allExecutorProfiles }; } function SectionHeader({ children }: { children: React.ReactNode }) { return (
{children}
); } function PlaceholdersHelp() { return (

Available placeholders:

    {ISSUE_WATCH_PLACEHOLDERS.map((p) => (
  • {`{{${p.key}}}`}{" "} {p.description}
  • ))}
); } function IssueWatchFormFields({ form, setForm, workspaceLocked, onAllReposChange, onSelectedReposChange, }: { form: FormState; setForm: React.Dispatch>; workspaceLocked: boolean; onAllReposChange: (checked: boolean) => void; onSelectedReposChange: (repos: RepoFilter[]) => void; }) { return (
setForm((prev) => ({ ...prev, workspaceId: v, workflowId: "", workflowStepId: "", selectedRepos: [], allRepos: false, })) } disabled={workspaceLocked} />
); } function IssueFilterFields({ form, setForm, onAllReposChange, onSelectedReposChange, }: { form: FormState; setForm: React.Dispatch>; onAllReposChange: (checked: boolean) => void; onSelectedReposChange: (repos: RepoFilter[]) => void; }) { return ( <> Filter
setForm((prev) => ({ ...prev, labels: e.target.value }))} placeholder="e.g. bug, enhancement, priority:high" />

Only match issues with these labels. Leave empty for all issues.