@agent-native/dispatch 0.14.0 → 0.14.2
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.
- package/dist/actions/navigate.js +1 -1
- package/dist/actions/navigate.js.map +1 -1
- package/dist/actions/view-screen.js +9 -0
- package/dist/actions/view-screen.js.map +1 -1
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +75 -70
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/components/messaging-setup-panel.d.ts.map +1 -1
- package/dist/components/messaging-setup-panel.js +15 -2
- package/dist/components/messaging-setup-panel.js.map +1 -1
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +1 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/pages/operations.d.ts +5 -0
- package/dist/routes/pages/operations.d.ts.map +1 -0
- package/dist/routes/pages/operations.js +51 -0
- package/dist/routes/pages/operations.js.map +1 -0
- package/dist/routes/pages/overview.d.ts +2 -2
- package/dist/server/lib/dispatch-routing.js +1 -1
- package/dist/server/lib/dispatch-routing.js.map +1 -1
- package/dist/server/lib/env-config.js +1 -1
- package/dist/server/lib/env-config.js.map +1 -1
- package/package.json +2 -2
- package/src/actions/navigate.ts +1 -1
- package/src/actions/view-screen.ts +11 -0
- package/src/components/layout/Layout.spec.tsx +154 -0
- package/src/components/layout/Layout.tsx +196 -189
- package/src/components/messaging-setup-panel.spec.tsx +17 -1
- package/src/components/messaging-setup-panel.tsx +81 -1
- package/src/routes/index.spec.ts +23 -0
- package/src/routes/index.ts +1 -0
- package/src/routes/pages/operations.tsx +134 -0
- package/src/server/lib/dispatch-routing.spec.ts +2 -0
- package/src/server/lib/dispatch-routing.ts +1 -1
- package/src/server/lib/env-config.ts +1 -1
- package/src/styles/dispatch-css.spec.ts +11 -0
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import { ExtensionsSidebarSection } from "@agent-native/core/client/extensions";
|
|
17
17
|
import { InvitationBanner, OrgSwitcher } from "@agent-native/core/client/org";
|
|
18
18
|
import {
|
|
19
|
+
IconActivity,
|
|
19
20
|
IconArrowUpRight,
|
|
20
21
|
IconApps,
|
|
21
22
|
IconBrain,
|
|
@@ -60,6 +61,7 @@ import {
|
|
|
60
61
|
} from "../ui/dropdown-menu";
|
|
61
62
|
import { Input } from "../ui/input";
|
|
62
63
|
import { Sheet, SheetContent, SheetDescription, SheetTitle } from "../ui/sheet";
|
|
64
|
+
import { Skeleton } from "../ui/skeleton";
|
|
63
65
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
|
|
64
66
|
import { Header } from "./Header";
|
|
65
67
|
import { HeaderActionsProvider } from "./HeaderActions";
|
|
@@ -113,37 +115,65 @@ const PRIMARY_NAV_ITEMS = [
|
|
|
113
115
|
icon: IconApps,
|
|
114
116
|
section: "primary",
|
|
115
117
|
},
|
|
118
|
+
] as const satisfies readonly DispatchNavItem[];
|
|
119
|
+
|
|
120
|
+
const OPERATIONS_NAV_ITEMS = [
|
|
121
|
+
{
|
|
122
|
+
id: "operations",
|
|
123
|
+
to: "/operations",
|
|
124
|
+
label: "Operations",
|
|
125
|
+
icon: IconActivity,
|
|
126
|
+
section: "operations",
|
|
127
|
+
},
|
|
116
128
|
{
|
|
117
129
|
id: "metrics",
|
|
118
130
|
to: "/metrics",
|
|
119
131
|
label: "Metrics",
|
|
120
132
|
icon: IconChartBar,
|
|
121
|
-
section: "
|
|
133
|
+
section: "operations",
|
|
122
134
|
},
|
|
123
135
|
{
|
|
124
|
-
id: "
|
|
125
|
-
to: "/
|
|
126
|
-
label: "
|
|
127
|
-
icon:
|
|
128
|
-
section: "
|
|
136
|
+
id: "automations",
|
|
137
|
+
to: "/automations",
|
|
138
|
+
label: "Automations",
|
|
139
|
+
icon: IconSettingsAutomation,
|
|
140
|
+
section: "operations",
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: "approvals",
|
|
144
|
+
to: "/approvals",
|
|
145
|
+
label: "Approvals",
|
|
146
|
+
icon: IconShieldCheck,
|
|
147
|
+
section: "operations",
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: "destinations",
|
|
151
|
+
to: "/destinations",
|
|
152
|
+
label: "Destinations",
|
|
153
|
+
icon: IconArrowUpRight,
|
|
154
|
+
section: "operations",
|
|
129
155
|
},
|
|
130
156
|
{
|
|
131
157
|
id: "integrations",
|
|
132
158
|
to: "/integrations",
|
|
133
159
|
label: "Integrations",
|
|
134
160
|
icon: IconPuzzle,
|
|
135
|
-
section: "
|
|
161
|
+
section: "operations",
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
id: "vault",
|
|
165
|
+
to: "/vault",
|
|
166
|
+
label: "Vault",
|
|
167
|
+
icon: IconKey,
|
|
168
|
+
section: "operations",
|
|
136
169
|
},
|
|
137
170
|
{
|
|
138
171
|
id: "agents",
|
|
139
172
|
to: "/agents",
|
|
140
173
|
label: "Agents",
|
|
141
174
|
icon: IconPlugConnected,
|
|
142
|
-
section: "
|
|
175
|
+
section: "operations",
|
|
143
176
|
},
|
|
144
|
-
] as const satisfies readonly DispatchNavItem[];
|
|
145
|
-
|
|
146
|
-
const OPERATIONS_NAV_ITEMS = [
|
|
147
177
|
{
|
|
148
178
|
id: "workspace",
|
|
149
179
|
to: "/workspace",
|
|
@@ -151,6 +181,16 @@ const OPERATIONS_NAV_ITEMS = [
|
|
|
151
181
|
icon: IconLayersSubtract,
|
|
152
182
|
section: "operations",
|
|
153
183
|
},
|
|
184
|
+
{
|
|
185
|
+
id: "settings",
|
|
186
|
+
to: "/settings",
|
|
187
|
+
label: "Settings",
|
|
188
|
+
icon: IconSettings,
|
|
189
|
+
section: "operations",
|
|
190
|
+
},
|
|
191
|
+
] as const satisfies readonly DispatchNavItem[];
|
|
192
|
+
|
|
193
|
+
const ADVANCED_NAV_ITEMS = [
|
|
154
194
|
{
|
|
155
195
|
id: "messaging",
|
|
156
196
|
to: "/messaging",
|
|
@@ -158,13 +198,6 @@ const OPERATIONS_NAV_ITEMS = [
|
|
|
158
198
|
icon: IconBrandTelegram,
|
|
159
199
|
section: "operations",
|
|
160
200
|
},
|
|
161
|
-
{
|
|
162
|
-
id: "destinations",
|
|
163
|
-
to: "/destinations",
|
|
164
|
-
label: "Destinations",
|
|
165
|
-
icon: IconArrowUpRight,
|
|
166
|
-
section: "operations",
|
|
167
|
-
},
|
|
168
201
|
{
|
|
169
202
|
id: "identities",
|
|
170
203
|
to: "/identities",
|
|
@@ -172,20 +205,6 @@ const OPERATIONS_NAV_ITEMS = [
|
|
|
172
205
|
icon: IconFingerprint,
|
|
173
206
|
section: "operations",
|
|
174
207
|
},
|
|
175
|
-
{
|
|
176
|
-
id: "approvals",
|
|
177
|
-
to: "/approvals",
|
|
178
|
-
label: "Approvals",
|
|
179
|
-
icon: IconShieldCheck,
|
|
180
|
-
section: "operations",
|
|
181
|
-
},
|
|
182
|
-
{
|
|
183
|
-
id: "automations",
|
|
184
|
-
to: "/automations",
|
|
185
|
-
label: "Automations",
|
|
186
|
-
icon: IconSettingsAutomation,
|
|
187
|
-
section: "operations",
|
|
188
|
-
},
|
|
189
208
|
{
|
|
190
209
|
id: "audit",
|
|
191
210
|
to: "/audit",
|
|
@@ -207,13 +226,6 @@ const OPERATIONS_NAV_ITEMS = [
|
|
|
207
226
|
icon: IconMessages,
|
|
208
227
|
section: "operations",
|
|
209
228
|
},
|
|
210
|
-
{
|
|
211
|
-
id: "settings",
|
|
212
|
-
to: "/settings",
|
|
213
|
-
label: "Settings",
|
|
214
|
-
icon: IconSettings,
|
|
215
|
-
section: "operations",
|
|
216
|
-
},
|
|
217
229
|
] as const satisfies readonly DispatchNavItem[];
|
|
218
230
|
|
|
219
231
|
const EMPTY_NAV_ITEMS: readonly DispatchNavItem[] = [];
|
|
@@ -338,6 +350,7 @@ function DispatchChatsSection({ onNavigate }: { onNavigate?: () => void }) {
|
|
|
338
350
|
const {
|
|
339
351
|
threads,
|
|
340
352
|
activeThreadId,
|
|
353
|
+
isLoading: chatsLoading,
|
|
341
354
|
createThread,
|
|
342
355
|
switchThread,
|
|
343
356
|
renameThread,
|
|
@@ -439,134 +452,123 @@ function DispatchChatsSection({ onNavigate }: { onNavigate?: () => void }) {
|
|
|
439
452
|
}
|
|
440
453
|
|
|
441
454
|
return (
|
|
442
|
-
<div className="
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
? "bg-sidebar-accent text-sidebar-accent-foreground"
|
|
477
|
-
: "text-sidebar-foreground/80 hover:bg-sidebar-accent/65 hover:text-sidebar-accent-foreground",
|
|
478
|
-
)}
|
|
455
|
+
<div className="ms-4 min-w-0 space-y-0.5">
|
|
456
|
+
{chatsLoading &&
|
|
457
|
+
visibleThreads.length === 0 &&
|
|
458
|
+
Array.from({ length: 3 }).map((_, index) => (
|
|
459
|
+
<div
|
|
460
|
+
key={`chat-skeleton-${index}`}
|
|
461
|
+
className="flex items-center gap-2 px-3 py-1"
|
|
462
|
+
>
|
|
463
|
+
<Skeleton className="size-3.5 shrink-0 rounded-sm" />
|
|
464
|
+
<Skeleton className="h-3 w-3/4 rounded" />
|
|
465
|
+
</div>
|
|
466
|
+
))}
|
|
467
|
+
{visibleThreads.map((thread) => {
|
|
468
|
+
const localPathname = localDispatchPath(location.pathname);
|
|
469
|
+
const isActive =
|
|
470
|
+
thread.id ===
|
|
471
|
+
(threadIdFromPath(localPathname) ??
|
|
472
|
+
(localPathname === "/chat" ? null : activeThreadId));
|
|
473
|
+
const isRenaming = thread.id === renamingThreadId;
|
|
474
|
+
const title = threadTitle(thread, t("dispatch.sidebar.newChat"));
|
|
475
|
+
return (
|
|
476
|
+
<div
|
|
477
|
+
key={thread.id}
|
|
478
|
+
className={cn(
|
|
479
|
+
"group/item relative flex min-w-0 items-center rounded-lg transition-colors",
|
|
480
|
+
isActive
|
|
481
|
+
? "bg-sidebar-accent text-sidebar-accent-foreground"
|
|
482
|
+
: "text-muted-foreground hover:bg-sidebar-accent/50 hover:text-foreground",
|
|
483
|
+
)}
|
|
484
|
+
>
|
|
485
|
+
{isRenaming ? (
|
|
486
|
+
<form
|
|
487
|
+
onSubmit={handleRenameSubmit}
|
|
488
|
+
className="flex min-w-0 flex-1 items-center px-1"
|
|
479
489
|
>
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
t("dispatch.sidebar.newChat"),
|
|
501
|
-
),
|
|
502
|
-
})}
|
|
503
|
-
className="h-6 min-w-0 rounded-sm border-sidebar-border bg-background px-1.5 text-xs"
|
|
504
|
-
/>
|
|
505
|
-
</form>
|
|
506
|
-
) : (
|
|
507
|
-
<>
|
|
490
|
+
<Input
|
|
491
|
+
ref={renameInputRef}
|
|
492
|
+
value={renameDraft}
|
|
493
|
+
onChange={(event) => setRenameDraft(event.target.value)}
|
|
494
|
+
onBlur={() => void commitRenameThread()}
|
|
495
|
+
onKeyDown={(event) => {
|
|
496
|
+
if (event.key === "Escape") {
|
|
497
|
+
event.preventDefault();
|
|
498
|
+
cancelRenameThread();
|
|
499
|
+
}
|
|
500
|
+
}}
|
|
501
|
+
maxLength={160}
|
|
502
|
+
aria-label={t("dispatch.sidebar.renameThread", { title })}
|
|
503
|
+
className="h-6 min-w-0 rounded-sm border-sidebar-border bg-background px-1.5 text-xs"
|
|
504
|
+
/>
|
|
505
|
+
</form>
|
|
506
|
+
) : (
|
|
507
|
+
<>
|
|
508
|
+
<Tooltip>
|
|
509
|
+
<TooltipTrigger asChild>
|
|
508
510
|
<button
|
|
509
511
|
type="button"
|
|
510
512
|
onClick={() => openThread(thread.id)}
|
|
511
|
-
className="
|
|
513
|
+
className="min-w-0 flex-1 cursor-pointer px-2 py-1.5 pe-12 text-start text-xs outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
512
514
|
>
|
|
513
|
-
<span className="min-w-0
|
|
514
|
-
{threadTitle(thread, t("dispatch.sidebar.newChat"))}
|
|
515
|
-
</span>
|
|
515
|
+
<span className="block min-w-0 truncate">{title}</span>
|
|
516
516
|
</button>
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
517
|
+
</TooltipTrigger>
|
|
518
|
+
<TooltipContent side="right">{title}</TooltipContent>
|
|
519
|
+
</Tooltip>
|
|
520
|
+
<div className="pointer-events-none absolute end-1 top-1/2 flex -translate-y-1/2 items-center gap-0.5">
|
|
521
|
+
<span className="pointer-events-none pe-1 text-[11px] text-muted-foreground/60 transition-opacity group-hover/item:opacity-0 group-focus-within/item:opacity-0">
|
|
522
|
+
{isActive
|
|
523
|
+
? ""
|
|
524
|
+
: formatThreadAge(threadUpdatedAt(thread), formatDate)}
|
|
525
|
+
</span>
|
|
526
|
+
<DropdownMenu>
|
|
527
|
+
<Tooltip>
|
|
528
|
+
<TooltipTrigger asChild>
|
|
527
529
|
<DropdownMenuTrigger asChild>
|
|
528
530
|
<button
|
|
529
531
|
type="button"
|
|
530
532
|
aria-label={t("dispatch.sidebar.chatOptions", {
|
|
531
|
-
title
|
|
532
|
-
thread,
|
|
533
|
-
t("dispatch.sidebar.newChat"),
|
|
534
|
-
),
|
|
533
|
+
title,
|
|
535
534
|
})}
|
|
536
|
-
className="
|
|
535
|
+
className="pointer-events-auto rounded p-0.5 text-muted-foreground/50 opacity-0 transition-all hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring group-hover/item:opacity-100 group-focus-within/item:opacity-100 data-[state=open]:opacity-100 data-[state=open]:text-foreground"
|
|
537
536
|
>
|
|
538
|
-
<IconDots className="size-
|
|
537
|
+
<IconDots className="size-3" />
|
|
539
538
|
</button>
|
|
540
539
|
</DropdownMenuTrigger>
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
540
|
+
</TooltipTrigger>
|
|
541
|
+
<TooltipContent side="right">
|
|
542
|
+
{t("dispatch.sidebar.chatOptions", { title })}
|
|
543
|
+
</TooltipContent>
|
|
544
|
+
</Tooltip>
|
|
545
|
+
<DropdownMenuContent
|
|
546
|
+
align="start"
|
|
547
|
+
side="right"
|
|
548
|
+
className="w-44"
|
|
549
|
+
>
|
|
550
|
+
<DropdownMenuItem
|
|
551
|
+
onSelect={() => startRenameThread(thread)}
|
|
552
|
+
>
|
|
553
|
+
<IconEdit className="size-3.5" />
|
|
554
|
+
{t("dispatch.sidebar.renameChat")}
|
|
555
|
+
</DropdownMenuItem>
|
|
556
|
+
</DropdownMenuContent>
|
|
557
|
+
</DropdownMenu>
|
|
558
|
+
</div>
|
|
559
|
+
</>
|
|
560
|
+
)}
|
|
561
|
+
</div>
|
|
562
|
+
);
|
|
563
|
+
})}
|
|
564
|
+
<button
|
|
565
|
+
type="button"
|
|
566
|
+
onClick={() => void handleNewChat()}
|
|
567
|
+
className="flex w-full cursor-pointer items-center gap-2 rounded-lg px-3 py-1.5 text-xs text-muted-foreground/60 transition-colors hover:bg-sidebar-accent/50 hover:text-foreground"
|
|
568
|
+
>
|
|
569
|
+
<IconPlus className="size-3 shrink-0" />
|
|
570
|
+
<span className="truncate">{t("dispatch.sidebar.newChat")}</span>
|
|
571
|
+
</button>
|
|
570
572
|
</div>
|
|
571
573
|
);
|
|
572
574
|
}
|
|
@@ -604,7 +606,7 @@ export function NavContent({
|
|
|
604
606
|
...navItemsForSection(extensionNavItems, "operations"),
|
|
605
607
|
];
|
|
606
608
|
const localPathname = localDispatchPath(location.pathname);
|
|
607
|
-
const
|
|
609
|
+
const advancedOpen = ADVANCED_NAV_ITEMS.some((item) =>
|
|
608
610
|
navItemMatchesPath(item, localPathname),
|
|
609
611
|
);
|
|
610
612
|
const navLabel = (item: DispatchNavItem) => {
|
|
@@ -720,36 +722,27 @@ export function NavContent({
|
|
|
720
722
|
<div
|
|
721
723
|
className={cn(
|
|
722
724
|
"flex items-center",
|
|
723
|
-
collapsed ? "justify-center" : "gap-
|
|
725
|
+
collapsed ? "justify-center" : "gap-2",
|
|
724
726
|
)}
|
|
725
727
|
>
|
|
726
728
|
{!collapsed && (
|
|
727
729
|
<>
|
|
728
|
-
<
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
/>
|
|
741
|
-
</div>
|
|
730
|
+
<img
|
|
731
|
+
src={appPath("/agent-native-icon-light.svg")}
|
|
732
|
+
alt=""
|
|
733
|
+
aria-hidden="true"
|
|
734
|
+
className="block h-5 w-auto shrink-0 dark:hidden"
|
|
735
|
+
/>
|
|
736
|
+
<img
|
|
737
|
+
src={appPath("/agent-native-icon-dark.svg")}
|
|
738
|
+
alt=""
|
|
739
|
+
aria-hidden="true"
|
|
740
|
+
className="hidden h-5 w-auto shrink-0 dark:block"
|
|
741
|
+
/>
|
|
742
742
|
<div className="min-w-0 flex-1">
|
|
743
|
-
<div className="truncate text-
|
|
743
|
+
<div className="truncate text-lg font-bold tracking-tight text-foreground">
|
|
744
744
|
{workspaceLabel ?? "Dispatch"}
|
|
745
745
|
</div>
|
|
746
|
-
<div className="truncate text-xs text-muted-foreground">
|
|
747
|
-
{workspaceLabel
|
|
748
|
-
? t("dispatch.sidebar.workspaceSubtitle", {
|
|
749
|
-
count: ws?.appCount ?? 0,
|
|
750
|
-
})
|
|
751
|
-
: t("dispatch.sidebar.workspaceControlPlane")}
|
|
752
|
-
</div>
|
|
753
746
|
</div>
|
|
754
747
|
</>
|
|
755
748
|
)}
|
|
@@ -791,30 +784,44 @@ export function NavContent({
|
|
|
791
784
|
collapsed && "flex flex-col items-center",
|
|
792
785
|
)}
|
|
793
786
|
>
|
|
794
|
-
{(
|
|
795
|
-
? [...primaryNavItems, ...operationsNavItems]
|
|
796
|
-
: primaryNavItems
|
|
797
|
-
).map(renderNavItem)}
|
|
787
|
+
{primaryNavItems.map(renderNavItem)}
|
|
798
788
|
</ul>
|
|
799
|
-
</nav>
|
|
800
789
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
790
|
+
{collapsed ? (
|
|
791
|
+
<ul className="mt-2 flex flex-col items-center space-y-0.5">
|
|
792
|
+
{[...operationsNavItems, ...ADVANCED_NAV_ITEMS].map(
|
|
793
|
+
renderNavItem,
|
|
794
|
+
)}
|
|
795
|
+
</ul>
|
|
796
|
+
) : (
|
|
797
|
+
<div className="mt-5">
|
|
798
|
+
<p className="px-2 pb-1 text-[11px] font-medium uppercase tracking-wide text-sidebar-foreground/45">
|
|
799
|
+
{t("dispatch.nav.operate", { defaultValue: "Operate" })}
|
|
800
|
+
</p>
|
|
801
|
+
<ul className="space-y-0.5">
|
|
802
|
+
{operationsNavItems.map(renderNavItem)}
|
|
803
|
+
</ul>
|
|
804
|
+
|
|
805
|
+
<details className="group mt-3" open={advancedOpen}>
|
|
806
|
+
<summary className="flex h-8 cursor-pointer list-none items-center justify-between rounded-md px-2 text-xs font-medium text-sidebar-foreground/60 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground [&::-webkit-details-marker]:hidden">
|
|
807
|
+
<span>
|
|
808
|
+
{t("dispatch.nav.advanced", { defaultValue: "Advanced" })}
|
|
809
|
+
</span>
|
|
807
810
|
<IconChevronDown
|
|
808
811
|
size={14}
|
|
809
812
|
className="transition-transform group-open:rotate-180"
|
|
810
813
|
/>
|
|
811
814
|
</summary>
|
|
812
815
|
<ul className="mt-1 space-y-0.5">
|
|
813
|
-
{
|
|
816
|
+
{ADVANCED_NAV_ITEMS.map(renderNavItem)}
|
|
814
817
|
</ul>
|
|
815
818
|
</details>
|
|
816
819
|
</div>
|
|
820
|
+
)}
|
|
821
|
+
</nav>
|
|
817
822
|
|
|
823
|
+
{!collapsed ? (
|
|
824
|
+
<div className="mt-auto shrink-0">
|
|
818
825
|
<div className="px-2 py-1">
|
|
819
826
|
<ExtensionsSidebarSection />
|
|
820
827
|
</div>
|
|
@@ -59,7 +59,16 @@ vi.mock("@agent-native/core/integrations", () => ({
|
|
|
59
59
|
documentation: { href: "/docs/messaging#slack" },
|
|
60
60
|
setup: { steps: ["Create a Slack app."] },
|
|
61
61
|
credentialRequirements: [
|
|
62
|
-
{
|
|
62
|
+
{
|
|
63
|
+
key: "SLACK_BOT_TOKEN",
|
|
64
|
+
label: "Slack Bot Token (legacy)",
|
|
65
|
+
required: false,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
key: "SLACK_CLIENT_ID",
|
|
69
|
+
label: "Slack OAuth Client ID",
|
|
70
|
+
required: true,
|
|
71
|
+
},
|
|
63
72
|
],
|
|
64
73
|
},
|
|
65
74
|
{
|
|
@@ -142,6 +151,7 @@ describe("MessagingSetupPanel", () => {
|
|
|
142
151
|
expect(container.textContent).toContain(
|
|
143
152
|
"Save the required Slack app credentials below",
|
|
144
153
|
);
|
|
154
|
+
expect(container.textContent).toContain("Slack Bot Token (legacy)");
|
|
145
155
|
});
|
|
146
156
|
|
|
147
157
|
it("shows connected and alternative credential states", async () => {
|
|
@@ -155,6 +165,12 @@ describe("MessagingSetupPanel", () => {
|
|
|
155
165
|
required: true,
|
|
156
166
|
configured: true,
|
|
157
167
|
},
|
|
168
|
+
{
|
|
169
|
+
key: "SLACK_CLIENT_ID",
|
|
170
|
+
label: "Slack OAuth Client ID",
|
|
171
|
+
required: true,
|
|
172
|
+
configured: true,
|
|
173
|
+
},
|
|
158
174
|
{
|
|
159
175
|
key: "RESEND_API_KEY",
|
|
160
176
|
label: "Resend API Key",
|
|
@@ -455,6 +455,12 @@ export function MessagingSetupPanel() {
|
|
|
455
455
|
));
|
|
456
456
|
const enabled = !!status?.enabled;
|
|
457
457
|
const envKeys = platform.credentialRequirements;
|
|
458
|
+
const primaryEnvKeys = envKeys.filter(
|
|
459
|
+
(envKey) => envKey.key !== "SLACK_BOT_TOKEN",
|
|
460
|
+
);
|
|
461
|
+
const legacyEnvKeys = envKeys.filter(
|
|
462
|
+
(envKey) => envKey.key === "SLACK_BOT_TOKEN",
|
|
463
|
+
);
|
|
458
464
|
const missingRequiredCredentials = hasMissingRequiredCredentials(
|
|
459
465
|
envKeys,
|
|
460
466
|
envStatusByKey,
|
|
@@ -789,7 +795,7 @@ export function MessagingSetupPanel() {
|
|
|
789
795
|
) : null}
|
|
790
796
|
</div>
|
|
791
797
|
<div className="space-y-3">
|
|
792
|
-
{
|
|
798
|
+
{primaryEnvKeys.map((envKey) => {
|
|
793
799
|
const envStatus = envStatusByKey.get(envKey.key);
|
|
794
800
|
const isConfigured = !!envStatus?.configured;
|
|
795
801
|
const helpText = envKey.helpText ?? envStatus?.helpText;
|
|
@@ -847,6 +853,80 @@ export function MessagingSetupPanel() {
|
|
|
847
853
|
);
|
|
848
854
|
})}
|
|
849
855
|
</div>
|
|
856
|
+
{legacyEnvKeys.length ? (
|
|
857
|
+
<Collapsible>
|
|
858
|
+
<CollapsibleTrigger className="group flex w-full cursor-pointer items-center gap-1.5 text-xs font-medium text-muted-foreground hover:text-foreground">
|
|
859
|
+
<IconChevronRight className="h-3.5 w-3.5 transition-transform group-data-[state=open]:rotate-90" />
|
|
860
|
+
<span>{legacyEnvKeys[0]?.label}</span>
|
|
861
|
+
</CollapsibleTrigger>
|
|
862
|
+
<CollapsibleContent>
|
|
863
|
+
<div className="mt-2 space-y-3 rounded-xl border border-amber-500/30 bg-amber-500/5 p-4">
|
|
864
|
+
<p className="text-xs text-muted-foreground">
|
|
865
|
+
{legacyEnvKeys[0]?.helpText}
|
|
866
|
+
</p>
|
|
867
|
+
{legacyEnvKeys.map((envKey) => {
|
|
868
|
+
const envStatus = envStatusByKey.get(envKey.key);
|
|
869
|
+
const isConfigured = !!envStatus?.configured;
|
|
870
|
+
const helpText =
|
|
871
|
+
envKey.helpText ?? envStatus?.helpText;
|
|
872
|
+
const label =
|
|
873
|
+
envKey.label || envStatus?.label || envKey.key;
|
|
874
|
+
return (
|
|
875
|
+
<div key={envKey.key} className="space-y-1.5">
|
|
876
|
+
<div className="flex items-center justify-between gap-3">
|
|
877
|
+
<div className="flex items-center gap-1.5">
|
|
878
|
+
<label className="text-xs font-medium text-foreground">
|
|
879
|
+
{label}
|
|
880
|
+
</label>
|
|
881
|
+
{helpText ? (
|
|
882
|
+
<HelpTooltip content={helpText} />
|
|
883
|
+
) : null}
|
|
884
|
+
</div>
|
|
885
|
+
<StatusPill
|
|
886
|
+
tone={isConfigured ? "success" : "neutral"}
|
|
887
|
+
label={isConfigured ? "Saved" : "Not set"}
|
|
888
|
+
/>
|
|
889
|
+
</div>
|
|
890
|
+
{!isConfigured ? (
|
|
891
|
+
<Input
|
|
892
|
+
type="password"
|
|
893
|
+
value={envValues[envKey.key] || ""}
|
|
894
|
+
onChange={(event) =>
|
|
895
|
+
setEnvValues((current) => ({
|
|
896
|
+
...current,
|
|
897
|
+
[envKey.key]: event.target.value,
|
|
898
|
+
}))
|
|
899
|
+
}
|
|
900
|
+
placeholder={`Enter ${label}`}
|
|
901
|
+
autoComplete="off"
|
|
902
|
+
/>
|
|
903
|
+
) : null}
|
|
904
|
+
</div>
|
|
905
|
+
);
|
|
906
|
+
})}
|
|
907
|
+
{legacyEnvKeys.some(
|
|
908
|
+
(envKey) =>
|
|
909
|
+
!envStatusByKey.get(envKey.key)?.configured,
|
|
910
|
+
) ? (
|
|
911
|
+
<Button
|
|
912
|
+
variant="outline"
|
|
913
|
+
onClick={() =>
|
|
914
|
+
saveEnvKeys(
|
|
915
|
+
platform,
|
|
916
|
+
legacyEnvKeys.map((envKey) => envKey.key),
|
|
917
|
+
)
|
|
918
|
+
}
|
|
919
|
+
disabled={savingKeysFor === platform.id}
|
|
920
|
+
>
|
|
921
|
+
{savingKeysFor === platform.id
|
|
922
|
+
? "Saving..."
|
|
923
|
+
: "Save credentials"}
|
|
924
|
+
</Button>
|
|
925
|
+
) : null}
|
|
926
|
+
</div>
|
|
927
|
+
</CollapsibleContent>
|
|
928
|
+
</Collapsible>
|
|
929
|
+
) : null}
|
|
850
930
|
{missingRequiredCredentials ? (
|
|
851
931
|
<Button
|
|
852
932
|
variant="outline"
|