@growth-operations/ui-shared 1.4.0 → 1.5.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.
- package/package.json +1 -1
- package/src/index.js +3 -0
- package/src/sandbox/SandboxInstall.jsx +89 -0
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -24,6 +24,9 @@ export { AlertsTab } from "./alerts/AlertsTab";
|
|
|
24
24
|
// Usage — the shared credit usage view (per-day LineChart + window stats).
|
|
25
25
|
export { UsageTab } from "./usage/UsageTab";
|
|
26
26
|
|
|
27
|
+
// Sandbox — the shared "Install on a sandbox" action (mints a signed link).
|
|
28
|
+
export { SandboxInstall } from "./sandbox/SandboxInstall";
|
|
29
|
+
|
|
27
30
|
// Lib helpers.
|
|
28
31
|
export {
|
|
29
32
|
fmtDate,
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// SandboxInstall — the shared "Install on a sandbox" action.
|
|
2
|
+
//
|
|
3
|
+
// The one sanctioned path to a sandbox install (a customer can't self-install on
|
|
4
|
+
// a sandbox from the marketplace — the backend rejects that). From a prod
|
|
5
|
+
// portal, this asks the app's mint endpoint for a signed sandbox install link
|
|
6
|
+
// (which carries sandbox=true + the prod customer to link), then surfaces it for
|
|
7
|
+
// the user to open on their sandbox account.
|
|
8
|
+
//
|
|
9
|
+
// Extensible per app: the caller passes the mint `path` on its OWN backend
|
|
10
|
+
// (base-hosted apps -> /v1/hubspot/app_pages/... no; the install mint lives at
|
|
11
|
+
// /v2/hubspot/install/{app}/sandbox-link on base; self-hosted apps serve their
|
|
12
|
+
// own), plus the portalId. callAppApi targets context.variables.BASE_URL, so
|
|
13
|
+
// each app's BASE_URL routes to the right service. Apps not allowlisted get a
|
|
14
|
+
// 403 from the endpoint, surfaced as a calm message.
|
|
15
|
+
import React, { useState } from "react";
|
|
16
|
+
import { Flex, Heading, Text, Button, Link, Alert } from "@hubspot/ui-extensions";
|
|
17
|
+
|
|
18
|
+
import { callAppApi, AppApiError } from "../sdk/app/base";
|
|
19
|
+
|
|
20
|
+
// context: extension context. path: the mint endpoint on the app's backend
|
|
21
|
+
// (e.g. `/v2/hubspot/install/${appKey}/sandbox-link`). portalId: the prod portal
|
|
22
|
+
// minting the link. appName: copy. heading/description: optional overrides.
|
|
23
|
+
export function SandboxInstall({
|
|
24
|
+
context,
|
|
25
|
+
path,
|
|
26
|
+
portalId,
|
|
27
|
+
appName = "this app",
|
|
28
|
+
heading = "Install on a sandbox",
|
|
29
|
+
description,
|
|
30
|
+
}) {
|
|
31
|
+
const [installUrl, setInstallUrl] = useState(null);
|
|
32
|
+
const [loading, setLoading] = useState(false);
|
|
33
|
+
const [error, setError] = useState(null);
|
|
34
|
+
|
|
35
|
+
const mint = async () => {
|
|
36
|
+
setLoading(true);
|
|
37
|
+
setError(null);
|
|
38
|
+
try {
|
|
39
|
+
const pid = portalId ?? context.portal.id;
|
|
40
|
+
const sep = path.includes("?") ? "&" : "?";
|
|
41
|
+
const res = await callAppApi(context, `${path}${sep}portalId=${pid}`, "POST");
|
|
42
|
+
setInstallUrl(res?.install_url ?? null);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
// The backend 403s apps that don't allow sandbox installs — show its
|
|
45
|
+
// message rather than a generic failure.
|
|
46
|
+
setError(err instanceof AppApiError ? err.message : String(err));
|
|
47
|
+
} finally {
|
|
48
|
+
setLoading(false);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Flex direction="column" gap="small">
|
|
54
|
+
<Heading>{heading}</Heading>
|
|
55
|
+
<Text>
|
|
56
|
+
{description ??
|
|
57
|
+
`Spin up ${appName} on a HubSpot sandbox account, linked to your billing. ` +
|
|
58
|
+
`Generate a link, then open it while signed in to your sandbox.`}
|
|
59
|
+
</Text>
|
|
60
|
+
|
|
61
|
+
{error && (
|
|
62
|
+
<Alert title="Couldn't create a sandbox link" variant="warning">
|
|
63
|
+
<Text>{error}</Text>
|
|
64
|
+
</Alert>
|
|
65
|
+
)}
|
|
66
|
+
|
|
67
|
+
{installUrl ? (
|
|
68
|
+
<Flex direction="column" gap="extra-small">
|
|
69
|
+
<Link href={{ url: installUrl, external: true }}>
|
|
70
|
+
Open the sandbox install →
|
|
71
|
+
</Link>
|
|
72
|
+
<Text variant="microcopy">
|
|
73
|
+
Open this while signed in to the sandbox account you want to install on.
|
|
74
|
+
</Text>
|
|
75
|
+
</Flex>
|
|
76
|
+
) : (
|
|
77
|
+
<Button
|
|
78
|
+
variant="secondary"
|
|
79
|
+
disabled={loading}
|
|
80
|
+
onClick={mint}
|
|
81
|
+
>
|
|
82
|
+
{loading ? "Generating…" : "Generate sandbox install link"}
|
|
83
|
+
</Button>
|
|
84
|
+
)}
|
|
85
|
+
</Flex>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export default SandboxInstall;
|