@agent-native/dispatch 0.12.2 → 0.12.3
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/routes/pages/$appId.d.ts.map +1 -1
- package/dist/routes/pages/$appId.js +3 -2
- package/dist/routes/pages/$appId.js.map +1 -1
- package/dist/routes/pages/agents.d.ts.map +1 -1
- package/dist/routes/pages/agents.js +14 -8
- package/dist/routes/pages/agents.js.map +1 -1
- package/dist/routes/pages/apps.$appId.d.ts.map +1 -1
- package/dist/routes/pages/apps.$appId.js +3 -2
- package/dist/routes/pages/apps.$appId.js.map +1 -1
- package/dist/routes/pages/apps.d.ts.map +1 -1
- package/dist/routes/pages/apps.js +29 -11
- package/dist/routes/pages/apps.js.map +1 -1
- package/dist/routes/pages/destinations.d.ts.map +1 -1
- package/dist/routes/pages/destinations.js +18 -12
- package/dist/routes/pages/destinations.js.map +1 -1
- package/dist/routes/pages/metrics.d.ts.map +1 -1
- package/dist/routes/pages/metrics.js +8 -5
- package/dist/routes/pages/metrics.js.map +1 -1
- package/dist/routes/pages/new-app.d.ts.map +1 -1
- package/dist/routes/pages/new-app.js +3 -2
- package/dist/routes/pages/new-app.js.map +1 -1
- package/package.json +2 -2
- package/src/routes/pages/$appId.tsx +15 -12
- package/src/routes/pages/agents.tsx +21 -14
- package/src/routes/pages/apps.$appId.tsx +15 -14
- package/src/routes/pages/apps.tsx +41 -20
- package/src/routes/pages/destinations.tsx +35 -24
- package/src/routes/pages/metrics.tsx +13 -10
- package/src/routes/pages/new-app.tsx +4 -3
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
agentNativePath,
|
|
4
4
|
useActionMutation,
|
|
5
5
|
useActionQuery,
|
|
6
|
+
useT,
|
|
6
7
|
} from "@agent-native/core/client";
|
|
7
8
|
import { AgentsPanel, type ConnectedAgent } from "@/components/agents-panel";
|
|
8
9
|
import { DispatchShell } from "@/components/dispatch-shell";
|
|
@@ -39,12 +40,13 @@ function dispatchMcpUrl(): string {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
function DispatchMcpAccessPanel() {
|
|
43
|
+
const t = useT();
|
|
42
44
|
const { data, isLoading } = useActionQuery("list-mcp-app-access", {});
|
|
43
45
|
const [optimistic, setOptimistic] = useState<McpAccessState | null>(null);
|
|
44
46
|
const saveAccess = useActionMutation("set-mcp-app-access", {
|
|
45
47
|
onSuccess: () => {
|
|
46
48
|
setOptimistic(null);
|
|
47
|
-
toast.success("
|
|
49
|
+
toast.success(t("dispatch.pages.mcpAccessUpdated"));
|
|
48
50
|
},
|
|
49
51
|
onError: (error) => {
|
|
50
52
|
setOptimistic(null);
|
|
@@ -73,7 +75,7 @@ function DispatchMcpAccessPanel() {
|
|
|
73
75
|
|
|
74
76
|
function persist(next: McpAccessState) {
|
|
75
77
|
if (next.mode === "selected-apps" && next.selectedAppIds.length === 0) {
|
|
76
|
-
toast.error("
|
|
78
|
+
toast.error(t("dispatch.pages.selectAppForMcp"));
|
|
77
79
|
return;
|
|
78
80
|
}
|
|
79
81
|
setOptimistic(next);
|
|
@@ -90,9 +92,9 @@ function DispatchMcpAccessPanel() {
|
|
|
90
92
|
async function copyUrl() {
|
|
91
93
|
try {
|
|
92
94
|
await navigator.clipboard.writeText(mcpUrl);
|
|
93
|
-
toast.success("
|
|
95
|
+
toast.success(t("dispatch.pages.mcpUrlCopied"));
|
|
94
96
|
} catch {
|
|
95
|
-
toast.error("
|
|
97
|
+
toast.error(t("dispatch.pages.mcpUrlCopyFailed"));
|
|
96
98
|
}
|
|
97
99
|
}
|
|
98
100
|
|
|
@@ -102,21 +104,25 @@ function DispatchMcpAccessPanel() {
|
|
|
102
104
|
<div className="min-w-0">
|
|
103
105
|
<div className="flex items-center gap-2 text-sm font-medium text-foreground">
|
|
104
106
|
<IconPlugConnected size={16} />
|
|
105
|
-
|
|
107
|
+
{t("dispatch.pages.unifiedMcpGateway")}
|
|
106
108
|
</div>
|
|
107
109
|
<div className="mt-1 max-w-2xl text-sm text-muted-foreground">
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
{t("dispatch.pages.unifiedMcpGatewayDescription")}{" "}
|
|
111
|
+
<code>list_apps</code>, <code>ask_app</code>, and{" "}
|
|
112
|
+
<code>open_app</code>.
|
|
111
113
|
</div>
|
|
112
114
|
</div>
|
|
113
115
|
<div className="flex items-center gap-3 rounded-xl border px-3 py-2">
|
|
114
116
|
<div>
|
|
115
117
|
<div className="text-xs font-medium text-foreground">
|
|
116
|
-
{access.mode === "all-apps"
|
|
118
|
+
{access.mode === "all-apps"
|
|
119
|
+
? t("dispatch.pages.allApps")
|
|
120
|
+
: t("dispatch.pages.selectedApps")}
|
|
117
121
|
</div>
|
|
118
122
|
<div className="text-xs text-muted-foreground">
|
|
119
|
-
{isLoading
|
|
123
|
+
{isLoading
|
|
124
|
+
? t("dispatch.pages.loading")
|
|
125
|
+
: t("dispatch.pages.grantedCount", { count: grantedCount })}
|
|
120
126
|
</div>
|
|
121
127
|
</div>
|
|
122
128
|
<Switch
|
|
@@ -130,7 +136,7 @@ function DispatchMcpAccessPanel() {
|
|
|
130
136
|
: apps.map((app) => app.id),
|
|
131
137
|
})
|
|
132
138
|
}
|
|
133
|
-
aria-label="
|
|
139
|
+
aria-label={t("dispatch.pages.exposeAllAppsMcp")}
|
|
134
140
|
/>
|
|
135
141
|
</div>
|
|
136
142
|
</div>
|
|
@@ -139,7 +145,7 @@ function DispatchMcpAccessPanel() {
|
|
|
139
145
|
<Input readOnly value={mcpUrl} className="font-mono text-xs" />
|
|
140
146
|
<Button type="button" variant="outline" onClick={copyUrl}>
|
|
141
147
|
<IconCopy size={15} />
|
|
142
|
-
|
|
148
|
+
{t("dispatch.pages.copyUrl")}
|
|
143
149
|
</Button>
|
|
144
150
|
</div>
|
|
145
151
|
|
|
@@ -186,12 +192,13 @@ function DispatchMcpAccessPanel() {
|
|
|
186
192
|
}
|
|
187
193
|
|
|
188
194
|
export default function AgentsRoute() {
|
|
195
|
+
const t = useT();
|
|
189
196
|
const { data, refetch } = useActionQuery("list-connected-agents", {});
|
|
190
197
|
|
|
191
198
|
return (
|
|
192
199
|
<DispatchShell
|
|
193
|
-
title="
|
|
194
|
-
description="
|
|
200
|
+
title={t("dispatch.nav.agents")}
|
|
201
|
+
description={t("dispatch.pages.agentsDescription")}
|
|
195
202
|
>
|
|
196
203
|
<div className="space-y-4">
|
|
197
204
|
<DispatchMcpAccessPanel />
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useMemo } from "react";
|
|
2
2
|
import { Link, useParams } from "react-router";
|
|
3
|
-
import { useActionQuery } from "@agent-native/core/client";
|
|
3
|
+
import { useActionQuery, useT } from "@agent-native/core/client";
|
|
4
4
|
import {
|
|
5
5
|
IconArrowLeft,
|
|
6
6
|
IconArrowUpRight,
|
|
@@ -20,6 +20,7 @@ export function meta() {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export default function WorkspaceAppRoute() {
|
|
23
|
+
const t = useT();
|
|
23
24
|
const { appId } = useParams();
|
|
24
25
|
const { data: apps = [], isLoading } = useActionQuery(
|
|
25
26
|
"list-workspace-apps",
|
|
@@ -42,14 +43,14 @@ export default function WorkspaceAppRoute() {
|
|
|
42
43
|
|
|
43
44
|
return (
|
|
44
45
|
<DispatchShell
|
|
45
|
-
title={app?.name || "
|
|
46
|
-
description="
|
|
46
|
+
title={app?.name || t("dispatch.pages.workspaceAppFallback")}
|
|
47
|
+
description={t("dispatch.pages.workspaceAppDescription")}
|
|
47
48
|
>
|
|
48
49
|
<div className="max-w-2xl rounded-lg border bg-card p-5">
|
|
49
50
|
<Button asChild size="sm" variant="ghost" className="-ml-2 mb-4">
|
|
50
51
|
<Link to="/apps">
|
|
51
52
|
<IconArrowLeft size={15} className="mr-1.5" />
|
|
52
|
-
|
|
53
|
+
{t("dispatch.nav.apps")}
|
|
53
54
|
</Link>
|
|
54
55
|
</Button>
|
|
55
56
|
|
|
@@ -62,10 +63,10 @@ export default function WorkspaceAppRoute() {
|
|
|
62
63
|
) : !app ? (
|
|
63
64
|
<div className="space-y-3">
|
|
64
65
|
<h2 className="text-base font-semibold text-foreground">
|
|
65
|
-
|
|
66
|
+
{t("dispatch.pages.appNotFound")}
|
|
66
67
|
</h2>
|
|
67
68
|
<p className="text-sm text-muted-foreground">
|
|
68
|
-
|
|
69
|
+
{t("dispatch.pages.pageNotFoundDescription")}
|
|
69
70
|
</p>
|
|
70
71
|
</div>
|
|
71
72
|
) : app.status === "pending" ? (
|
|
@@ -79,23 +80,23 @@ export default function WorkspaceAppRoute() {
|
|
|
79
80
|
className="gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300"
|
|
80
81
|
>
|
|
81
82
|
<IconClockHour4 size={12} />
|
|
82
|
-
|
|
83
|
+
{t("dispatch.pages.building")}
|
|
83
84
|
</Badge>
|
|
84
85
|
</div>
|
|
85
86
|
<p className="text-sm text-muted-foreground">
|
|
86
|
-
|
|
87
|
+
{t("dispatch.pages.appBuildingPrefix")}{" "}
|
|
87
88
|
<span className="font-mono text-foreground">{app.path}</span>{" "}
|
|
88
|
-
|
|
89
|
+
{t("dispatch.pages.appBuildingSuffix")}
|
|
89
90
|
</p>
|
|
90
91
|
{app.branchName ? (
|
|
91
92
|
<p className="text-xs text-muted-foreground">
|
|
92
|
-
|
|
93
|
+
{t("dispatch.pages.branch", { branch: app.branchName })}
|
|
93
94
|
</p>
|
|
94
95
|
) : null}
|
|
95
96
|
{app.builderUrl ? (
|
|
96
97
|
<Button asChild>
|
|
97
98
|
<a href={app.builderUrl} target="_blank" rel="noreferrer">
|
|
98
|
-
|
|
99
|
+
{t("dispatch.pages.openBuilderBranch")}
|
|
99
100
|
<IconArrowUpRight size={15} className="ml-1.5" />
|
|
100
101
|
</a>
|
|
101
102
|
</Button>
|
|
@@ -104,16 +105,16 @@ export default function WorkspaceAppRoute() {
|
|
|
104
105
|
) : (
|
|
105
106
|
<div className="space-y-3">
|
|
106
107
|
<h2 className="text-base font-semibold text-foreground">
|
|
107
|
-
|
|
108
|
+
{t("dispatch.pages.openingApp", { name: app.name })}
|
|
108
109
|
</h2>
|
|
109
110
|
<p className="text-sm text-muted-foreground">
|
|
110
|
-
|
|
111
|
+
{t("dispatch.pages.redirectingTo")}{" "}
|
|
111
112
|
<span className="font-mono text-foreground">{app.path}</span>.
|
|
112
113
|
</p>
|
|
113
114
|
{href ? (
|
|
114
115
|
<Button asChild>
|
|
115
116
|
<a href={href}>
|
|
116
|
-
|
|
117
|
+
{t("dispatch.pages.openApp")}
|
|
117
118
|
<IconArrowUpRight size={15} className="ml-1.5" />
|
|
118
119
|
</a>
|
|
119
120
|
</Button>
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
useActionMutation,
|
|
4
|
+
useActionQuery,
|
|
5
|
+
useT,
|
|
6
|
+
} from "@agent-native/core/client";
|
|
3
7
|
import {
|
|
4
8
|
IconApps,
|
|
5
9
|
IconBrain,
|
|
@@ -71,6 +75,7 @@ const TEMPLATE_ICONS: Record<string, typeof IconMail> = {
|
|
|
71
75
|
};
|
|
72
76
|
|
|
73
77
|
export default function AppsRoute() {
|
|
78
|
+
const t = useT();
|
|
74
79
|
const [showHidden, setShowHidden] = useState(false);
|
|
75
80
|
const [templatesOpen, setTemplatesOpen] = useState(false);
|
|
76
81
|
const { data: apps = [], isLoading: appsLoading } = useActionQuery(
|
|
@@ -103,11 +108,13 @@ export default function AppsRoute() {
|
|
|
103
108
|
|
|
104
109
|
return (
|
|
105
110
|
<DispatchShell
|
|
106
|
-
title="
|
|
111
|
+
title={t("dispatch.nav.apps")}
|
|
107
112
|
description={
|
|
108
113
|
workspaceLabel
|
|
109
|
-
?
|
|
110
|
-
|
|
114
|
+
? t("dispatch.pages.appsDescriptionWithWorkspace", {
|
|
115
|
+
workspace: workspaceLabel,
|
|
116
|
+
})
|
|
117
|
+
: t("dispatch.pages.appsDescription")
|
|
111
118
|
}
|
|
112
119
|
>
|
|
113
120
|
<div className="space-y-8">
|
|
@@ -121,13 +128,19 @@ export default function AppsRoute() {
|
|
|
121
128
|
<div className="min-w-0">
|
|
122
129
|
<h2 className="truncate text-sm font-semibold text-foreground">
|
|
123
130
|
{workspaceLabel
|
|
124
|
-
?
|
|
125
|
-
|
|
131
|
+
? t("dispatch.pages.appsInWorkspace", {
|
|
132
|
+
workspace: workspaceLabel,
|
|
133
|
+
})
|
|
134
|
+
: t("dispatch.pages.workspaceApps")}
|
|
126
135
|
</h2>
|
|
127
136
|
<p className="mt-0.5 text-xs text-muted-foreground">
|
|
128
|
-
{
|
|
137
|
+
{t("dispatch.pages.activeCount", {
|
|
138
|
+
count: visibleApps.length,
|
|
139
|
+
})}
|
|
129
140
|
{archivedApps.length > 0
|
|
130
|
-
? ` · ${
|
|
141
|
+
? ` · ${t("dispatch.pages.hiddenCount", {
|
|
142
|
+
count: archivedApps.length,
|
|
143
|
+
})}`
|
|
131
144
|
: ""}
|
|
132
145
|
</p>
|
|
133
146
|
</div>
|
|
@@ -138,7 +151,7 @@ export default function AppsRoute() {
|
|
|
138
151
|
trigger={
|
|
139
152
|
<Button size="sm">
|
|
140
153
|
<IconPlus size={15} className="mr-1.5" />
|
|
141
|
-
|
|
154
|
+
{t("dispatch.pages.createApp")}
|
|
142
155
|
</Button>
|
|
143
156
|
}
|
|
144
157
|
/>
|
|
@@ -169,12 +182,14 @@ export default function AppsRoute() {
|
|
|
169
182
|
/>
|
|
170
183
|
<div className="min-w-0">
|
|
171
184
|
<h2 className="text-sm font-semibold text-foreground">
|
|
172
|
-
|
|
185
|
+
{t("dispatch.pages.templates")}
|
|
173
186
|
</h2>
|
|
174
187
|
<p className="text-xs text-muted-foreground">
|
|
175
188
|
{templatesLoading
|
|
176
|
-
? "
|
|
177
|
-
:
|
|
189
|
+
? t("dispatch.pages.checkingTemplates")
|
|
190
|
+
: t("dispatch.pages.templatesAvailable", {
|
|
191
|
+
count: typedTemplates.length,
|
|
192
|
+
})}
|
|
178
193
|
</p>
|
|
179
194
|
</div>
|
|
180
195
|
</div>
|
|
@@ -185,7 +200,9 @@ export default function AppsRoute() {
|
|
|
185
200
|
size="sm"
|
|
186
201
|
className="gap-1.5"
|
|
187
202
|
>
|
|
188
|
-
{templatesOpen
|
|
203
|
+
{templatesOpen
|
|
204
|
+
? t("dispatch.pages.hide")
|
|
205
|
+
: t("dispatch.pages.show")}
|
|
189
206
|
<IconChevronDown
|
|
190
207
|
size={14}
|
|
191
208
|
className={cn(
|
|
@@ -225,11 +242,12 @@ export default function AppsRoute() {
|
|
|
225
242
|
/>
|
|
226
243
|
<div className="min-w-0">
|
|
227
244
|
<h2 className="text-sm font-semibold text-foreground">
|
|
228
|
-
|
|
245
|
+
{t("dispatch.pages.hiddenApps")}
|
|
229
246
|
</h2>
|
|
230
247
|
<p className="text-xs text-muted-foreground">
|
|
231
|
-
{
|
|
232
|
-
|
|
248
|
+
{t("dispatch.pages.hiddenAppCount", {
|
|
249
|
+
count: archivedApps.length,
|
|
250
|
+
})}
|
|
233
251
|
</p>
|
|
234
252
|
</div>
|
|
235
253
|
</div>
|
|
@@ -240,7 +258,9 @@ export default function AppsRoute() {
|
|
|
240
258
|
size="sm"
|
|
241
259
|
className="gap-1.5"
|
|
242
260
|
>
|
|
243
|
-
{showHidden
|
|
261
|
+
{showHidden
|
|
262
|
+
? t("dispatch.pages.hide")
|
|
263
|
+
: t("dispatch.pages.show")}
|
|
244
264
|
<IconChevronDown
|
|
245
265
|
size={14}
|
|
246
266
|
className={cn(
|
|
@@ -293,23 +313,24 @@ function AppsSkeletonGrid() {
|
|
|
293
313
|
}
|
|
294
314
|
|
|
295
315
|
function EmptyAppsState() {
|
|
316
|
+
const t = useT();
|
|
296
317
|
return (
|
|
297
318
|
<div className="rounded-lg border border-dashed bg-card px-4 py-10 text-center">
|
|
298
319
|
<div className="mx-auto flex size-10 items-center justify-center rounded-lg bg-muted text-muted-foreground">
|
|
299
320
|
<IconApps size={18} />
|
|
300
321
|
</div>
|
|
301
322
|
<h3 className="mt-3 text-sm font-semibold text-foreground">
|
|
302
|
-
|
|
323
|
+
{t("dispatch.pages.noWorkspaceApps")}
|
|
303
324
|
</h3>
|
|
304
325
|
<p className="mx-auto mt-1 max-w-sm text-sm text-muted-foreground">
|
|
305
|
-
|
|
326
|
+
{t("dispatch.pages.noWorkspaceAppsDescription")}
|
|
306
327
|
</p>
|
|
307
328
|
<div className="mt-4">
|
|
308
329
|
<CreateAppPopover
|
|
309
330
|
trigger={
|
|
310
331
|
<Button size="sm">
|
|
311
332
|
<IconPlus size={15} className="mr-1.5" />
|
|
312
|
-
|
|
333
|
+
{t("dispatch.pages.createApp")}
|
|
313
334
|
</Button>
|
|
314
335
|
}
|
|
315
336
|
/>
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
useActionMutation,
|
|
4
|
+
useActionQuery,
|
|
5
|
+
useT,
|
|
6
|
+
} from "@agent-native/core/client";
|
|
3
7
|
import { toast } from "sonner";
|
|
4
8
|
import { DispatchShell } from "@/components/dispatch-shell";
|
|
5
9
|
import { Button } from "@/components/ui/button";
|
|
@@ -33,15 +37,18 @@ function QuickSendRow({
|
|
|
33
37
|
}: {
|
|
34
38
|
destination: { id: string; name: string };
|
|
35
39
|
}) {
|
|
40
|
+
const t = useT();
|
|
36
41
|
const [text, setText] = useState("");
|
|
37
42
|
const send = useActionMutation("send-platform-message", {
|
|
38
43
|
onSuccess: () => {
|
|
39
|
-
toast.success("
|
|
44
|
+
toast.success(t("dispatch.pages.messageSent"));
|
|
40
45
|
setText("");
|
|
41
46
|
},
|
|
42
47
|
onError: (error) => {
|
|
43
48
|
toast.error(
|
|
44
|
-
error instanceof Error
|
|
49
|
+
error instanceof Error
|
|
50
|
+
? error.message
|
|
51
|
+
: t("dispatch.pages.unableToSendMessage"),
|
|
45
52
|
);
|
|
46
53
|
},
|
|
47
54
|
});
|
|
@@ -50,7 +57,7 @@ function QuickSendRow({
|
|
|
50
57
|
<Input
|
|
51
58
|
value={text}
|
|
52
59
|
onChange={(event) => setText(event.target.value)}
|
|
53
|
-
placeholder="
|
|
60
|
+
placeholder={t("dispatch.pages.quickTestMessage")}
|
|
54
61
|
/>
|
|
55
62
|
<Button
|
|
56
63
|
onClick={() =>
|
|
@@ -61,13 +68,14 @@ function QuickSendRow({
|
|
|
61
68
|
}
|
|
62
69
|
disabled={send.isPending}
|
|
63
70
|
>
|
|
64
|
-
|
|
71
|
+
{t("dispatch.pages.send")}
|
|
65
72
|
</Button>
|
|
66
73
|
</div>
|
|
67
74
|
);
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
export default function DestinationsRoute() {
|
|
78
|
+
const t = useT();
|
|
71
79
|
const { data } = useActionQuery("list-destinations", {});
|
|
72
80
|
const [form, setForm] = useState({
|
|
73
81
|
name: "",
|
|
@@ -79,7 +87,7 @@ export default function DestinationsRoute() {
|
|
|
79
87
|
|
|
80
88
|
const upsert = useActionMutation("upsert-destination", {
|
|
81
89
|
onSuccess: () => {
|
|
82
|
-
toast.success("
|
|
90
|
+
toast.success(t("dispatch.pages.destinationSaved"));
|
|
83
91
|
setForm((current) => ({
|
|
84
92
|
...current,
|
|
85
93
|
name: "",
|
|
@@ -90,18 +98,18 @@ export default function DestinationsRoute() {
|
|
|
90
98
|
},
|
|
91
99
|
});
|
|
92
100
|
const remove = useActionMutation("delete-destination", {
|
|
93
|
-
onSuccess: () => toast.success("
|
|
101
|
+
onSuccess: () => toast.success(t("dispatch.pages.destinationRemoved")),
|
|
94
102
|
});
|
|
95
103
|
|
|
96
104
|
return (
|
|
97
105
|
<DispatchShell
|
|
98
|
-
title="
|
|
99
|
-
description="
|
|
106
|
+
title={t("dispatch.nav.destinations")}
|
|
107
|
+
description={t("dispatch.pages.destinationsDescription")}
|
|
100
108
|
>
|
|
101
109
|
<div className="grid gap-4 xl:grid-cols-[minmax(0,1.1fr)_minmax(0,0.9fr)]">
|
|
102
110
|
<section className="rounded-2xl border bg-card p-5">
|
|
103
111
|
<h2 className="text-lg font-semibold text-foreground">
|
|
104
|
-
|
|
112
|
+
{t("dispatch.pages.savedDestinations")}
|
|
105
113
|
</h2>
|
|
106
114
|
<div className="mt-4 space-y-3">
|
|
107
115
|
{(data || []).map((destination: any) => (
|
|
@@ -129,24 +137,28 @@ export default function DestinationsRoute() {
|
|
|
129
137
|
<AlertDialog>
|
|
130
138
|
<AlertDialogTrigger asChild>
|
|
131
139
|
<Button variant="outline" size="sm">
|
|
132
|
-
|
|
140
|
+
{t("dispatch.pages.delete")}
|
|
133
141
|
</Button>
|
|
134
142
|
</AlertDialogTrigger>
|
|
135
143
|
<AlertDialogContent>
|
|
136
144
|
<AlertDialogHeader>
|
|
137
|
-
<AlertDialogTitle>
|
|
145
|
+
<AlertDialogTitle>
|
|
146
|
+
{t("dispatch.pages.deleteDestinationTitle")}
|
|
147
|
+
</AlertDialogTitle>
|
|
138
148
|
<AlertDialogDescription>
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
149
|
+
{t("dispatch.pages.deleteDestinationDescription", {
|
|
150
|
+
name: destination.name,
|
|
151
|
+
})}
|
|
142
152
|
</AlertDialogDescription>
|
|
143
153
|
</AlertDialogHeader>
|
|
144
154
|
<AlertDialogFooter>
|
|
145
|
-
<AlertDialogCancel>
|
|
155
|
+
<AlertDialogCancel>
|
|
156
|
+
{t("dispatch.pages.cancel")}
|
|
157
|
+
</AlertDialogCancel>
|
|
146
158
|
<AlertDialogAction
|
|
147
159
|
onClick={() => remove.mutate({ id: destination.id })}
|
|
148
160
|
>
|
|
149
|
-
|
|
161
|
+
{t("dispatch.pages.delete")}
|
|
150
162
|
</AlertDialogAction>
|
|
151
163
|
</AlertDialogFooter>
|
|
152
164
|
</AlertDialogContent>
|
|
@@ -157,8 +169,7 @@ export default function DestinationsRoute() {
|
|
|
157
169
|
))}
|
|
158
170
|
{(data?.length || 0) === 0 && (
|
|
159
171
|
<div className="rounded-xl border border-dashed px-4 py-8 text-sm text-muted-foreground">
|
|
160
|
-
|
|
161
|
-
Telegram chat on the right.
|
|
172
|
+
{t("dispatch.pages.noDestinations")}
|
|
162
173
|
</div>
|
|
163
174
|
)}
|
|
164
175
|
</div>
|
|
@@ -166,7 +177,7 @@ export default function DestinationsRoute() {
|
|
|
166
177
|
|
|
167
178
|
<section className="rounded-2xl border bg-card p-5">
|
|
168
179
|
<h2 className="text-lg font-semibold text-foreground">
|
|
169
|
-
|
|
180
|
+
{t("dispatch.pages.addDestination")}
|
|
170
181
|
</h2>
|
|
171
182
|
<div className="mt-4 space-y-3">
|
|
172
183
|
<Input
|
|
@@ -174,7 +185,7 @@ export default function DestinationsRoute() {
|
|
|
174
185
|
onChange={(event) =>
|
|
175
186
|
setForm((current) => ({ ...current, name: event.target.value }))
|
|
176
187
|
}
|
|
177
|
-
placeholder="
|
|
188
|
+
placeholder={t("dispatch.pages.dailyDigestChannel")}
|
|
178
189
|
/>
|
|
179
190
|
<Select
|
|
180
191
|
value={form.platform}
|
|
@@ -218,7 +229,7 @@ export default function DestinationsRoute() {
|
|
|
218
229
|
threadRef: event.target.value,
|
|
219
230
|
}))
|
|
220
231
|
}
|
|
221
|
-
placeholder="
|
|
232
|
+
placeholder={t("dispatch.pages.optionalThreadOrTopicId")}
|
|
222
233
|
/>
|
|
223
234
|
<Textarea
|
|
224
235
|
value={form.notes}
|
|
@@ -228,7 +239,7 @@ export default function DestinationsRoute() {
|
|
|
228
239
|
notes: event.target.value,
|
|
229
240
|
}))
|
|
230
241
|
}
|
|
231
|
-
placeholder="
|
|
242
|
+
placeholder={t("dispatch.pages.destinationNotes")}
|
|
232
243
|
/>
|
|
233
244
|
<Button
|
|
234
245
|
className="w-full"
|
|
@@ -243,7 +254,7 @@ export default function DestinationsRoute() {
|
|
|
243
254
|
}
|
|
244
255
|
disabled={!form.name || !form.destination}
|
|
245
256
|
>
|
|
246
|
-
|
|
257
|
+
{t("dispatch.pages.saveDestination")}
|
|
247
258
|
</Button>
|
|
248
259
|
</div>
|
|
249
260
|
</section>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useMemo, useState, type ReactNode } from "react";
|
|
2
|
-
import { useActionQuery } from "@agent-native/core/client";
|
|
2
|
+
import { useActionQuery, useT } from "@agent-native/core/client";
|
|
3
3
|
import {
|
|
4
4
|
IconActivity,
|
|
5
5
|
IconAlertTriangle,
|
|
@@ -637,6 +637,7 @@ function RecentTable({
|
|
|
637
637
|
}
|
|
638
638
|
|
|
639
639
|
export default function MetricsRoute() {
|
|
640
|
+
const t = useT();
|
|
640
641
|
const [sinceDays, setSinceDays] = useState(30);
|
|
641
642
|
const { data, isLoading, error } = useActionQuery(
|
|
642
643
|
"list-dispatch-usage-metrics",
|
|
@@ -657,11 +658,11 @@ export default function MetricsRoute() {
|
|
|
657
658
|
|
|
658
659
|
return (
|
|
659
660
|
<DispatchShell
|
|
660
|
-
title="
|
|
661
|
+
title={t("dispatch.nav.metrics")}
|
|
661
662
|
description={
|
|
662
663
|
billing.unit === "builder-credits"
|
|
663
|
-
? "
|
|
664
|
-
: "
|
|
664
|
+
? t("dispatch.pages.metricsDescriptionBuilder")
|
|
665
|
+
: t("dispatch.pages.metricsDescriptionLlm")
|
|
665
666
|
}
|
|
666
667
|
>
|
|
667
668
|
<div className="space-y-4">
|
|
@@ -677,9 +678,11 @@ export default function MetricsRoute() {
|
|
|
677
678
|
{error ? (
|
|
678
679
|
<Alert variant="destructive">
|
|
679
680
|
<IconAlertTriangle className="h-4 w-4" />
|
|
680
|
-
<AlertTitle>
|
|
681
|
+
<AlertTitle>{t("dispatch.pages.metricsUnavailable")}</AlertTitle>
|
|
681
682
|
<AlertDescription>
|
|
682
|
-
{error instanceof Error
|
|
683
|
+
{error instanceof Error
|
|
684
|
+
? error.message
|
|
685
|
+
: t("dispatch.pages.unableToLoadUsage")}
|
|
683
686
|
</AlertDescription>
|
|
684
687
|
</Alert>
|
|
685
688
|
) : null}
|
|
@@ -696,25 +699,25 @@ export default function MetricsRoute() {
|
|
|
696
699
|
icon={<IconCoin size={17} />}
|
|
697
700
|
/>
|
|
698
701
|
<MetricCard
|
|
699
|
-
label="
|
|
702
|
+
label={t("dispatch.pages.llmCalls")}
|
|
700
703
|
value={formatNumber(metrics.totals.calls)}
|
|
701
704
|
detail={`${formatNumber(metrics.totals.chatCalls)} chat turns`}
|
|
702
705
|
icon={<IconActivity size={17} />}
|
|
703
706
|
/>
|
|
704
707
|
<MetricCard
|
|
705
|
-
label="
|
|
708
|
+
label={t("dispatch.pages.activeUsers")}
|
|
706
709
|
value={formatNumber(metrics.totals.activeUsers)}
|
|
707
710
|
detail={`${formatNumber(metrics.access.totalUsers)} users with access`}
|
|
708
711
|
icon={<IconUsersGroup size={17} />}
|
|
709
712
|
/>
|
|
710
713
|
<MetricCard
|
|
711
|
-
label="
|
|
714
|
+
label={t("dispatch.pages.workspaceAppsStat")}
|
|
712
715
|
value={formatNumber(metrics.totals.workspaceApps)}
|
|
713
716
|
detail={`${formatNumber(metrics.byApp.length)} with usage`}
|
|
714
717
|
icon={<IconApps size={17} />}
|
|
715
718
|
/>
|
|
716
719
|
<MetricCard
|
|
717
|
-
label="
|
|
720
|
+
label={t("dispatch.pages.chatThreads")}
|
|
718
721
|
value={formatNumber(metrics.totals.chatThreads)}
|
|
719
722
|
detail={`${formatNumber(metrics.totals.chatMessages)} messages`}
|
|
720
723
|
icon={<IconMessages size={17} />}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NewWorkspaceAppFlow } from "@agent-native/core/client";
|
|
1
|
+
import { NewWorkspaceAppFlow, useT } from "@agent-native/core/client";
|
|
2
2
|
import { DispatchShell } from "@/components/dispatch-shell";
|
|
3
3
|
|
|
4
4
|
export function meta() {
|
|
@@ -6,10 +6,11 @@ export function meta() {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export default function NewAppRoute() {
|
|
9
|
+
const t = useT();
|
|
9
10
|
return (
|
|
10
11
|
<DispatchShell
|
|
11
|
-
title="
|
|
12
|
-
description="
|
|
12
|
+
title={t("dispatch.pages.newApp")}
|
|
13
|
+
description={t("dispatch.pages.newAppDescription")}
|
|
13
14
|
>
|
|
14
15
|
<NewWorkspaceAppFlow sourceApp="dispatch" className="px-0 py-0" />
|
|
15
16
|
</DispatchShell>
|