@growth-operations/ui-shared 1.27.0 → 1.29.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.27.0",
3
+ "version": "1.29.0",
4
4
  "description": "Shared HubSpot UI-extension modules (Home, Billing) for Growth Operations apps.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -61,16 +61,25 @@ function RecommendedTile({ notInUse, isSuperAdmin, onNavigate }) {
61
61
  <Flex direction="column" gap="small">
62
62
  <Text format={{ fontWeight: "bold" }}>Recommended to explore</Text>
63
63
  {picks.map((f) => {
64
- // Setup CTAs point at admin-only surfaces (the layout editor above
65
- // all) — show them to super admins only; everyone else gets name +
66
- // rationale alone (a non-admin clicking into a HubSpot permission
67
- // wall is worse than no link).
64
+ // Setup CTAs render for EVERYONE (1.29.0, Jasper 2026-10-14: "why
65
+ // gate so much on super admin?"). The old blanket super-admin gate
66
+ // assumed the targets were admin-only surfaces — but HubSpot
67
+ // permissions are granular (CRM customization ≠ super admin), and
68
+ // hiding the CTA kept non-admins from even knowing what to ask
69
+ // their admin for. adminOnly items instead carry a "(Super Admin)"
70
+ // hint for non-admin viewers, so the permission wall is never a
71
+ // blind click. The adminOnly marker still distinguishes the two
72
+ // surface kinds; old backends emit no marker → every item gets the
73
+ // hint for non-admins (conservative, and matches the old gate's
74
+ // assumption that the targets are admin surfaces).
68
75
  //
69
76
  // "Learn more →" (docs_url) intentionally NOT rendered (Jasper
70
77
  // 2026-10-14): every feature's docs_url points at the same single
71
78
  // setup guide — noise until per-feature articles exist. docs_url
72
79
  // stays in the wire contract; re-enabling is a one-line revert.
73
- const ctas = isSuperAdmin ? f.ctas : [];
80
+ const ctas = f.ctas;
81
+ const hint = (c) =>
82
+ c.adminOnly && !isSuperAdmin ? " (Super Admin)" : "";
74
83
  return (
75
84
  <Flex key={f.key} direction="column" gap="extra-small">
76
85
  <Text format={{ fontWeight: "demibold" }}>{f.name}</Text>
@@ -78,7 +87,8 @@ function RecommendedTile({ notInUse, isSuperAdmin, onNavigate }) {
78
87
  {ctas.map((cta) =>
79
88
  cta.url ? (
80
89
  <Link key={cta.label} href={{ url: cta.url, external: true }}>
81
- {cta.label} →
90
+ {cta.label}
91
+ {hint(cta)} →
82
92
  </Link>
83
93
  ) : onNavigate ? (
84
94
  <Button
@@ -87,6 +97,7 @@ function RecommendedTile({ notInUse, isSuperAdmin, onNavigate }) {
87
97
  onClick={() => onNavigate(cta.route)}
88
98
  >
89
99
  {cta.label}
100
+ {hint(cta)}
90
101
  </Button>
91
102
  ) : null
92
103
  )}
@@ -18,13 +18,17 @@ const normalizeUnit = (u) =>
18
18
  // One well-formed setup CTA is {label: non-empty string, url XOR route:
19
19
  // non-empty string}. Anything else (missing label, both targets, neither
20
20
  // target, wrong types) is dropped — a malformed item never renders.
21
+ // admin_only (1.28.0): an explicit false marks a per-user surface (installing
22
+ // the Chrome extension) that renders for EVERYONE; a missing marker means
23
+ // admin-only, so old backends keep the blanket super-admin gate.
21
24
  const normalizeCta = (c) => {
22
25
  if (!c || typeof c !== "object") return null;
23
26
  const label = typeof c.label === "string" && c.label ? c.label : null;
24
27
  const url = typeof c.url === "string" && c.url ? c.url : null;
25
28
  const route = typeof c.route === "string" && c.route ? c.route : null;
26
29
  if (!label || !(url || route) || (url && route)) return null;
27
- return url ? { label, url } : { label, route };
30
+ const adminOnly = c.admin_only !== false;
31
+ return { label, ...(url ? { url } : { route }), adminOnly };
28
32
  };
29
33
 
30
34
  // Resolve the feature_usage block into render-ready shape: