@growth-operations/ui-shared 1.0.8 → 1.0.10

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@growth-operations/ui-shared",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "Shared HubSpot UI-extension modules (Home, Billing) for Growth Operations apps.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -6,6 +6,7 @@ import { OnboardingChecklist } from "./OnboardingChecklist";
6
6
  import { SyncPulse } from "./SyncPulse";
7
7
  import { CreditMeter } from "./CreditMeter";
8
8
  import { FeatureNudges } from "./FeatureNudges";
9
+ import { HelpfulLinks } from "./HelpfulLinks";
9
10
 
10
11
  // Minimal inline ATS tile (the `pipeline` block). Kept tiny here rather than a
11
12
  // separate file — it's the simplest of the three archetype tiles. Renders
@@ -76,7 +77,13 @@ function ArchetypeTile({ state }) {
76
77
  // PageLink navigation (the host owns the router).
77
78
  // appName — display name override; defaults to state.app.name.
78
79
  // onRetry — optional, passed to InstallProgress for the failed state.
79
- export function AppHome({ state, onNavigate, appName, onRetry }) {
80
+ // helpfulLinks — optional props for the standard HelpfulLinks section
81
+ // (e.g. { portalId, appId, appName, docsUrl, supportUrl,
82
+ // links }). Omit to hide the section. The host passes this
83
+ // (it owns context.portal.id + the app's docs/support); it can
84
+ // also come from a state.helpful_links block if the backend
85
+ // builds one.
86
+ export function AppHome({ state, onNavigate, appName, onRetry, helpfulLinks }) {
80
87
  if (!state) {
81
88
  return <LoadingSpinner showLabel label="Loading…" />;
82
89
  }
@@ -85,10 +92,21 @@ export function AppHome({ state, onNavigate, appName, onRetry }) {
85
92
  const activationComplete =
86
93
  !activation || activation.complete === true || activation.status === "completed";
87
94
 
95
+ // HelpfulLinks props: explicit prop wins, else a state.helpful_links block if
96
+ // the backend builds one. Shown in both the install and cockpit views so help
97
+ // is reachable even mid-setup.
98
+ const links = helpfulLinks ?? state.helpful_links ?? null;
99
+ const helpfulLinksEl = links ? <HelpfulLinks {...links} /> : null;
100
+
88
101
  // Install isn't finished (running / failed / completed-with-warnings) — show
89
102
  // the install experience instead of the cockpit.
90
103
  if (!activationComplete) {
91
- return <InstallProgress state={state} onRetry={onRetry} />;
104
+ return (
105
+ <Flex direction="column" gap="medium">
106
+ <InstallProgress state={state} onRetry={onRetry} />
107
+ {helpfulLinksEl}
108
+ </Flex>
109
+ );
92
110
  }
93
111
 
94
112
  const onboardingComplete = state.onboarding?.complete === true;
@@ -108,6 +126,8 @@ export function AppHome({ state, onNavigate, appName, onRetry }) {
108
126
  <ArchetypeTile state={state} />
109
127
 
110
128
  <FeatureNudges nudges={state.nudges} onNavigate={onNavigate} />
129
+
130
+ {helpfulLinksEl}
111
131
  </Flex>
112
132
  );
113
133
  }
@@ -0,0 +1,75 @@
1
+ import React from "react";
2
+ import { Flex, Text, Link, Divider } from "@hubspot/ui-extensions";
3
+
4
+ // Build the in-app app-pages deep link (the app's Home/tabs live under /app).
5
+ function appPagesUrl(portalId, appId, pagePath = "") {
6
+ const suffix = pagePath ? `/${pagePath.replace(/^\//, "")}` : "";
7
+ return `https://app.hubspot.com/app/${portalId}/${appId}${suffix}`;
8
+ }
9
+
10
+ // HelpfulLinks — a standard "Helpful Links" section for every Growth Operations
11
+ // app. Renders a labeled list of links: optionally an "Open <app>" deep link
12
+ // back to the app pages (so the settings extension isn't a dead end), docs +
13
+ // support from the app config, and any app-specific extras.
14
+ //
15
+ // Props:
16
+ // portalId — context.portal.id (number/string).
17
+ // appId — this app's HubSpot app id (per-env). Needed for the app-pages
18
+ // deep link; omit to hide that link.
19
+ // appName — display name, e.g. "Sparkfly" (used in the deep-link label).
20
+ // appPages — when true (default) and appId is set, show "Open <appName> →"
21
+ // linking to the app pages. Set the page path via appPagesPath.
22
+ // appPagesPath — optional pages route to deep-link into (default: Home).
23
+ // docsUrl, supportUrl — external help links (omit to hide).
24
+ // links — app-specific extras: [{ label, url }]. Rendered after the
25
+ // standard links.
26
+ // title — section heading (default "Helpful Links").
27
+ export function HelpfulLinks({
28
+ portalId,
29
+ appId,
30
+ appName = "the app",
31
+ appPages = true,
32
+ appPagesPath = "",
33
+ docsUrl,
34
+ supportUrl,
35
+ links = [],
36
+ title = "Helpful Links",
37
+ }) {
38
+ // external=false keeps navigation in the SAME tab. The app-pages deep link is
39
+ // an in-app HubSpot URL, so it must stay same-tab (external:true would force a
40
+ // new tab). Docs/support and app-specific extras are off-platform → external.
41
+ const items = [];
42
+
43
+ if (appPages && appId && portalId != null) {
44
+ items.push({
45
+ label: `Open ${appName} →`,
46
+ url: appPagesUrl(portalId, appId, appPagesPath),
47
+ external: false,
48
+ });
49
+ }
50
+ if (docsUrl) items.push({ label: "Documentation", url: docsUrl, external: true });
51
+ if (supportUrl) items.push({ label: "Support", url: supportUrl, external: true });
52
+ for (const l of links) {
53
+ if (l && l.url && l.label) {
54
+ // Default app-specific extras to external (they're usually HubSpot record
55
+ // URLs or off-platform), but let a link opt into same-tab via external:false.
56
+ items.push({ label: l.label, url: l.url, external: l.external !== false });
57
+ }
58
+ }
59
+
60
+ if (items.length === 0) return null;
61
+
62
+ return (
63
+ <Flex direction="column" gap="small">
64
+ <Text format={{ fontWeight: "bold" }}>{title}</Text>
65
+ <Divider />
66
+ {items.map((item) => (
67
+ <Link key={item.url} href={{ url: item.url, external: item.external }}>
68
+ {item.label}
69
+ </Link>
70
+ ))}
71
+ </Flex>
72
+ );
73
+ }
74
+
75
+ export default HelpfulLinks;
package/src/index.js CHANGED
@@ -12,6 +12,7 @@ export { OnboardingChecklist } from "./home/OnboardingChecklist";
12
12
  export { SyncPulse } from "./home/SyncPulse";
13
13
  export { FeatureNudges } from "./home/FeatureNudges";
14
14
  export { CreditMeter } from "./home/CreditMeter";
15
+ export { HelpfulLinks } from "./home/HelpfulLinks";
15
16
 
16
17
  // Billing.
17
18
  export { BillingTab } from "./billing/BillingTab";