@growth-operations/ui-shared 1.30.0 → 1.31.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.30.0",
3
+ "version": "1.31.0",
4
4
  "description": "Shared HubSpot UI-extension modules (Home, Billing) for Growth Operations apps.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -7,6 +7,7 @@ import { SyncPulse } from "./SyncPulse";
7
7
  import { FeatureUsage } from "./FeatureUsage";
8
8
  import { PlanFitBanner } from "./PlanFitBanner";
9
9
  import { PeriodComparison } from "./PeriodComparison";
10
+ import { BillingPeriodUsage } from "../usage/BillingPeriodUsage";
10
11
  import { FeatureNudges } from "./FeatureNudges";
11
12
  import { HelpfulLinks } from "./HelpfulLinks";
12
13
 
@@ -53,7 +54,14 @@ function ArchetypeTile({ state }) {
53
54
  // (it owns context.portal.id + the app's docs/support); it can
54
55
  // also come from a state.helpful_links block if the backend
55
56
  // builds one.
56
- export function AppHome({ state, onNavigate, appName, onRetry, helpfulLinks }) {
57
+ // usageExplorer — optional { context, basePath, portalId }: swap the fixed
58
+ // last-vs-current PeriodComparison for the billing-period
59
+ // explorer chart (BillingPeriodUsage, Current/Last 2/3/6/12
60
+ // dropdown). Requires the host's usage endpoint to serve
61
+ // ?periods= (common v1.35.0+ backends). Jasper 2026-10-14:
62
+ // ATS Home uses this; the Usage tab keeps the rolling-days
63
+ // LineChart.
64
+ export function AppHome({ state, onNavigate, appName, onRetry, helpfulLinks, usageExplorer }) {
57
65
  if (!state) {
58
66
  return <LoadingSpinner showLabel label="Loading…" />;
59
67
  }
@@ -116,9 +124,25 @@ export function AppHome({ state, onNavigate, appName, onRetry, helpfulLinks }) {
116
124
 
117
125
  <ArchetypeTile state={state} />
118
126
 
119
- {/* Post-checklist: the period-over-period comparison (null until the
120
- install has one full closed period). */}
121
- {onboardingComplete && <PeriodComparison state={state} />}
127
+ {/* Post-checklist: the period chart. Default is the fixed
128
+ period-over-period comparison (null until the install has one full
129
+ closed period); usageExplorer hosts get the billing-period explorer
130
+ instead (it self-fetches, so it renders its own empty state rather
131
+ than gating on a closed period). */}
132
+ {onboardingComplete &&
133
+ (usageExplorer ? (
134
+ <BillingPeriodUsage
135
+ context={usageExplorer.context}
136
+ basePath={usageExplorer.basePath}
137
+ portalId={usageExplorer.portalId}
138
+ appName={appName}
139
+ title="Usage over time"
140
+ titleAs="text"
141
+ showStats={false}
142
+ />
143
+ ) : (
144
+ <PeriodComparison state={state} />
145
+ ))}
122
146
 
123
147
  {/* Most used / recommended-to-explore (state.feature_usage) — renders
124
148
  nothing on backends without the block or for legacy installs. Gated
@@ -76,7 +76,16 @@ export function makeUsageFetcher({ context, basePath = DEFAULT_BASE_PATH, portal
76
76
 
77
77
  // Hook-free presentational core (exported for tests): stats + stacked chart
78
78
  // from an already-loaded payload.
79
- export function BillingPeriodUsageView({ data, periods, appName, showStats }) {
79
+ export function BillingPeriodUsageView({
80
+ data,
81
+ periods,
82
+ appName,
83
+ showStats,
84
+ emptyTitle = "No usage in this window",
85
+ // Customer-facing default; internal hosts (the GOAA card) override — their
86
+ // viewer isn't the user, so "you run a metered action" reads wrong there.
87
+ emptyBody = null,
88
+ }) {
80
89
  const series = data?.series ?? [];
81
90
  const usedInWindow = data?.used_in_window ?? 0;
82
91
  const remaining = data?.remaining;
@@ -96,10 +105,10 @@ export function BillingPeriodUsageView({ data, periods, appName, showStats }) {
96
105
  )}
97
106
 
98
107
  {series.length === 0 ? (
99
- <EmptyState title="No usage in this window" layout="vertical">
108
+ <EmptyState title={emptyTitle} layout="vertical">
100
109
  <Text>
101
- Once you run a metered {appName} action, your credit usage shows
102
- here by billing period. Try a longer lookback.
110
+ {emptyBody ??
111
+ `Once you run a metered ${appName} action, your credit usage shows here by billing period. Try a longer lookback.`}
103
112
  </Text>
104
113
  </EmptyState>
105
114
  ) : (
@@ -133,6 +142,8 @@ export function BillingPeriodUsage({
133
142
  title = "Usage",
134
143
  titleAs = "heading", // "text" for CRM-card contexts (bold Text, not Heading)
135
144
  showStats = true,
145
+ emptyTitle,
146
+ emptyBody,
136
147
  }) {
137
148
  const [periods, setPeriods] = useState("1");
138
149
  const fetcher = fetchUsage ?? makeUsageFetcher({ context, basePath, portalId });
@@ -167,12 +178,14 @@ export function BillingPeriodUsage({
167
178
  fetchUsage={fetcher}
168
179
  appName={appName}
169
180
  showStats={showStats}
181
+ emptyTitle={emptyTitle}
182
+ emptyBody={emptyBody}
170
183
  />
171
184
  </Flex>
172
185
  );
173
186
  }
174
187
 
175
- function BillingPeriodContent({ periods, fetchUsage, appName, showStats }) {
188
+ function BillingPeriodContent({ periods, fetchUsage, appName, showStats, emptyTitle, emptyBody }) {
176
189
  const [data, setData] = useState(null);
177
190
  const [loading, setLoading] = useState(true);
178
191
  const [error, setError] = useState(null);
@@ -213,6 +226,8 @@ function BillingPeriodContent({ periods, fetchUsage, appName, showStats }) {
213
226
  periods={periods}
214
227
  appName={appName}
215
228
  showStats={showStats}
229
+ emptyTitle={emptyTitle}
230
+ emptyBody={emptyBody}
216
231
  />
217
232
  );
218
233
  }