@growth-operations/ui-shared 1.23.0 → 1.24.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@growth-operations/ui-shared",
3
- "version": "1.23.0",
3
+ "version": "1.24.0",
4
4
  "description": "Shared HubSpot UI-extension modules (Home, Billing) for Growth Operations apps.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -159,7 +159,7 @@ export function AppHome({ state, onNavigate, appName, onRetry, helpfulLinks }) {
159
159
  on the non-billing setup steps (NOT the paid-gated plan step) so a
160
160
  fresh install sees setup first but free/trial installs aren't
161
161
  locked out. */}
162
- {setupStepsComplete && <FeatureUsage state={state} />}
162
+ {setupStepsComplete && <FeatureUsage state={state} onNavigate={onNavigate} />}
163
163
 
164
164
  <FeatureNudges nudges={state.nudges} onNavigate={onNavigate} />
165
165
 
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { Flex, Tile, Text, Link } from "@hubspot/ui-extensions";
2
+ import { Flex, Tile, Text, Link, Button } from "@hubspot/ui-extensions";
3
3
  import {
4
4
  featureUsageOf,
5
5
  pickDailyRecommendations,
@@ -46,7 +46,7 @@ function MostUsedTile({ mostUsed }) {
46
46
  );
47
47
  }
48
48
 
49
- function RecommendedTile({ notInUse }) {
49
+ function RecommendedTile({ notInUse, isSuperAdmin, onNavigate }) {
50
50
  if (notInUse.length === 0) return null;
51
51
  // Long lists (ATS: 13 features) render as a rotating daily slice instead of
52
52
  // a wall — the whole list still arrives in the payload; the window is a
@@ -59,17 +59,34 @@ function RecommendedTile({ notInUse }) {
59
59
  <Tile>
60
60
  <Flex direction="column" gap="small">
61
61
  <Text format={{ fontWeight: "bold" }}>Recommended to explore</Text>
62
- {picks.map((f) => (
63
- <Flex key={f.key} direction="column" gap="extra-small">
64
- <Text format={{ fontWeight: "demibold" }}>{f.name}</Text>
65
- {f.rationale ? <Text>{f.rationale}</Text> : null}
66
- {f.docsUrl ? (
67
- <Link href={{ url: f.docsUrl, external: true }}>
68
- Learn more →
69
- </Link>
70
- ) : null}
71
- </Flex>
72
- ))}
62
+ {picks.map((f) => {
63
+ // Setup CTAs point at admin-only surfaces (the layout editor above
64
+ // all) — show them to super admins only; everyone else keeps the
65
+ // docs link (a non-admin clicking into a HubSpot permission wall is
66
+ // worse than no link).
67
+ const showCta = isSuperAdmin && f.ctaLabel && (f.ctaUrl || f.ctaRoute);
68
+ return (
69
+ <Flex key={f.key} direction="column" gap="extra-small">
70
+ <Text format={{ fontWeight: "demibold" }}>{f.name}</Text>
71
+ {f.rationale ? <Text>{f.rationale}</Text> : null}
72
+ {showCta && f.ctaUrl ? (
73
+ <Link href={{ url: f.ctaUrl, external: true }}>
74
+ {f.ctaLabel} →
75
+ </Link>
76
+ ) : null}
77
+ {showCta && !f.ctaUrl && f.ctaRoute && onNavigate ? (
78
+ <Button variant="secondary" onClick={() => onNavigate(f.ctaRoute)}>
79
+ {f.ctaLabel}
80
+ </Button>
81
+ ) : null}
82
+ {f.docsUrl ? (
83
+ <Link href={{ url: f.docsUrl, external: true }}>
84
+ Learn more →
85
+ </Link>
86
+ ) : null}
87
+ </Flex>
88
+ );
89
+ })}
73
90
  {recommendationsRotated(notInUse) ? (
74
91
  <Text variant="microcopy">
75
92
  New picks daily — {notInUse.length} features to explore in all.
@@ -80,13 +97,17 @@ function RecommendedTile({ notInUse }) {
80
97
  );
81
98
  }
82
99
 
83
- export function FeatureUsage({ state }) {
100
+ export function FeatureUsage({ state, onNavigate }) {
84
101
  const view = featureUsageOf(state);
85
102
  if (!view) return null;
86
103
  return (
87
104
  <>
88
105
  <MostUsedTile mostUsed={view.mostUsed} />
89
- <RecommendedTile notInUse={view.notInUse} />
106
+ <RecommendedTile
107
+ notInUse={view.notInUse}
108
+ isSuperAdmin={state?.user?.is_super_admin === true}
109
+ onNavigate={onNavigate}
110
+ />
90
111
  </>
91
112
  );
92
113
  }
@@ -39,6 +39,12 @@ export function resolveFeatureUsage(featureUsage) {
39
39
  name: f.name,
40
40
  docsUrl: typeof f.docs_url === "string" && f.docs_url ? f.docs_url : null,
41
41
  rationale: typeof f.rationale === "string" ? f.rationale : "",
42
+ // Optional SETUP action (the same cta_* vocabulary as nudges): the "go
43
+ // do the thing" deep link for an unused feature — layout editor, store
44
+ // listing, or in-app route. Absent on backends that predate it.
45
+ ctaLabel: typeof f.cta_label === "string" && f.cta_label ? f.cta_label : null,
46
+ ctaUrl: typeof f.cta_url === "string" && f.cta_url ? f.cta_url : null,
47
+ ctaRoute: typeof f.cta_route === "string" && f.cta_route ? f.cta_route : null,
42
48
  }));
43
49
 
44
50
  if (mostUsed.length === 0 && notInUse.length === 0) return null;