Skip to content

Commit 9b84902

Browse files
authored
feat: workflow CRUD (#6)
1 parent 1fdb996 commit 9b84902

File tree

12 files changed

+893
-17
lines changed

12 files changed

+893
-17
lines changed

src/api/posthogClient.ts

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,4 +214,121 @@ export class PostHogAPIClient {
214214
repository: repoName,
215215
}));
216216
}
217+
218+
async createWorkflow(data: {
219+
name: string;
220+
description?: string;
221+
color?: string;
222+
is_default?: boolean;
223+
}) {
224+
const teamId = await this.getTeamId();
225+
return await this.api.post("/api/projects/{project_id}/workflows/", {
226+
path: { project_id: teamId.toString() },
227+
body: {
228+
name: data.name,
229+
description: data.description,
230+
color: data.color,
231+
is_default: data.is_default ?? false,
232+
is_active: true,
233+
} as Schemas.TaskWorkflow,
234+
});
235+
}
236+
237+
async updateWorkflow(
238+
workflowId: string,
239+
data: {
240+
name?: string;
241+
description?: string;
242+
color?: string;
243+
is_default?: boolean;
244+
is_active?: boolean;
245+
},
246+
) {
247+
const teamId = await this.getTeamId();
248+
return await this.api.patch("/api/projects/{project_id}/workflows/{id}/", {
249+
path: { project_id: teamId.toString(), id: workflowId },
250+
body: data,
251+
});
252+
}
253+
254+
async deactivateWorkflow(workflowId: string) {
255+
const teamId = await this.getTeamId();
256+
return await this.api.post(
257+
"/api/projects/{project_id}/workflows/{id}/deactivate/",
258+
{
259+
path: { project_id: teamId.toString(), id: workflowId },
260+
},
261+
);
262+
}
263+
264+
async createStage(
265+
workflowId: string,
266+
data: {
267+
name: string;
268+
key: string;
269+
position: number;
270+
color?: string;
271+
agent_name?: string | null;
272+
is_manual_only?: boolean;
273+
},
274+
) {
275+
const teamId = await this.getTeamId();
276+
return await this.api.post(
277+
"/api/projects/{project_id}/workflows/{workflow_id}/stages/",
278+
{
279+
path: {
280+
project_id: teamId.toString(),
281+
workflow_id: workflowId,
282+
},
283+
body: {
284+
...data,
285+
workflow: workflowId,
286+
} as Schemas.WorkflowStage,
287+
},
288+
);
289+
}
290+
291+
async updateStage(
292+
workflowId: string,
293+
stageId: string,
294+
data: {
295+
name?: string;
296+
key?: string;
297+
position?: number;
298+
color?: string;
299+
agent_name?: string | null;
300+
is_manual_only?: boolean;
301+
is_archived?: boolean;
302+
},
303+
) {
304+
const teamId = await this.getTeamId();
305+
return await this.api.patch(
306+
"/api/projects/{project_id}/workflows/{workflow_id}/stages/{id}/",
307+
{
308+
path: {
309+
project_id: teamId.toString(),
310+
workflow_id: workflowId,
311+
id: stageId,
312+
},
313+
body: data,
314+
},
315+
);
316+
}
317+
318+
async getAgents() {
319+
const teamId = await this.getTeamId();
320+
const url = new URL(`${this.api.baseUrl}/api/projects/${teamId}/agents/`);
321+
const response = await this.api.fetcher.fetch({
322+
method: "get",
323+
url,
324+
path: `/api/projects/${teamId}/agents/`,
325+
});
326+
327+
if (!response.ok) {
328+
throw new Error(`Failed to fetch agents: ${response.statusText}`);
329+
}
330+
331+
const data = await response.json();
332+
return data.results ?? data ?? [];
333+
}
217334
}

src/renderer/components/MainLayout.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,19 @@ import { TabBar } from "./TabBar";
1010
import { TaskCreate } from "./TaskCreate";
1111
import { TaskDetail } from "./TaskDetail";
1212
import { TaskList } from "./TaskList";
13+
import { WorkflowForm } from "./WorkflowForm";
1314
import { WorkflowView } from "./WorkflowView";
1415

1516
export function MainLayout() {
1617
const { activeTabId, tabs, createTab, setActiveTab } = useTabStore();
1718
const { fetchIntegrations } = useIntegrationStore();
1819
const [commandMenuOpen, setCommandMenuOpen] = useState(false);
1920
const [taskCreateOpen, setTaskCreateOpen] = useState(false);
21+
const [workflowCreateOpen, setWorkflowCreateOpen] = useState(false);
22+
23+
useEffect(() => {
24+
fetchIntegrations();
25+
}, [fetchIntegrations]);
2026

2127
useEffect(() => {
2228
fetchIntegrations();
@@ -32,6 +38,7 @@ export function MainLayout() {
3238
enabled: !commandMenuOpen,
3339
});
3440
useHotkeys("mod+n", () => setTaskCreateOpen(true));
41+
useHotkeys("mod+shift+n", () => setWorkflowCreateOpen(true));
3542

3643
const handleSelectTask = (task: Task) => {
3744
// Check if task is already open in a tab
@@ -66,6 +73,7 @@ export function MainLayout() {
6673
<TaskList
6774
onSelectTask={handleSelectTask}
6875
onNewTask={() => setTaskCreateOpen(true)}
76+
onNewWorkflow={() => setWorkflowCreateOpen(true)}
6977
/>
7078
)}
7179

@@ -86,6 +94,10 @@ export function MainLayout() {
8694
onCreateTask={() => setTaskCreateOpen(true)}
8795
/>
8896
<TaskCreate open={taskCreateOpen} onOpenChange={setTaskCreateOpen} />
97+
<WorkflowForm
98+
open={workflowCreateOpen}
99+
onOpenChange={setWorkflowCreateOpen}
100+
/>
89101
</Flex>
90102
);
91103
}

src/renderer/components/TaskList.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,14 @@ interface TaskListProps {
2424

2525
interface TaskListInternalProps extends TaskListProps {
2626
onNewTask?: () => void;
27+
onNewWorkflow?: () => void;
2728
}
2829

29-
export function TaskList({ onSelectTask, onNewTask }: TaskListInternalProps) {
30+
export function TaskList({
31+
onSelectTask,
32+
onNewTask,
33+
onNewWorkflow,
34+
}: TaskListInternalProps) {
3035
const tasks = useTaskStore((state) => state.tasks);
3136
const taskOrder = useTaskStore((state) => state.taskOrder);
3237
const filter = useTaskStore((state) => state.filter);
@@ -386,6 +391,19 @@ export function TaskList({ onSelectTask, onNewTask }: TaskListInternalProps) {
386391
]}
387392
/>
388393
</Box>
394+
{onNewWorkflow && (
395+
<Box onClick={onNewWorkflow}>
396+
<ShortcutCard
397+
icon={<FileTextIcon className="h-4 w-4 text-gray-11" />}
398+
title="New workflow"
399+
keys={[
400+
navigator.platform.includes("Mac") ? "⌘" : "Ctrl",
401+
"⇧",
402+
"N",
403+
]}
404+
/>
405+
</Box>
406+
)}
389407
</Flex>
390408
</Box>
391409
</Box>

0 commit comments

Comments
 (0)