@agent-native/dispatch 0.17.3 → 0.17.5
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/create-workspace-resource.d.ts +13 -13
- package/dist/actions/delete-workspace-resource.d.ts +13 -13
- package/dist/actions/provider-api-register.d.ts +10 -10
- package/dist/client/transactional-emails.d.ts +57 -0
- package/dist/client/transactional-emails.d.ts.map +1 -0
- package/dist/client/transactional-emails.js +116 -0
- package/dist/client/transactional-emails.js.map +1 -0
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +8 -1
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/components/transactional-email-activity.d.ts +24 -0
- package/dist/components/transactional-email-activity.d.ts.map +1 -0
- package/dist/components/transactional-email-activity.js +31 -0
- package/dist/components/transactional-email-activity.js.map +1 -0
- package/dist/components/transactional-email-metrics.d.ts +40 -0
- package/dist/components/transactional-email-metrics.d.ts.map +1 -0
- package/dist/components/transactional-email-metrics.js +44 -0
- package/dist/components/transactional-email-metrics.js.map +1 -0
- package/dist/components/transactional-email-preview.d.ts +7 -0
- package/dist/components/transactional-email-preview.d.ts.map +1 -0
- package/dist/components/transactional-email-preview.js +41 -0
- package/dist/components/transactional-email-preview.js.map +1 -0
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +2 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/pages/transactional-email.$appId.$id.d.ts +5 -0
- package/dist/routes/pages/transactional-email.$appId.$id.d.ts.map +1 -0
- package/dist/routes/pages/transactional-email.$appId.$id.js +144 -0
- package/dist/routes/pages/transactional-email.$appId.$id.js.map +1 -0
- package/dist/routes/pages/transactional-email.d.ts +5 -0
- package/dist/routes/pages/transactional-email.d.ts.map +1 -0
- package/dist/routes/pages/transactional-email.js +139 -0
- package/dist/routes/pages/transactional-email.js.map +1 -0
- package/dist/server/lib/thread-debug-store.d.ts.map +1 -1
- package/dist/server/lib/thread-debug-store.js +2 -1
- package/dist/server/lib/thread-debug-store.js.map +1 -1
- package/package.json +2 -2
- package/src/client/transactional-emails.spec.ts +91 -0
- package/src/client/transactional-emails.ts +201 -0
- package/src/components/layout/Layout.tsx +8 -0
- package/src/components/transactional-email-activity.tsx +102 -0
- package/src/components/transactional-email-metrics.tsx +127 -0
- package/src/components/transactional-email-preview.tsx +88 -0
- package/src/routes/index.ts +5 -0
- package/src/routes/pages/transactional-email.$appId.$id.tsx +362 -0
- package/src/routes/pages/transactional-email.tsx +571 -0
- package/src/server/lib/thread-debug-store.spec.ts +7 -3
- package/src/server/lib/thread-debug-store.ts +2 -1
|
@@ -6,19 +6,6 @@ declare const _default: import("@agent-native/core").ActionDefinition<{
|
|
|
6
6
|
content: string;
|
|
7
7
|
scope: "all" | "selected";
|
|
8
8
|
}, {
|
|
9
|
-
id: string;
|
|
10
|
-
ownerEmail: string;
|
|
11
|
-
orgId: string | null;
|
|
12
|
-
kind: string;
|
|
13
|
-
name: string;
|
|
14
|
-
description: string | null;
|
|
15
|
-
path: string;
|
|
16
|
-
content: string;
|
|
17
|
-
scope: string;
|
|
18
|
-
createdBy: string;
|
|
19
|
-
createdAt: number;
|
|
20
|
-
updatedAt: number;
|
|
21
|
-
} | {
|
|
22
9
|
id: string;
|
|
23
10
|
ownerEmail: string;
|
|
24
11
|
orgId: string | null;
|
|
@@ -35,6 +22,19 @@ declare const _default: import("@agent-native/core").ActionDefinition<{
|
|
|
35
22
|
reviewedAt: number | null;
|
|
36
23
|
createdAt: number;
|
|
37
24
|
updatedAt: number;
|
|
25
|
+
} | {
|
|
26
|
+
id: string;
|
|
27
|
+
ownerEmail: string;
|
|
28
|
+
orgId: string | null;
|
|
29
|
+
kind: string;
|
|
30
|
+
name: string;
|
|
31
|
+
description: string | null;
|
|
32
|
+
path: string;
|
|
33
|
+
content: string;
|
|
34
|
+
scope: string;
|
|
35
|
+
createdBy: string;
|
|
36
|
+
createdAt: number;
|
|
37
|
+
updatedAt: number;
|
|
38
38
|
}>;
|
|
39
39
|
export default _default;
|
|
40
40
|
//# sourceMappingURL=create-workspace-resource.d.ts.map
|
|
@@ -1,19 +1,6 @@
|
|
|
1
1
|
declare const _default: import("@agent-native/core").ActionDefinition<{
|
|
2
2
|
id: string;
|
|
3
3
|
}, {
|
|
4
|
-
id: string;
|
|
5
|
-
ownerEmail: string;
|
|
6
|
-
orgId: string | null;
|
|
7
|
-
kind: string;
|
|
8
|
-
name: string;
|
|
9
|
-
description: string | null;
|
|
10
|
-
path: string;
|
|
11
|
-
content: string;
|
|
12
|
-
scope: string;
|
|
13
|
-
createdBy: string;
|
|
14
|
-
createdAt: number;
|
|
15
|
-
updatedAt: number;
|
|
16
|
-
} | {
|
|
17
4
|
id: string;
|
|
18
5
|
ownerEmail: string;
|
|
19
6
|
orgId: string | null;
|
|
@@ -30,6 +17,19 @@ declare const _default: import("@agent-native/core").ActionDefinition<{
|
|
|
30
17
|
reviewedAt: number | null;
|
|
31
18
|
createdAt: number;
|
|
32
19
|
updatedAt: number;
|
|
20
|
+
} | {
|
|
21
|
+
id: string;
|
|
22
|
+
ownerEmail: string;
|
|
23
|
+
orgId: string | null;
|
|
24
|
+
kind: string;
|
|
25
|
+
name: string;
|
|
26
|
+
description: string | null;
|
|
27
|
+
path: string;
|
|
28
|
+
content: string;
|
|
29
|
+
scope: string;
|
|
30
|
+
createdBy: string;
|
|
31
|
+
createdAt: number;
|
|
32
|
+
updatedAt: number;
|
|
33
33
|
}>;
|
|
34
34
|
export default _default;
|
|
35
35
|
//# sourceMappingURL=delete-workspace-resource.d.ts.map
|
|
@@ -47,10 +47,9 @@ declare const _default: import("node_modules/@agent-native/core/dist").ActionDef
|
|
|
47
47
|
notes?: string | undefined;
|
|
48
48
|
scope?: "org" | "user" | undefined;
|
|
49
49
|
}, {
|
|
50
|
-
id?: undefined;
|
|
51
|
-
provider?: undefined;
|
|
52
50
|
deleted?: undefined;
|
|
53
51
|
found?: undefined;
|
|
52
|
+
message?: undefined;
|
|
54
53
|
providers: {
|
|
55
54
|
id: string;
|
|
56
55
|
label: string;
|
|
@@ -61,45 +60,46 @@ declare const _default: import("node_modules/@agent-native/core/dist").ActionDef
|
|
|
61
60
|
updatedAt: number;
|
|
62
61
|
}[];
|
|
63
62
|
count: number;
|
|
63
|
+
provider?: undefined;
|
|
64
64
|
registered?: undefined;
|
|
65
|
+
id?: undefined;
|
|
65
66
|
label?: undefined;
|
|
66
|
-
message?: undefined;
|
|
67
67
|
} | {
|
|
68
|
-
id?: undefined;
|
|
69
68
|
deleted?: undefined;
|
|
69
|
+
message?: undefined;
|
|
70
70
|
providers?: undefined;
|
|
71
71
|
count?: undefined;
|
|
72
72
|
found: boolean;
|
|
73
73
|
provider: import("node_modules/@agent-native/core/dist/provider-api").CustomProviderConfig;
|
|
74
74
|
registered?: undefined;
|
|
75
|
+
id?: undefined;
|
|
75
76
|
label?: undefined;
|
|
76
|
-
message?: undefined;
|
|
77
77
|
} | {
|
|
78
|
-
provider?: undefined;
|
|
79
78
|
deleted?: undefined;
|
|
79
|
+
message?: undefined;
|
|
80
80
|
providers?: undefined;
|
|
81
81
|
count?: undefined;
|
|
82
|
+
provider?: undefined;
|
|
82
83
|
found: boolean;
|
|
83
84
|
id: string;
|
|
84
85
|
registered?: undefined;
|
|
85
86
|
label?: undefined;
|
|
86
|
-
message?: undefined;
|
|
87
87
|
} | {
|
|
88
|
-
provider?: undefined;
|
|
89
88
|
found?: undefined;
|
|
89
|
+
message?: undefined;
|
|
90
90
|
providers?: undefined;
|
|
91
91
|
count?: undefined;
|
|
92
|
+
provider?: undefined;
|
|
92
93
|
deleted: boolean;
|
|
93
94
|
id: string;
|
|
94
95
|
registered?: undefined;
|
|
95
96
|
label?: undefined;
|
|
96
|
-
message?: undefined;
|
|
97
97
|
} | {
|
|
98
|
-
provider?: undefined;
|
|
99
98
|
deleted?: undefined;
|
|
100
99
|
found?: undefined;
|
|
101
100
|
providers?: undefined;
|
|
102
101
|
count?: undefined;
|
|
102
|
+
provider?: undefined;
|
|
103
103
|
registered: boolean;
|
|
104
104
|
id: string;
|
|
105
105
|
label: string;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads each mounted app's transactional email catalog from the browser.
|
|
3
|
+
*
|
|
4
|
+
* Workspace apps are path-mounted on a single origin, so the browser can call
|
|
5
|
+
* another app's action endpoint directly and its own session cookie carries the
|
|
6
|
+
* caller's identity. Fanning out here rather than server-side keeps every app's
|
|
7
|
+
* existing access checks in force — Dispatch never sees a catalog the signed-in
|
|
8
|
+
* user could not have loaded themselves.
|
|
9
|
+
*/
|
|
10
|
+
export interface AppTransactionalEmail {
|
|
11
|
+
id: string;
|
|
12
|
+
app: string;
|
|
13
|
+
name: string;
|
|
14
|
+
trigger: string;
|
|
15
|
+
recipient: string;
|
|
16
|
+
recipientLabel: string;
|
|
17
|
+
sender: string;
|
|
18
|
+
senderLabel: string;
|
|
19
|
+
/** null when the send log could not be read — not the same as zero sends. */
|
|
20
|
+
sent: number | null;
|
|
21
|
+
failed: number | null;
|
|
22
|
+
lastSentAt: number | null;
|
|
23
|
+
}
|
|
24
|
+
export interface AppEmailCatalog {
|
|
25
|
+
appId: string;
|
|
26
|
+
appName: string;
|
|
27
|
+
appPath: string;
|
|
28
|
+
emails: AppTransactionalEmail[];
|
|
29
|
+
/** Set when this app's catalog could not be read at all. */
|
|
30
|
+
error: string | null;
|
|
31
|
+
/** Set when the catalog loaded but its send counts did not. */
|
|
32
|
+
statsError: string | null;
|
|
33
|
+
}
|
|
34
|
+
export interface LocalTransactionalEmailCatalog {
|
|
35
|
+
app: string;
|
|
36
|
+
statsAvailable: boolean;
|
|
37
|
+
statsError: string | null;
|
|
38
|
+
emails: AppTransactionalEmail[];
|
|
39
|
+
}
|
|
40
|
+
export declare function aggregateSharedEmails(local: LocalTransactionalEmailCatalog, appCatalogs: AppEmailCatalog[]): {
|
|
41
|
+
emails: AppTransactionalEmail[];
|
|
42
|
+
statsError: string | null;
|
|
43
|
+
};
|
|
44
|
+
export declare function callAppAction<T>(appPath: string, action: string, params: Record<string, unknown>, method: "GET" | "POST"): Promise<T>;
|
|
45
|
+
export declare function fetchAppEmailCatalog(app: {
|
|
46
|
+
id: string;
|
|
47
|
+
name: string;
|
|
48
|
+
path: string;
|
|
49
|
+
}, windowDays: number): Promise<AppEmailCatalog>;
|
|
50
|
+
export interface EmailPreview {
|
|
51
|
+
subject: string;
|
|
52
|
+
html: string;
|
|
53
|
+
text: string;
|
|
54
|
+
}
|
|
55
|
+
/** Render one email's preview with dummy data, from the owning app. */
|
|
56
|
+
export declare function fetchEmailPreview(appPath: string, id: string): Promise<EmailPreview>;
|
|
57
|
+
//# sourceMappingURL=transactional-emails.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactional-emails.d.ts","sourceRoot":"","sources":["../../src/client/transactional-emails.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAChC,4DAA4D;IAC5D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,+DAA+D;IAC/D,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,8BAA8B;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,OAAO,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,qBAAqB,EAAE,CAAC;CACjC;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,8BAA8B,EACrC,WAAW,EAAE,eAAe,EAAE,GAC7B;IAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CA4ChE;AAOD,wBAAsB,aAAa,CAAC,CAAC,EACnC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,MAAM,EAAE,KAAK,GAAG,MAAM,GACrB,OAAO,CAAC,CAAC,CAAC,CAmBZ;AASD,wBAAsB,oBAAoB,CACxC,GAAG,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAC/C,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,eAAe,CAAC,CA0C1B;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,uEAAuE;AACvE,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,YAAY,CAAC,CAavB"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads each mounted app's transactional email catalog from the browser.
|
|
3
|
+
*
|
|
4
|
+
* Workspace apps are path-mounted on a single origin, so the browser can call
|
|
5
|
+
* another app's action endpoint directly and its own session cookie carries the
|
|
6
|
+
* caller's identity. Fanning out here rather than server-side keeps every app's
|
|
7
|
+
* existing access checks in force — Dispatch never sees a catalog the signed-in
|
|
8
|
+
* user could not have loaded themselves.
|
|
9
|
+
*/
|
|
10
|
+
export function aggregateSharedEmails(local, appCatalogs) {
|
|
11
|
+
const catalogs = [
|
|
12
|
+
{
|
|
13
|
+
appId: local.app,
|
|
14
|
+
appName: local.app,
|
|
15
|
+
appPath: "",
|
|
16
|
+
emails: local.emails,
|
|
17
|
+
error: null,
|
|
18
|
+
statsError: local.statsAvailable ? null : local.statsError,
|
|
19
|
+
},
|
|
20
|
+
...appCatalogs.filter((catalog) => catalog.appId !== local.app),
|
|
21
|
+
];
|
|
22
|
+
const unreadable = catalogs.find((catalog) => catalog.error || catalog.statsError);
|
|
23
|
+
const statsAvailable = !unreadable;
|
|
24
|
+
return {
|
|
25
|
+
statsError: unreadable?.error ?? unreadable?.statsError ?? null,
|
|
26
|
+
emails: local.emails
|
|
27
|
+
.filter((email) => email.app === "core")
|
|
28
|
+
.map((definition) => {
|
|
29
|
+
if (!statsAvailable) {
|
|
30
|
+
return { ...definition, sent: null, failed: null, lastSentAt: null };
|
|
31
|
+
}
|
|
32
|
+
let sent = 0;
|
|
33
|
+
let failed = 0;
|
|
34
|
+
let lastSentAt = null;
|
|
35
|
+
for (const catalog of catalogs) {
|
|
36
|
+
const email = catalog.emails.find((candidate) => candidate.id === definition.id);
|
|
37
|
+
sent += email?.sent ?? 0;
|
|
38
|
+
failed += email?.failed ?? 0;
|
|
39
|
+
if (email?.lastSentAt != null &&
|
|
40
|
+
(lastSentAt === null || email.lastSentAt > lastSentAt)) {
|
|
41
|
+
lastSentAt = email.lastSentAt;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { ...definition, sent, failed, lastSentAt };
|
|
45
|
+
}),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function actionUrl(appPath, action, query = "") {
|
|
49
|
+
const base = appPath.replace(/\/$/, "");
|
|
50
|
+
return `${base}/_agent-native/actions/${action}${query}`;
|
|
51
|
+
}
|
|
52
|
+
export async function callAppAction(appPath, action, params, method) {
|
|
53
|
+
const query = method === "GET"
|
|
54
|
+
? `?${new URLSearchParams(Object.entries(params).map(([key, value]) => [key, String(value)]))}`
|
|
55
|
+
: "";
|
|
56
|
+
const res = await fetch(actionUrl(appPath, action, query), {
|
|
57
|
+
method,
|
|
58
|
+
credentials: "include",
|
|
59
|
+
headers: {
|
|
60
|
+
Accept: "application/json",
|
|
61
|
+
"Content-Type": "application/json",
|
|
62
|
+
"X-Agent-Native-Frontend": "1",
|
|
63
|
+
},
|
|
64
|
+
...(method === "POST" ? { body: JSON.stringify(params) } : {}),
|
|
65
|
+
});
|
|
66
|
+
if (!res.ok)
|
|
67
|
+
throw new Error(`${action} failed: HTTP ${res.status}`);
|
|
68
|
+
return (await res.json());
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Load one app's catalog. Never throws — a single unreachable app must not
|
|
72
|
+
* blank the whole screen, but its failure is returned rather than swallowed so
|
|
73
|
+
* the UI can say "couldn't read" instead of showing it as having no emails.
|
|
74
|
+
*/
|
|
75
|
+
const CATALOG_TIMEOUT_MS = 10_000;
|
|
76
|
+
export async function fetchAppEmailCatalog(app, windowDays) {
|
|
77
|
+
const base = {
|
|
78
|
+
appId: app.id,
|
|
79
|
+
appName: app.name,
|
|
80
|
+
appPath: app.path,
|
|
81
|
+
emails: [],
|
|
82
|
+
error: null,
|
|
83
|
+
statsError: null,
|
|
84
|
+
};
|
|
85
|
+
try {
|
|
86
|
+
const res = await fetch(actionUrl(app.path, "list-transactional-emails", `?windowDays=${windowDays}`), {
|
|
87
|
+
credentials: "include",
|
|
88
|
+
headers: { Accept: "application/json" },
|
|
89
|
+
signal: AbortSignal.timeout(CATALOG_TIMEOUT_MS),
|
|
90
|
+
});
|
|
91
|
+
if (!res.ok) {
|
|
92
|
+
return { ...base, error: `HTTP ${res.status}` };
|
|
93
|
+
}
|
|
94
|
+
const body = (await res.json());
|
|
95
|
+
return {
|
|
96
|
+
...base,
|
|
97
|
+
emails: body.emails ?? [],
|
|
98
|
+
statsError: body.statsAvailable === false ? (body.statsError ?? "unknown") : null,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
return {
|
|
103
|
+
...base,
|
|
104
|
+
error: error instanceof Error ? error.message : String(error),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** Render one email's preview with dummy data, from the owning app. */
|
|
109
|
+
export async function fetchEmailPreview(appPath, id) {
|
|
110
|
+
const res = await fetch(actionUrl(appPath, "render-transactional-email-preview", `?id=${encodeURIComponent(id)}`), { credentials: "include", headers: { Accept: "application/json" } });
|
|
111
|
+
if (!res.ok) {
|
|
112
|
+
throw new Error(`Preview failed: HTTP ${res.status}`);
|
|
113
|
+
}
|
|
114
|
+
return (await res.json());
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=transactional-emails.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactional-emails.js","sourceRoot":"","sources":["../../src/client/transactional-emails.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Reads each mounted app's transactional email catalog from the browser.\n *\n * Workspace apps are path-mounted on a single origin, so the browser can call\n * another app's action endpoint directly and its own session cookie carries the\n * caller's identity. Fanning out here rather than server-side keeps every app's\n * existing access checks in force — Dispatch never sees a catalog the signed-in\n * user could not have loaded themselves.\n */\n\nexport interface AppTransactionalEmail {\n id: string;\n app: string;\n name: string;\n trigger: string;\n recipient: string;\n recipientLabel: string;\n sender: string;\n senderLabel: string;\n /** null when the send log could not be read — not the same as zero sends. */\n sent: number | null;\n failed: number | null;\n lastSentAt: number | null;\n}\n\nexport interface AppEmailCatalog {\n appId: string;\n appName: string;\n appPath: string;\n emails: AppTransactionalEmail[];\n /** Set when this app's catalog could not be read at all. */\n error: string | null;\n /** Set when the catalog loaded but its send counts did not. */\n statsError: string | null;\n}\n\nexport interface LocalTransactionalEmailCatalog {\n app: string;\n statsAvailable: boolean;\n statsError: string | null;\n emails: AppTransactionalEmail[];\n}\n\nexport function aggregateSharedEmails(\n local: LocalTransactionalEmailCatalog,\n appCatalogs: AppEmailCatalog[],\n): { emails: AppTransactionalEmail[]; statsError: string | null } {\n const catalogs = [\n {\n appId: local.app,\n appName: local.app,\n appPath: \"\",\n emails: local.emails,\n error: null,\n statsError: local.statsAvailable ? null : local.statsError,\n },\n ...appCatalogs.filter((catalog) => catalog.appId !== local.app),\n ];\n const unreadable = catalogs.find(\n (catalog) => catalog.error || catalog.statsError,\n );\n const statsAvailable = !unreadable;\n\n return {\n statsError: unreadable?.error ?? unreadable?.statsError ?? null,\n emails: local.emails\n .filter((email) => email.app === \"core\")\n .map((definition) => {\n if (!statsAvailable) {\n return { ...definition, sent: null, failed: null, lastSentAt: null };\n }\n let sent = 0;\n let failed = 0;\n let lastSentAt: number | null = null;\n for (const catalog of catalogs) {\n const email = catalog.emails.find(\n (candidate) => candidate.id === definition.id,\n );\n sent += email?.sent ?? 0;\n failed += email?.failed ?? 0;\n if (\n email?.lastSentAt != null &&\n (lastSentAt === null || email.lastSentAt > lastSentAt)\n ) {\n lastSentAt = email.lastSentAt;\n }\n }\n return { ...definition, sent, failed, lastSentAt };\n }),\n };\n}\n\nfunction actionUrl(appPath: string, action: string, query = \"\"): string {\n const base = appPath.replace(/\\/$/, \"\");\n return `${base}/_agent-native/actions/${action}${query}`;\n}\n\nexport async function callAppAction<T>(\n appPath: string,\n action: string,\n params: Record<string, unknown>,\n method: \"GET\" | \"POST\",\n): Promise<T> {\n const query =\n method === \"GET\"\n ? `?${new URLSearchParams(\n Object.entries(params).map(([key, value]) => [key, String(value)]),\n )}`\n : \"\";\n const res = await fetch(actionUrl(appPath, action, query), {\n method,\n credentials: \"include\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n \"X-Agent-Native-Frontend\": \"1\",\n },\n ...(method === \"POST\" ? { body: JSON.stringify(params) } : {}),\n });\n if (!res.ok) throw new Error(`${action} failed: HTTP ${res.status}`);\n return (await res.json()) as T;\n}\n\n/**\n * Load one app's catalog. Never throws — a single unreachable app must not\n * blank the whole screen, but its failure is returned rather than swallowed so\n * the UI can say \"couldn't read\" instead of showing it as having no emails.\n */\nconst CATALOG_TIMEOUT_MS = 10_000;\n\nexport async function fetchAppEmailCatalog(\n app: { id: string; name: string; path: string },\n windowDays: number,\n): Promise<AppEmailCatalog> {\n const base: AppEmailCatalog = {\n appId: app.id,\n appName: app.name,\n appPath: app.path,\n emails: [],\n error: null,\n statsError: null,\n };\n try {\n const res = await fetch(\n actionUrl(\n app.path,\n \"list-transactional-emails\",\n `?windowDays=${windowDays}`,\n ),\n {\n credentials: \"include\",\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(CATALOG_TIMEOUT_MS),\n },\n );\n if (!res.ok) {\n return { ...base, error: `HTTP ${res.status}` };\n }\n const body = (await res.json()) as {\n emails?: AppTransactionalEmail[];\n statsAvailable?: boolean;\n statsError?: string | null;\n };\n return {\n ...base,\n emails: body.emails ?? [],\n statsError:\n body.statsAvailable === false ? (body.statsError ?? \"unknown\") : null,\n };\n } catch (error) {\n return {\n ...base,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nexport interface EmailPreview {\n subject: string;\n html: string;\n text: string;\n}\n\n/** Render one email's preview with dummy data, from the owning app. */\nexport async function fetchEmailPreview(\n appPath: string,\n id: string,\n): Promise<EmailPreview> {\n const res = await fetch(\n actionUrl(\n appPath,\n \"render-transactional-email-preview\",\n `?id=${encodeURIComponent(id)}`,\n ),\n { credentials: \"include\", headers: { Accept: \"application/json\" } },\n );\n if (!res.ok) {\n throw new Error(`Preview failed: HTTP ${res.status}`);\n }\n return (await res.json()) as EmailPreview;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Layout.d.ts","sourceRoot":"","sources":["../../../src/components/layout/Layout.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Layout.d.ts","sourceRoot":"","sources":["../../../src/components/layout/Layout.tsx"],"names":[],"mappings":"AA+CA,OAAO,EAKL,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAgBf,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,YAAY,CAAC;AAE1D,MAAM,MAAM,eAAe,GAAG,aAAa,CAAC;IAC1C,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAEH,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,EAAE,EAAE,MAAM,CAAC;IACX,iFAAiF;IACjF,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,mFAAmF;IACnF,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,gEAAgE;IAChE,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;CACvC;AAED,MAAM,WAAW,uBAAuB;IACtC,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACtC,kFAAkF;IAClF,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B;AAkOD,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,SAAa,UAWlE;AAmPD,wBAAgB,UAAU,CAAC,EACzB,UAAU,EACV,UAAU,EACV,SAAiB,EACjB,WAAmB,EACnB,iBAAiB,GAClB,EAAE;IACD,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,UAAU,CAAC,EAAE,uBAAuB,CAAC;IACrC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;CAClD,mDA8RA;AAED,wBAAgB,MAAM,CAAC,EACrB,QAAQ,EACR,UAAU,EACV,aAAa,GACd,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,uBAAuB,CAAC;IACrC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,mDA6IA"}
|
|
@@ -7,7 +7,7 @@ import { InvitationBanner, OrgSwitcher } from "@agent-native/core/client/org";
|
|
|
7
7
|
import { FeedbackButton } from "@agent-native/core/client/ui";
|
|
8
8
|
import { SidebarFooterActions } from "@agent-native/toolkit/app-shell";
|
|
9
9
|
import { ChatHistoryRail, } from "@agent-native/toolkit/chat-history";
|
|
10
|
-
import { IconActivity, IconArrowUpRight, IconApps, IconBrain, IconBrandSlack, IconChartBar, IconBrandTelegram, IconKey, IconChevronDown, IconLayersSubtract, IconMessageQuestion, IconMessages, IconPlugConnected, IconBroadcast, IconFingerprint, IconHistory, IconLayoutSidebarLeftCollapse, IconLayoutSidebarLeftExpand, IconPuzzle, IconSettings, IconSettingsAutomation, IconShieldCheck, IconSearch, IconWorld, IconDeviceDesktop, } from "@tabler/icons-react";
|
|
10
|
+
import { IconActivity, IconArrowUpRight, IconApps, IconBrain, IconBrandSlack, IconChartBar, IconBrandTelegram, IconKey, IconChevronDown, IconLayersSubtract, IconMail, IconMessageQuestion, IconMessages, IconPlugConnected, IconBroadcast, IconFingerprint, IconHistory, IconLayoutSidebarLeftCollapse, IconLayoutSidebarLeftExpand, IconPuzzle, IconSettings, IconSettingsAutomation, IconShieldCheck, IconSearch, IconWorld, IconDeviceDesktop, } from "@tabler/icons-react";
|
|
11
11
|
import { useEffect, useMemo, useRef, useState, } from "react";
|
|
12
12
|
import { NavLink, useLocation, useNavigate } from "react-router";
|
|
13
13
|
import { cn } from "../../lib/utils.js";
|
|
@@ -82,6 +82,13 @@ const OPERATIONS_NAV_ITEMS = [
|
|
|
82
82
|
icon: IconPuzzle,
|
|
83
83
|
section: "operations",
|
|
84
84
|
},
|
|
85
|
+
{
|
|
86
|
+
id: "transactional-email",
|
|
87
|
+
to: "/transactional-email",
|
|
88
|
+
label: "Transactional email",
|
|
89
|
+
icon: IconMail,
|
|
90
|
+
section: "operations",
|
|
91
|
+
},
|
|
85
92
|
{
|
|
86
93
|
id: "vault",
|
|
87
94
|
to: "/vault",
|