-
Notifications
You must be signed in to change notification settings - Fork 66
Expand file tree
/
Copy pathWebsiteNewTask.tsx
More file actions
147 lines (141 loc) · 5.84 KB
/
Copy pathWebsiteNewTask.tsx
File metadata and controls
147 lines (141 loc) · 5.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import { ANALYTICS_EVENTS } from "@posthog/shared/analytics-events";
import type { Task } from "@posthog/shared/domain-types";
import { CHANNEL_TASK_SUGGESTIONS } from "@posthog/ui/features/canvas/channelTaskSuggestions";
import { ChannelBreadcrumb } from "@posthog/ui/features/canvas/components/ChannelBreadcrumb";
import { ChannelContextPanel } from "@posthog/ui/features/canvas/components/ChannelContextPanel";
import { useChannels } from "@posthog/ui/features/canvas/hooks/useChannels";
import { useChannelTaskMutations } from "@posthog/ui/features/canvas/hooks/useChannelTasks";
import { useFolderInstructions } from "@posthog/ui/features/canvas/hooks/useFolderInstructions";
import { TaskInput } from "@posthog/ui/features/task-detail/components/TaskInput";
import { taskDetailQuery } from "@posthog/ui/features/tasks/queries";
import { useSetHeaderContent } from "@posthog/ui/hooks/useSetHeaderContent";
import { ResizableSidebar } from "@posthog/ui/primitives/ResizableSidebar";
import { toast } from "@posthog/ui/primitives/toast";
import { track } from "@posthog/ui/shell/analytics";
import { Flex } from "@radix-ui/themes";
import { useQueryClient } from "@tanstack/react-query";
import { useNavigate } from "@tanstack/react-router";
import { useCallback, useMemo, useState } from "react";
// A channel's "New task" view. Reuses /code's TaskInput, but routes the created
// task into the channel (/website/$channelId/tasks/$id) instead of /code, and
// files the task to the channel by creating an extra `task` row under the
// channel folder on the project's desktop_file_system surface.
export function WebsiteNewTask({ channelId }: { channelId: string }) {
const navigate = useNavigate();
const queryClient = useQueryClient();
const { fileTask } = useChannelTaskMutations();
const { channels } = useChannels();
const channelName = channels.find((c) => c.id === channelId)?.name;
// Surface the channel breadcrumb in the shared header, same as the other
// channel scenes ("# channel / New task").
useSetHeaderContent(
useMemo(
() => (
<ChannelBreadcrumb
channelName={channelName ?? "Channel"}
channelId={channelId}
leafLabel="New task"
/>
),
[channelName, channelId],
),
);
// The channel's CONTEXT.md, passed to the agent as optional background so
// tasks created here start with the shared context. Absent/empty is fine.
const { data: instructions } = useFolderInstructions(channelId);
const channelContext = instructions?.content;
// Right-side preview of the CONTEXT.md, opened from the composer's chip so the
// user can read what will be sent before submitting (mirrors the post-submit
// context tab). Local view state — no panel-layout store exists pre-submit.
const [contextPanelOpen, setContextPanelOpen] = useState(false);
const [contextPanelWidth, setContextPanelWidth] = useState(360);
const [contextPanelResizing, setContextPanelResizing] = useState(false);
const handleContextChipClick = useCallback(() => {
const nextOpen = !contextPanelOpen;
setContextPanelOpen(nextOpen);
// Only count opening the panel, not closing it, so an open→close→open
// cycle doesn't inflate the metric.
if (nextOpen) {
track(ANALYTICS_EVENTS.CHANNEL_ACTION, {
action_type: "view_context",
surface: "new_task",
channel_id: channelId,
});
}
}, [channelId, contextPanelOpen]);
const onTaskCreated = useCallback(
(task: Task) => {
// Seed the detail cache so the destination route resolves instantly
// (mirrors openTask), then file to the channel + navigate.
queryClient.setQueryData(taskDetailQuery(task.id).queryKey, task);
void fileTask(channelId, task.id, task.title)
.then(() => {
track(ANALYTICS_EVENTS.CHANNEL_ACTION, {
action_type: "file_task",
surface: "new_task",
channel_id: channelId,
task_id: task.id,
success: true,
});
})
.catch((error: unknown) => {
track(ANALYTICS_EVENTS.CHANNEL_ACTION, {
action_type: "file_task",
surface: "new_task",
channel_id: channelId,
task_id: task.id,
success: false,
});
toast.error("Couldn't file task to context", {
description: error instanceof Error ? error.message : String(error),
});
});
void navigate({
to: "/website/$channelId/tasks/$taskId",
params: { channelId, taskId: task.id },
});
},
[channelId, fileTask, navigate, queryClient],
);
return (
<Flex className="h-full min-w-0 flex-1">
<div className="min-w-0 flex-1">
<TaskInput
onTaskCreated={onTaskCreated}
channelContext={channelContext}
channelName={channelName}
channelContextId={channelId}
allowNoRepo
suggestions={CHANNEL_TASK_SUGGESTIONS}
onSuggestionSelect={(label) =>
track(ANALYTICS_EVENTS.CHANNEL_ACTION, {
action_type: "new_task_suggestion",
surface: "new_task",
channel_id: channelId,
suggestion_label: label,
})
}
onContextChipClick={
channelContext ? handleContextChipClick : undefined
}
/>
</div>
<ResizableSidebar
open={contextPanelOpen && !!channelContext}
width={contextPanelWidth}
setWidth={setContextPanelWidth}
isResizing={contextPanelResizing}
setIsResizing={setContextPanelResizing}
side="right"
>
{contextPanelOpen && channelContext ? (
<ChannelContextPanel
channelName={channelName}
body={channelContext}
onClose={() => setContextPanelOpen(false)}
/>
) : null}
</ResizableSidebar>
</Flex>
);
}