@sentry/junior-dashboard 0.272.0 → 0.274.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/app.js +3 -3
  2. package/dist/client.js +69 -67
  3. package/dist/index.js +3 -3
  4. package/dist/tailwind.css +1 -1
  5. package/package.json +4 -4
  6. package/src/client/App.tsx +440 -430
  7. package/src/client/components/JuniorLogo.tsx +1 -1
  8. package/src/client/components/ProfileMenu.tsx +4 -41
  9. package/src/client/components/layout/DashboardChrome.tsx +9 -4
  10. package/src/client/components/layout/DashboardHeader.tsx +11 -51
  11. package/src/client/components/layout/NavRail.tsx +156 -0
  12. package/src/client/components/ui/sidebar.tsx +336 -0
  13. package/src/client/conversations/ConversationComposer.tsx +7 -4
  14. package/src/client/conversations/ConversationPage.tsx +3 -0
  15. package/src/client/conversations/ConversationSidebar.tsx +121 -60
  16. package/src/client/conversations/ConversationWorkspace.tsx +8 -3
  17. package/src/client/conversations/PendingMailboxStack.tsx +22 -9
  18. package/src/client/conversations/TranscriptMessageShell.tsx +19 -8
  19. package/src/client/conversations/TranscriptMessageView.tsx +8 -1
  20. package/src/client/conversations/TranscriptTurnContextView.tsx +1 -1
  21. package/src/client/conversations/composerAutocorrect.ts +12 -0
  22. package/src/client/conversations/conversationAge.ts +24 -0
  23. package/src/client/conversations/conversationOutbox.ts +19 -0
  24. package/src/client/conversations/conversationRoutes.ts +14 -5
  25. package/src/client/conversations/conversationSections.ts +64 -50
  26. package/src/client/conversations/eventTranscript.ts +6 -0
  27. package/src/client/conversations/queries.ts +1 -17
  28. package/src/client/conversations/transcript.ts +12 -2
  29. package/src/client/conversations/useConversationFinishedIndicators.ts +40 -8
  30. package/src/client/format.ts +8 -1
  31. package/src/client/pages/settings/SettingsPage.tsx +28 -0
  32. package/src/client/review-desk/ConversationReviewBar.tsx +20 -2
  33. package/src/client/review-desk/ReviewDeskPage.tsx +5 -1
  34. package/src/client/review-desk/queries.ts +34 -7
  35. package/src/client/types.ts +2 -0
  36. package/src/mock-reporting/routes.ts +1 -1
@@ -12,12 +12,14 @@ import {
12
12
  import { Button } from "../../components/ui/button";
13
13
  import { Input } from "../../components/ui/input";
14
14
  import { Textarea } from "../../components/ui/textarea";
15
+ import { Checkbox } from "../../components/ui/checkbox";
15
16
  import { Field, FieldDescription, FieldLabel } from "../../components/ui/field";
16
17
  import { RadioGroup, RadioGroupItem } from "../../components/ui/radio-group";
17
18
  import {
18
19
  useConversationStartPosition,
19
20
  type ConversationStartPosition,
20
21
  } from "../../conversations/transcriptReadPosition";
22
+ import { useComposerAutocorrect } from "../../conversations/composerAutocorrect";
21
23
  import { InlineError } from "../../components/InlineError";
22
24
  import { Card } from "../../components/layout/Card";
23
25
  import { DashboardApiError, fetchDashboardJson, patch, put } from "../../http";
@@ -105,6 +107,7 @@ export function SettingsPage({ identity }: SettingsPageProps) {
105
107
  </Card>
106
108
  </form>
107
109
  <ConversationStartSetting />
110
+ <ComposerAutocorrectSetting />
108
111
  <CustomizationForm />
109
112
  </SettingsPageLayout>
110
113
  );
@@ -166,6 +169,31 @@ function ConversationStartSetting() {
166
169
  );
167
170
  }
168
171
 
172
+ /** Let the user turn on keyboard autocorrect in the composer on this browser. */
173
+ function ComposerAutocorrectSetting() {
174
+ const [autocorrect, setAutocorrect] = useComposerAutocorrect();
175
+ return (
176
+ <Card className="mt-6 mb-0" padding="md" variant="raised">
177
+ <h2 className="m-0 text-lg font-bold">Message box</h2>
178
+ <label className="mt-4 flex cursor-pointer items-start gap-3">
179
+ <Checkbox
180
+ checked={autocorrect}
181
+ className="mt-0.5"
182
+ onCheckedChange={setAutocorrect}
183
+ />
184
+ <span className="grid gap-0.5">
185
+ <span className="text-sm font-medium">Autocorrect</span>
186
+ <span className="text-xs text-dashboard-text-muted">
187
+ Let your keyboard correct spelling and capitalize sentences as you
188
+ type. This can change ids, paths, and code. This choice applies to
189
+ this browser.
190
+ </span>
191
+ </span>
192
+ </label>
193
+ </Card>
194
+ );
195
+ }
196
+
169
197
  /**
170
198
  * Let the user save text that changes how the agent replies to them. The
171
199
  * server returns 404 when the app does not enable this experiment.
@@ -1,10 +1,14 @@
1
1
  import { ChevronRight } from "lucide-react";
2
- import { useState } from "react";
2
+ import { useSearchParams } from "react-router";
3
3
 
4
4
  import { Drawer } from "../components/Drawer";
5
5
  import { LoadMorePagination } from "../components/Pagination";
6
6
  import { ReviewItemCard } from "./ReviewItemCard";
7
7
  import { useReviewItemsData } from "./queries";
8
+ import {
9
+ CONVERSATION_REVIEW_OPEN,
10
+ CONVERSATION_REVIEW_PARAM,
11
+ } from "../conversations/conversationRoutes";
8
12
 
9
13
  /** `more` marks a count of the loaded pages only, such as "50+". */
10
14
  function itemCountLabel(count: number, more: boolean): string {
@@ -15,9 +19,23 @@ function itemCountLabel(count: number, more: boolean): string {
15
19
  /**
16
20
  * Show how many open Review items are linked to a Conversation, above its
17
21
  * message box. The bar opens a side panel where the viewer can act on each.
22
+ * The URL owns the panel, so a sidebar link can open it.
18
23
  */
19
24
  export function ConversationReviewBar(props: { conversationId: string }) {
20
- const [open, setOpen] = useState(false);
25
+ const [searchParams, setSearchParams] = useSearchParams();
26
+ const open =
27
+ searchParams.get(CONVERSATION_REVIEW_PARAM) === CONVERSATION_REVIEW_OPEN;
28
+ const setOpen = (value: boolean) =>
29
+ setSearchParams(
30
+ (current) => {
31
+ const next = new URLSearchParams(current);
32
+ if (value)
33
+ next.set(CONVERSATION_REVIEW_PARAM, CONVERSATION_REVIEW_OPEN);
34
+ else next.delete(CONVERSATION_REVIEW_PARAM);
35
+ return next;
36
+ },
37
+ { replace: true },
38
+ );
21
39
  const { items, query } = useReviewItemsData({
22
40
  conversationId: props.conversationId,
23
41
  });
@@ -69,7 +69,11 @@ export function ReviewDeskPage() {
69
69
  useDocumentTitle("Inbox");
70
70
  const [searchParams] = useSearchParams();
71
71
  const [search, setSearch, q] = useDebouncedSearchParam();
72
- const { items: list, moreArchived, query: items } = useReviewItemsData({ q });
72
+ const {
73
+ items: list,
74
+ moreArchived,
75
+ query: items,
76
+ } = useReviewItemsData({ forViewer: true, q });
73
77
  const showBlocked = !q;
74
78
  const blocked = useBlockedAutomationsData(showBlocked);
75
79
  const blockedAutomations = showBlocked
@@ -26,16 +26,19 @@ const COUNT_REFRESH_MS = 60_000;
26
26
  /**
27
27
  * List the open Review items that the viewer can see, one page at a time.
28
28
  * A search also finds archived items, with the first page. `items` holds the
29
- * items of every loaded page, and `query` loads more.
29
+ * items of every loaded page, and `query` loads more. `forViewer` drops the
30
+ * items that are assigned only to other people.
30
31
  */
31
32
  export function useReviewItemsData(filters: {
32
33
  conversationId?: string;
34
+ forViewer?: boolean;
33
35
  q?: string;
34
36
  }) {
35
37
  const params = new URLSearchParams();
36
38
  if (filters.conversationId) {
37
39
  params.set("conversationId", filters.conversationId);
38
40
  }
41
+ if (filters.forViewer) params.set("for", "viewer");
39
42
  if (filters.q) params.set("q", filters.q);
40
43
  const search = params.toString();
41
44
  const query = useInfiniteQuery({
@@ -150,13 +153,20 @@ export function useReviewItemAction() {
150
153
  });
151
154
  }
152
155
 
153
- const NO_CONVERSATIONS: ReadonlySet<string> = new Set();
156
+ /** Newest open Review item title and item count for one waiting Conversation. */
157
+ export type WaitingConversation = { count: number; title: string };
158
+
159
+ const NO_WAITING: ReadonlyMap<string, WaitingConversation> = new Map();
154
160
 
155
161
  /**
156
- * Return the Conversations that wait on the viewer. Today only the review
157
- * desk sets this: a linked open action item assigned to the viewer.
162
+ * Return the Conversations that wait on the viewer, by Conversation id. Today
163
+ * only the review desk sets this: a linked open action item assigned to the
164
+ * viewer.
158
165
  */
159
- export function useWaitingConversationIds(): ReadonlySet<string> {
166
+ export function useWaitingConversations(): ReadonlyMap<
167
+ string,
168
+ WaitingConversation
169
+ > {
160
170
  const query = useQuery({
161
171
  queryKey: [...REVIEW_ITEMS_QUERY_KEY, "waiting-conversations"],
162
172
  queryFn: ({ signal }) =>
@@ -168,6 +178,23 @@ export function useWaitingConversationIds(): ReadonlySet<string> {
168
178
  refetchInterval: COUNT_REFRESH_MS,
169
179
  retry: false,
170
180
  });
171
- const ids = query.data?.conversationIds;
172
- return useMemo(() => (ids ? new Set(ids) : NO_CONVERSATIONS), [ids]);
181
+ const conversations = query.data?.conversations;
182
+ return useMemo(
183
+ () =>
184
+ conversations
185
+ ? new Map(
186
+ conversations.map(({ conversationId, count, title }) => [
187
+ conversationId,
188
+ { count, title },
189
+ ]),
190
+ )
191
+ : NO_WAITING,
192
+ [conversations],
193
+ );
194
+ }
195
+
196
+ /** Return the ids of the Conversations that wait on the viewer. */
197
+ export function useWaitingConversationIds(): ReadonlySet<string> {
198
+ const waiting = useWaitingConversations();
199
+ return useMemo(() => new Set(waiting.keys()), [waiting]);
173
200
  }
@@ -121,6 +121,8 @@ export type TranscriptViewMessage = {
121
121
  actorIdentity?: ActorIdentity;
122
122
  /** Agent invocation whose input this is. An agent wrote it, not a person. */
123
123
  agentInvocationId?: string;
124
+ /** Identity id of the author. Conversation `authors` maps it to the person. */
125
+ actorIdentityId?: string;
124
126
  contexts?: TranscriptViewTurnContext[];
125
127
  /** Mailbox delivery mode while the message is still pending history commit. */
126
128
  delivery?: "defer" | "interrupt";
@@ -331,7 +331,7 @@ export function createMockReportingApi(): Hono<{
331
331
  jsonResponse(reviewItemCountSchema, { needsViewer: 0 }),
332
332
  );
333
333
  app.get("/review-items/waiting-conversations", () =>
334
- jsonResponse(reviewItemWaitingConversationsSchema, { conversationIds: [] }),
334
+ jsonResponse(reviewItemWaitingConversationsSchema, { conversations: [] }),
335
335
  );
336
336
  app.get("/review-items/:id", () =>
337
337
  errorResponse("Review item not found.", 404),