@streamoid/ui 0.6.16 → 0.6.18
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/README.md +35 -18
- package/dist/docs/AGENTS.md +321 -0
- package/dist/docs/CreditWarningBanner.md +305 -0
- package/dist/docs/InvoiceHistoryMobile.md +222 -0
- package/dist/docs/ScAccess.md +259 -0
- package/dist/docs/ScAppCard.md +244 -0
- package/dist/docs/ScAppCardForCopilot.md +230 -0
- package/dist/docs/ScAppCardV3.md +273 -0
- package/dist/docs/ScAppField.md +308 -0
- package/dist/docs/ScAppListingCard.md +271 -0
- package/dist/docs/ScAppSwitchPanel.md +286 -0
- package/dist/docs/ScAppcardLogos.md +226 -0
- package/dist/docs/ScArtifaxInvite.md +262 -0
- package/dist/docs/ScArtifaxSidebar.md +330 -0
- package/dist/docs/ScAskAgentButton.md +307 -0
- package/dist/docs/ScBadges.md +261 -0
- package/dist/docs/ScBeacon.md +244 -0
- package/dist/docs/ScBillingHistoryHeader.md +210 -0
- package/dist/docs/ScBillingHistoryTableList.md +243 -0
- package/dist/docs/ScBillingLogsTableHeader.md +212 -0
- package/dist/docs/ScBillingLogsTableList.md +251 -0
- package/dist/docs/ScBriefCard.md +255 -0
- package/dist/docs/ScButton.md +251 -0
- package/dist/docs/ScCalendar.md +268 -0
- package/dist/docs/ScCalendarDateComps.md +264 -0
- package/dist/docs/ScCatalogixInvite.md +345 -0
- package/dist/docs/ScCatalogixSidebar.md +337 -0
- package/dist/docs/ScCatalogixStoreHeader.md +246 -0
- package/dist/docs/ScCatalogixStoreTableList.md +316 -0
- package/dist/docs/ScCheckField.md +233 -0
- package/dist/docs/ScCheckbox.md +272 -0
- package/dist/docs/ScCounter.md +235 -0
- package/dist/docs/ScCreditsUsageCard.md +247 -0
- package/dist/docs/ScCreditsUsageCardMobile.md +224 -0
- package/dist/docs/ScDefaultCard.md +269 -0
- package/dist/docs/ScDp.md +245 -0
- package/dist/docs/ScDrawer.md +318 -0
- package/dist/docs/ScFieldButton.md +255 -0
- package/dist/docs/ScFileField.md +268 -0
- package/dist/docs/ScGoogleSignIn.md +250 -0
- package/dist/docs/ScGuide.md +278 -0
- package/dist/docs/ScHDivider.md +213 -0
- package/dist/docs/ScHeader.md +222 -0
- package/dist/docs/ScImageField.md +253 -0
- package/dist/docs/ScInChatList.md +277 -0
- package/dist/docs/ScInChatMessage.md +205 -0
- package/dist/docs/ScInfoPopup.md +248 -0
- package/dist/docs/ScIntialProfileCover.md +233 -0
- package/dist/docs/ScInvoiceHistoryMobile.md +187 -0
- package/dist/docs/ScLogoUnit.md +232 -0
- package/dist/docs/ScMappingCard.md +241 -0
- package/dist/docs/ScMediaApproval.md +301 -0
- package/dist/docs/ScMediaSelect.md +310 -0
- package/dist/docs/ScMenuOptions.md +308 -0
- package/dist/docs/ScMobileBottomAction.md +252 -0
- package/dist/docs/ScMobileTopNav.md +279 -0
- package/dist/docs/ScModal.md +291 -0
- package/dist/docs/ScOnlyField.md +302 -0
- package/dist/docs/ScOnlyIcon.md +213 -0
- package/dist/docs/ScPagination.md +284 -0
- package/dist/docs/ScPairtext.md +287 -0
- package/dist/docs/ScPendingAction.md +238 -0
- package/dist/docs/ScPhtogenixInvite.md +275 -0
- package/dist/docs/ScPlanCard.md +302 -0
- package/dist/docs/ScPlanComparison.md +264 -0
- package/dist/docs/ScPlanDetailsCard.md +246 -0
- package/dist/docs/ScPlanDetailsCardMobile.md +240 -0
- package/dist/docs/ScPopUpMenu.md +224 -0
- package/dist/docs/ScProfile.md +234 -0
- package/dist/docs/ScProfileImageUpdate.md +261 -0
- package/dist/docs/ScProfileOptions.md +245 -0
- package/dist/docs/ScProfilePopup.md +396 -0
- package/dist/docs/ScProfileSettingsComp.md +250 -0
- package/dist/docs/ScProfileV2Mobile.md +216 -0
- package/dist/docs/ScProgressBar.md +267 -0
- package/dist/docs/ScQuickPrompt.md +277 -0
- package/dist/docs/ScRadio.md +228 -0
- package/dist/docs/ScReferralCardMobile.md +226 -0
- package/dist/docs/ScReferralTableHeader.md +260 -0
- package/dist/docs/ScReferralTableList.md +293 -0
- package/dist/docs/ScRole.md +226 -0
- package/dist/docs/ScRoleMobile.md +199 -0
- package/dist/docs/ScSelect.md +270 -0
- package/dist/docs/ScSelection.md +256 -0
- package/dist/docs/ScSelectionList.md +272 -0
- package/dist/docs/ScSelectionPill.md +240 -0
- package/dist/docs/ScSelectionPillGroup.md +302 -0
- package/dist/docs/ScSettingsNav.md +212 -0
- package/dist/docs/ScSettingsTabComp.md +260 -0
- package/dist/docs/ScSideBarLogoUnit.md +340 -0
- package/dist/docs/ScSidebar.md +243 -0
- package/dist/docs/ScSidebarIcons.md +232 -0
- package/dist/docs/ScSidebarMenu.md +283 -0
- package/dist/docs/ScSidebarProfile.md +231 -0
- package/dist/docs/ScSidebarSwitchMenu.md +258 -0
- package/dist/docs/ScSlider.md +194 -0
- package/dist/docs/ScStoreCard.md +252 -0
- package/dist/docs/ScStrLogo.md +253 -0
- package/dist/docs/ScStreamoidWordmark.md +302 -0
- package/dist/docs/ScSubAgent.md +226 -0
- package/dist/docs/ScTabComp.md +308 -0
- package/dist/docs/ScTabField.md +258 -0
- package/dist/docs/ScTabSwitcher.md +307 -0
- package/dist/docs/ScTableHeader.md +261 -0
- package/dist/docs/ScTableList.md +301 -0
- package/dist/docs/ScTableListMobile.md +282 -0
- package/dist/docs/ScTabs.md +268 -0
- package/dist/docs/ScTaxonomyPill.md +263 -0
- package/dist/docs/ScTextArea.md +259 -0
- package/dist/docs/ScTextField.md +324 -0
- package/dist/docs/ScThinkingStepIcon.md +249 -0
- package/dist/docs/ScTodoList.md +288 -0
- package/dist/docs/ScToggleSwitch.md +229 -0
- package/dist/docs/ScUsageHistoryMobile.md +194 -0
- package/dist/docs/ScVDivider.md +215 -0
- package/dist/docs/ScValueMappingL1.md +256 -0
- package/dist/docs/ScVersion.md +251 -0
- package/dist/docs/ScWorkspace.md +233 -0
- package/dist/docs/ScWorkspaceCard.md +234 -0
- package/dist/docs/ScWorkspaceSettingsMobile.md +265 -0
- package/dist/docs/ScWorkspaceSwitchCard.md +314 -0
- package/dist/docs/ScWorkspaceSwitchMobile.md +241 -0
- package/dist/docs/ScWorkspaceSwitchMobileV2.md +278 -0
- package/dist/docs/StreamoidSidebar.md +403 -0
- package/dist/docs/StreamoidWorkspaceSwitcher.md +307 -0
- package/dist/docs/UsageHistoryMobile.md +235 -0
- package/dist/docs/components.json +4849 -0
- package/dist/index.css +43 -37
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/package.json +3 -2
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: CreditWarningBanner
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: sidebar
|
|
5
|
+
status: stable
|
|
6
|
+
renders: div | button (collapsed)
|
|
7
|
+
tags: [credits, low-credits, warning, banner, danger, buy-credits, sidebar-footer, dismiss]
|
|
8
|
+
related: [StreamoidSidebar, ScArtifaxSidebar, ScCreditsUsageCard, ScPlanCard, ScBadges]
|
|
9
|
+
do_not_confuse_with: [ScCreditsUsageCard, ScGuide, ScBadges, ScInfoPopup]
|
|
10
|
+
used_by: [cxo, photogenix, artifax]
|
|
11
|
+
required_props: [availableCredits, remainingPct, level, onBuyCredits]
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# CreditWarningBanner
|
|
15
|
+
|
|
16
|
+
**The low-credits alert that sits in a sidebar's pre-footer slot.** Expanded: a tinted
|
|
17
|
+
card with a warning triangle, "Low Credits", a dismiss ✕, and the sentence
|
|
18
|
+
"Only *N* credits (*P*%) remaining. **Buy credits** to avoid interruptions."
|
|
19
|
+
Collapsed: a 40×40 tinted square holding just the triangle.
|
|
20
|
+
|
|
21
|
+
⚠️ **The export is `CreditWarningBanner`, not `ScCreditWarningBanner`.** It is the one
|
|
22
|
+
component in this family without the `Sc` prefix — the component catalog names it
|
|
23
|
+
wrongly. `import { ScCreditWarningBanner }` is `undefined`.
|
|
24
|
+
|
|
25
|
+
## TL;DR for agents
|
|
26
|
+
|
|
27
|
+
- **Reach for it when:** you need the "you're nearly out of credits" nudge in a
|
|
28
|
+
sidebar rail, in either expanded or collapsed form.
|
|
29
|
+
- **Don't reach for it when:** you want the credits **meter** on a billing page
|
|
30
|
+
(→ `ScCreditsUsageCard`), a generic callout (→ `ScGuide`), or a status chip
|
|
31
|
+
(→ `ScBadges`).
|
|
32
|
+
- **Four things that will bite you:**
|
|
33
|
+
1. The export name has **no `Sc` prefix**.
|
|
34
|
+
2. It owns its own dismiss state: ✕ makes it return `null` **forever**, with no
|
|
35
|
+
callback and no way to reopen it except remounting with a new `key`.
|
|
36
|
+
3. `remainingPct` is printed **raw** — no rounding, no clamping. Pass
|
|
37
|
+
`Math.round(...)` or you ship `(33.33333333333333%)`.
|
|
38
|
+
4. `expanded` defaults to **`true`**; in a collapsed rail you must pass
|
|
39
|
+
`expanded={false}` explicitly.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 1. How to use it
|
|
44
|
+
|
|
45
|
+
### Import
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { CreditWarningBanner } from "@streamoid/ui";
|
|
49
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Minimal usage
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
<CreditWarningBanner
|
|
56
|
+
availableCredits={1200}
|
|
57
|
+
remainingPct={4}
|
|
58
|
+
level="danger"
|
|
59
|
+
onBuyCredits={() => navigate("/settings/billing")}
|
|
60
|
+
/>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Props
|
|
64
|
+
|
|
65
|
+
| Prop | Type | Default | Notes |
|
|
66
|
+
|---|---|---|---|
|
|
67
|
+
| `availableCredits` | `number` | — | **Required.** Rendered via `.toLocaleString()` — thousands separators come from the browser locale. |
|
|
68
|
+
| `remainingPct` | `number` | — | **Required.** Printed verbatim inside `(…%)`. **Round it yourself.** |
|
|
69
|
+
| `level` | `"warning" \| "danger"` | — | **Required — there is no default.** `warning` = orange tint + `--primitives-warning-400`; `danger` = red tint + `--primitives-error-400`. (Both sidebar wrappers default it to `"danger"`; calling this component directly does not.) |
|
|
70
|
+
| `onBuyCredits` | `() => void` | — | **Required.** The underlined "Buy credits" link, and the collapsed square's fallback click. |
|
|
71
|
+
| `expanded` | `boolean` | `true` | ⚠️ Real default. `false` → the 40×40 icon-only square. |
|
|
72
|
+
| `onCollapsedClick` | `() => void` | `onBuyCredits` | ⚠️ Collapsed square click. Omit it and clicking the square **navigates to billing**; CXO passes the sidebar's `onToggle` so it expands the rail instead. |
|
|
73
|
+
|
|
74
|
+
### What renders in each state
|
|
75
|
+
|
|
76
|
+
| Region | `expanded` (default) | `expanded={false}` |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| Root | `div`, `padding: 16px 8px`, full width | **`button`**, 40×40, `aria-label="Show low credits warning"` |
|
|
79
|
+
| Card | tinted bg + 0.5px tinted border, 12px radius, 12px padding | same tint, `radius-xl` |
|
|
80
|
+
| Header | triangle icon + "Low Credits" (12px/600) + ✕ | — |
|
|
81
|
+
| Body | "Only *N* credits (*P*%) remaining. Buy credits to avoid interruptions." | — |
|
|
82
|
+
| Icon | 16×16 triangle, `strokeWidth` 1.25 | same 16×16 triangle |
|
|
83
|
+
| Dismiss | ✕ button, `aria-label="Dismiss low credits warning"` | **none — cannot be dismissed** |
|
|
84
|
+
|
|
85
|
+
### Recipes
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
// The standard host wiring: StreamoidSidebar's pre-footer, both rails
|
|
89
|
+
<StreamoidSidebar
|
|
90
|
+
expanded={sidebarExpanded}
|
|
91
|
+
onToggle={onToggle}
|
|
92
|
+
preFooterContent={
|
|
93
|
+
creditWarning ? (
|
|
94
|
+
<CreditWarningBanner
|
|
95
|
+
availableCredits={creditWarning.availableCredits}
|
|
96
|
+
remainingPct={creditWarning.remainingPct}
|
|
97
|
+
level={creditWarning.level}
|
|
98
|
+
expanded={sidebarExpanded} // MUST track the rail
|
|
99
|
+
onBuyCredits={() => navigate("/settings/billing")}
|
|
100
|
+
onCollapsedClick={onToggle} // expand the rail, don't jump to billing
|
|
101
|
+
/>
|
|
102
|
+
) : undefined
|
|
103
|
+
}
|
|
104
|
+
/* … */
|
|
105
|
+
/>
|
|
106
|
+
|
|
107
|
+
// Deriving the props safely
|
|
108
|
+
const pct = total > 0 ? Math.max(0, Math.min(100, (available / total) * 100)) : 0;
|
|
109
|
+
<CreditWarningBanner
|
|
110
|
+
availableCredits={available}
|
|
111
|
+
remainingPct={Math.round(pct)} // ROUND — the component won't
|
|
112
|
+
level={pct < 5 ? "danger" : "warning"}
|
|
113
|
+
onBuyCredits={goBilling}
|
|
114
|
+
expanded={expanded}
|
|
115
|
+
onCollapsedClick={onToggle}
|
|
116
|
+
/>
|
|
117
|
+
|
|
118
|
+
// Bringing it back after the user dismissed it (e.g. credits dropped further)
|
|
119
|
+
<CreditWarningBanner key={`credits-${level}-${bucket}`} … />
|
|
120
|
+
|
|
121
|
+
// Through a sidebar that owns it for you (level defaults to "danger" there)
|
|
122
|
+
<ScArtifaxSidebar
|
|
123
|
+
creditWarning={{ availableCredits: 120, remainingPct: 4, onBuyCredits: goBilling }}
|
|
124
|
+
/* … */
|
|
125
|
+
/>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 2. Where to use it
|
|
131
|
+
|
|
132
|
+
- **`StreamoidSidebar`'s `preFooterContent`** — CXO
|
|
133
|
+
(`src/app/components/app-sidebar.tsx:986`) does this directly, and is the only
|
|
134
|
+
**live** host render site. Photogenix has a local `LowCreditsBanner` wrapper that
|
|
135
|
+
computes the percentage, but it is currently **not rendered**: it is imported by
|
|
136
|
+
`DashboardContent.tsx` without being used, and the shipped rail hand-rolls its own
|
|
137
|
+
tinted div in `Sidebar.tsx` instead. Reuse the wrapper rather than the hand-roll.
|
|
138
|
+
- **`ScArtifaxSidebar`'s `creditWarning` prop** (Artifax's live wiring) and
|
|
139
|
+
**`ScCatalogixSidebar`'s `creditWarning` prop** (that wrapper is `status: legacy`
|
|
140
|
+
and unused) — both render this component internally, so don't nest a second one.
|
|
141
|
+
- Catalogix's current shell renders no credit banner at all.
|
|
142
|
+
- Its expanded padding (`16px 8px`) assumes it sits inside a 256px rail; the collapsed
|
|
143
|
+
form assumes a 56px rail.
|
|
144
|
+
- **Not** for page-level billing surfaces — that's `ScCreditsUsageCard` /
|
|
145
|
+
`ScPlanDetailsCard`.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 3. When to use it
|
|
150
|
+
|
|
151
|
+
### Use it when
|
|
152
|
+
|
|
153
|
+
- Credits are low enough that the user should act **now**, and the message should
|
|
154
|
+
follow them across every screen (hence: the sidebar).
|
|
155
|
+
- You want the same alert to survive a rail collapse as a compact square.
|
|
156
|
+
- The only action is "buy credits".
|
|
157
|
+
|
|
158
|
+
### Don't use it — reach for this instead
|
|
159
|
+
|
|
160
|
+
| Situation | Use instead |
|
|
161
|
+
|---|---|
|
|
162
|
+
| Credits **remaining / usage meter** on a billing or settings page | `ScCreditsUsageCard` / `ScCreditsUsageCardMobile` |
|
|
163
|
+
| Current-plan summary, upgrade CTA | `ScPlanDetailsCard` / `ScPlanCard` |
|
|
164
|
+
| A generic onboarding/informational callout | `ScGuide` |
|
|
165
|
+
| A non-interactive status or count chip | `ScBadges` |
|
|
166
|
+
| Contextual "?" help | `ScInfoPopup` |
|
|
167
|
+
| A blocking "out of credits" dialog | `ScModal` + `ScButton` |
|
|
168
|
+
| A free-plan upsell CTA in the rail | a plain `ScButton` in `preFooterContent` (what Photogenix does) |
|
|
169
|
+
|
|
170
|
+
### Don't confuse with
|
|
171
|
+
|
|
172
|
+
| You may actually want | Not this |
|
|
173
|
+
|---|---|
|
|
174
|
+
| `ScCreditsUsageCard` — the billing-page meter with usage breakdown | This is a small red/orange sidebar nudge |
|
|
175
|
+
| `ScGuide` — heading + body callout card, tokenised | This one reads `--primitives-*` directly and cannot re-theme |
|
|
176
|
+
| `ScBadges` — a chip | This is a dismissible card with an action |
|
|
177
|
+
| `ScCreditWarningBanner` — **does not exist** | The export is `CreditWarningBanner` |
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 4. Why to use it
|
|
182
|
+
|
|
183
|
+
- **Both rail forms in one component.** The collapsed 40×40 square keeps the exact
|
|
184
|
+
tint and icon of the expanded card, so the warning never disappears when the user
|
|
185
|
+
collapses the sidebar.
|
|
186
|
+
- **A consistent severity vocabulary.** `level` maps to the same warning/error hues
|
|
187
|
+
used everywhere else (`--primitives-warning-400` / `--primitives-error-400`) with
|
|
188
|
+
matching 8%-alpha fills and 32%-alpha strokes, instead of every app picking its own red.
|
|
189
|
+
- **Correct button semantics for the two actions.** The dismiss ✕, the "Buy credits"
|
|
190
|
+
link and the collapsed square are all real `<button>`s with `aria-label`s; the
|
|
191
|
+
triangle is `aria-hidden`.
|
|
192
|
+
- **Locale-aware numbers.** `availableCredits.toLocaleString()` so "1,200" not "1200".
|
|
193
|
+
- **It's what the sidebars already render.** Using it directly keeps a host's
|
|
194
|
+
hand-rolled banner from drifting away from Artifax's and Catalogix's.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Gotchas
|
|
199
|
+
|
|
200
|
+
**1. The export has no `Sc` prefix.**
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
// WRONG — undefined; the catalog's name for it is incorrect
|
|
204
|
+
import { ScCreditWarningBanner } from "@streamoid/ui";
|
|
205
|
+
|
|
206
|
+
// RIGHT
|
|
207
|
+
import { CreditWarningBanner } from "@streamoid/ui";
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**2. Dismissal is internal and permanent.** `const [dismissed, setDismissed] = useState(false)`
|
|
211
|
+
and `if (dismissed) return null`. There is no `onDismiss`, no `dismissed` prop, no
|
|
212
|
+
`persist` option. Once the user clicks ✕ the banner is gone for the lifetime of that
|
|
213
|
+
component instance — even if credits drop to zero.
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
// If it must be able to come back, change the key
|
|
217
|
+
<CreditWarningBanner key={`credits-${severityBucket}`} … />
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**3. `remainingPct` is printed unformatted.** Photogenix's own wrapper computes
|
|
221
|
+
`(availableCredits / totalCredits) * 100` without rounding, which renders as
|
|
222
|
+
`(33.33333333333333%)`. Round (and clamp) at the call site.
|
|
223
|
+
|
|
224
|
+
**4. `expanded` defaults to `true`.** In a collapsed rail you must thread the rail
|
|
225
|
+
state through, or you get the full 256px-wide card inside a 56px rail.
|
|
226
|
+
|
|
227
|
+
**5. `onCollapsedClick` falls back to `onBuyCredits`.** Clicking the little square
|
|
228
|
+
then navigates to billing, which is not what a "show me the warning" affordance should
|
|
229
|
+
do. CXO passes the sidebar's `onToggle`, and `ScCatalogixSidebar` defaults its
|
|
230
|
+
`creditWarning.onCollapsedClick` to `onToggle` — but `ScArtifaxSidebar` has **no**
|
|
231
|
+
`onCollapsedClick` field on its `creditWarning` object at all, so in Artifax the
|
|
232
|
+
collapsed square always jumps to billing.
|
|
233
|
+
|
|
234
|
+
**6. It reads `--primitives-*`, not `--alias-*`.** `--primitives-warning-400` /
|
|
235
|
+
`--primitives-error-400` plus literal `rgba(255,165,0,…)` / `rgba(255,0,0,…)` fills
|
|
236
|
+
and borders. It is the one component in this family that bypasses the semantic token
|
|
237
|
+
layer, so it does **not** re-theme with light mode the way the rest of the DS does.
|
|
238
|
+
|
|
239
|
+
**7. All copy is hardcoded English.** "Low Credits", "Only {n} credits ({p}%)
|
|
240
|
+
remaining.", "Buy credits", "to avoid interruptions.", plus the
|
|
241
|
+
`"Dismiss low credits warning"` / `"Show low credits warning"` aria-labels. No label
|
|
242
|
+
props — not usable on a localised surface without changing the DS.
|
|
243
|
+
|
|
244
|
+
**8. `level` is required, with no default.** Rendering it via a sidebar's
|
|
245
|
+
`creditWarning` prop gets you `"danger"` by default; calling the component directly
|
|
246
|
+
does not, and TypeScript will (correctly) reject the omission.
|
|
247
|
+
|
|
248
|
+
**9. The collapsed form can't be dismissed.** No ✕ in that branch — so a user who
|
|
249
|
+
collapses the rail is stuck with the square until they expand and dismiss.
|
|
250
|
+
|
|
251
|
+
**10. `toLocaleString()` is locale-dependent.** Server-rendered and client-rendered
|
|
252
|
+
output can differ (hydration warnings) and screenshot tests can vary by CI locale.
|
|
253
|
+
|
|
254
|
+
**11. It needs Tailwind.** The layout uses `flex`, `shrink-0`, `w-full`, `min-w-0`,
|
|
255
|
+
`flex-1`, `items-center`, `cursor-pointer`, `underline`, `transition-opacity`,
|
|
256
|
+
`hover:opacity-90`. `@streamoid/ui/dist/index.css` does **not** ship those utilities —
|
|
257
|
+
your app must (all four hosts do).
|
|
258
|
+
|
|
259
|
+
**12. Fixed geometry.** Collapsed square 40×40, icons 16×16, ✕ hit-area 16×16 (small
|
|
260
|
+
for touch), body text a literal `fontSize: 12`.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## In the wild
|
|
265
|
+
|
|
266
|
+
```tsx
|
|
267
|
+
// cxo-dashboard src/app/components/app-sidebar.tsx:986 (inside StreamoidSidebar's preFooterContent)
|
|
268
|
+
<CreditWarningBanner
|
|
269
|
+
availableCredits={creditWarning.availableCredits}
|
|
270
|
+
remainingPct={creditWarning.remainingPct}
|
|
271
|
+
level={creditWarning.level}
|
|
272
|
+
expanded={sidebarExpanded}
|
|
273
|
+
onBuyCredits={handleBuyCreditsNavigation}
|
|
274
|
+
onCollapsedClick={onToggle}
|
|
275
|
+
/>
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
```tsx
|
|
279
|
+
// photogenix_v2 dashboard/client/src/components/layout/LowCreditsBanner.tsx:27
|
|
280
|
+
// (a real wrapper, but currently unrendered — see §2; exercised only by
|
|
281
|
+
// dashboard/tests/low-credits-banner.vitest.tsx)
|
|
282
|
+
<CreditWarningBanner
|
|
283
|
+
availableCredits={availableCredits}
|
|
284
|
+
remainingPct={remainingPct} // NB: unrounded float — see Gotcha 3
|
|
285
|
+
level={level}
|
|
286
|
+
onBuyCredits={onBuyCredits}
|
|
287
|
+
expanded={expanded}
|
|
288
|
+
onCollapsedClick={onCollapsedClick}
|
|
289
|
+
/>
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Artifax reaches it indirectly, through `ScArtifaxSidebar`'s `creditWarning` prop
|
|
293
|
+
(`artifax packages/shared/src/components/DashboardSidebar.tsx:680`). Catalogix's shell
|
|
294
|
+
renders no banner today.
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Related
|
|
299
|
+
|
|
300
|
+
- `StreamoidSidebar` — pass this as `preFooterContent`, threading `expanded`.
|
|
301
|
+
- `ScArtifaxSidebar` / `ScCatalogixSidebar` — render it for you from `creditWarning`.
|
|
302
|
+
- `ScCreditsUsageCard` — the billing-page credits meter (tokenised, non-dismissible).
|
|
303
|
+
- `ScPlanCard` / `ScPlanDetailsCard` — where "Buy credits" usually lands.
|
|
304
|
+
- `ScGuide` — the tokenised generic callout card.
|
|
305
|
+
- `@streamoid/icons` — `SiconClose` is the dismiss glyph; the warning triangle is an inline SVG in this component.
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: InvoiceHistoryMobile
|
|
3
|
+
package: "@streamoid/ui"
|
|
4
|
+
category: mobile
|
|
5
|
+
status: stable
|
|
6
|
+
renders: div
|
|
7
|
+
tags: [mobile, billing, invoice, history, row, list-item, amount, cxo]
|
|
8
|
+
related: [UsageHistoryMobile, ScBillingHistoryTableList, ScBillingHistoryHeader, ScInvoiceHistoryMobile]
|
|
9
|
+
do_not_confuse_with: [ScInvoiceHistoryMobile, UsageHistoryMobile, ScBillingHistoryTableList, ScTableListMobile]
|
|
10
|
+
used_by: [cxo]
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# InvoiceHistoryMobile
|
|
14
|
+
|
|
15
|
+
**One invoice row in CXO's mobile billing list.** Invoice id over a date on the left,
|
|
16
|
+
a green amount right-aligned, and a 0.5px bottom border so a stack of them reads as a
|
|
17
|
+
list. Note the export name: **no `Sc` prefix** — it and `UsageHistoryMobile` are the
|
|
18
|
+
only two components in the mobile family without one.
|
|
19
|
+
|
|
20
|
+
## TL;DR for agents
|
|
21
|
+
|
|
22
|
+
- **Reach for it when:** you are rendering the "Invoice history" tab of a mobile
|
|
23
|
+
billing screen, one component per invoice.
|
|
24
|
+
- **Don't reach for it when:** you're on desktop (→ `ScBillingHistoryTableList` +
|
|
25
|
+
`ScBillingHistoryHeader`) or the row is a credit-usage log (→ `UsageHistoryMobile`).
|
|
26
|
+
- **Four things that will bite you:**
|
|
27
|
+
1. **The export is `InvoiceHistoryMobile`, not `ScInvoiceHistoryMobile`.** A
|
|
28
|
+
`ScInvoiceHistoryMobile` *does* exist in the source tree
|
|
29
|
+
(`src/SC-InvoiceHistory-Mobile/`) but it is **not exported** — see "Don't
|
|
30
|
+
confuse with".
|
|
31
|
+
2. **No `onClick`, and no props spread.** `className` is the only escape hatch;
|
|
32
|
+
you cannot attach a handler, `key`-adjacent `data-*`, `style`, or `aria-*`.
|
|
33
|
+
Wrap it if the row must be tappable.
|
|
34
|
+
3. **The amount is always success-green**, including refunds and zero.
|
|
35
|
+
4. All three text props have demo defaults (`"# INV-2800-2026"`, `"Nov 9th, 2025"`,
|
|
36
|
+
`"₹320.89"` — a rupee amount).
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 1. How to use it
|
|
41
|
+
|
|
42
|
+
### Import
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { InvoiceHistoryMobile } from "@streamoid/ui";
|
|
46
|
+
import "@streamoid/ui/dist/index.css"; // once, at your app root
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Minimal usage
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
{invoices.map((inv) => (
|
|
53
|
+
<InvoiceHistoryMobile
|
|
54
|
+
key={inv.id}
|
|
55
|
+
invoiceId={`# ${inv.number}`}
|
|
56
|
+
date={formatDate(inv.createdAt)}
|
|
57
|
+
amount={`$${inv.amountPaid.toFixed(2)}`}
|
|
58
|
+
/>
|
|
59
|
+
))}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Props
|
|
63
|
+
|
|
64
|
+
| Prop | Type | Default | Notes |
|
|
65
|
+
|---|---|---|---|
|
|
66
|
+
| `invoiceId` | `string` | `"# INV-2800-2026"` | ⚠️ Demo default. 16px primary line. You supply the `#` — the component adds nothing. Truncates with ellipsis. |
|
|
67
|
+
| `date` | `string` | `"Nov 9th, 2025"` | ⚠️ Demo default. 14px tertiary sub-line. Pre-formatted string; no date parsing. |
|
|
68
|
+
| `amount` | `string` | `"₹320.89"` | ⚠️ Demo default **in rupees**. 16px/600, always `--alias-text-and-icons-success` green. You supply the currency symbol. |
|
|
69
|
+
| `className` | `string` | – | Appended after internal classes (safely — uses `className ?? ""`). **The only extra prop it accepts.** |
|
|
70
|
+
|
|
71
|
+
That is the whole API. There is no `onClick`, no `...props`, no `status`, no icon.
|
|
72
|
+
|
|
73
|
+
### Recipes
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
// Make the row tappable — the component itself has no onClick
|
|
77
|
+
<div role="button" tabIndex={0} onClick={() => openInvoice(inv)} className="cursor-pointer">
|
|
78
|
+
<InvoiceHistoryMobile
|
|
79
|
+
invoiceId={`# ${inv.number}`}
|
|
80
|
+
date={formatDate(inv.createdAt)}
|
|
81
|
+
amount={`$${inv.amountPaid.toFixed(2)}`}
|
|
82
|
+
/>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
// Empty state — render your own; this component has no zero/empty mode
|
|
86
|
+
{invoices.length === 0
|
|
87
|
+
? <p>No invoices found</p>
|
|
88
|
+
: invoices.map((inv) => <InvoiceHistoryMobile key={inv.id} {...toRow(inv)} />)}
|
|
89
|
+
|
|
90
|
+
// Stripe amounts arrive in cents
|
|
91
|
+
<InvoiceHistoryMobile
|
|
92
|
+
invoiceId={`# ${inv.meta?.stripeInvoiceId ?? inv._id}`}
|
|
93
|
+
date={formatDate(inv.createdAt)}
|
|
94
|
+
amount={inv.amountPaid != null ? `$${inv.amountPaid.toFixed(2)}` : `$${(inv.amount / 100).toFixed(2)}`}
|
|
95
|
+
/>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 2. Where to use it
|
|
101
|
+
|
|
102
|
+
- **CXO mobile billing screen**, inside the "Invoice history" tab of the
|
|
103
|
+
`ScTabSwitcher`, below `ScPlanDetailsCardMobile` and `ScCreditsUsageCardMobile`.
|
|
104
|
+
- Any narrow scrolling list of "document + date + money" rows.
|
|
105
|
+
|
|
106
|
+
The row draws its own bottom border, so stack them with **no gap** and no dividers of
|
|
107
|
+
your own. It composes nothing from the DS.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 3. When to use it
|
|
112
|
+
|
|
113
|
+
### Use it when
|
|
114
|
+
|
|
115
|
+
- The viewport is mobile and each invoice is a full-width two-line row.
|
|
116
|
+
- You already have id, date and amount as display strings.
|
|
117
|
+
- The list is read-only, or you own the tap target yourself (wrapper div).
|
|
118
|
+
|
|
119
|
+
### Don't use it — reach for this instead
|
|
120
|
+
|
|
121
|
+
| Situation | Use instead |
|
|
122
|
+
|---|---|
|
|
123
|
+
| Desktop billing history table | `ScBillingHistoryHeader` + `ScBillingHistoryTableList` |
|
|
124
|
+
| Credit-usage / debit log rows on mobile | `UsageHistoryMobile` |
|
|
125
|
+
| Desktop credit/usage log table | `ScBillingLogsTableHeader` + `ScBillingLogsTableList` |
|
|
126
|
+
| A generic member/user row on mobile | `ScTableListMobile` |
|
|
127
|
+
| Referral rows on mobile | `ScReferralCardMobile` |
|
|
128
|
+
| A row that must be clickable *with* DS-provided semantics | none of these — wrap it, or use `ScTableListMobile` which has `onRowClick` |
|
|
129
|
+
|
|
130
|
+
### Don't confuse with
|
|
131
|
+
|
|
132
|
+
| You may actually want | Not this |
|
|
133
|
+
|---|---|
|
|
134
|
+
| `ScInvoiceHistoryMobile` in `src/SC-InvoiceHistory-Mobile/` — an earlier, near-identical copy with `invoiceNumber` instead of `invoiceId`, plus `onClick` and a props spread | It is **not exported** from `index.ts`, its CSS is **not** in `dist/index.css`, and `IScInvoiceHistoryMobileProps` is absent from `dist/index.d.ts`. Importing it will fail. Use `InvoiceHistoryMobile`. |
|
|
135
|
+
| `UsageHistoryMobile` — same chrome, but two rows of two values and a **red** figure | Invoices are green money in; usage is red credits out |
|
|
136
|
+
| `ScBillingHistoryTableList` — the desktop row | Different metrics, header component required |
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 4. Why to use it
|
|
141
|
+
|
|
142
|
+
- **The truncation math is done.** `flex: 1; min-width: 0` on the text column with
|
|
143
|
+
`ellipsis` on both lines, and `flex-shrink: 0` on the amount, so a long invoice id
|
|
144
|
+
never pushes the money off-screen — the single thing hand-rolled money rows get wrong.
|
|
145
|
+
- **Self-dividing.** The bottom border is part of the row, so you never end up with a
|
|
146
|
+
trailing divider or a double line between sections.
|
|
147
|
+
- **Correct tokens.** It uses the real `--alias-text-and-icons-*` names (unlike much
|
|
148
|
+
of the mobile family), so it genuinely flips light/dark.
|
|
149
|
+
- **Money semantics are consistent** with `UsageHistoryMobile`: green = money/credits
|
|
150
|
+
in, red = credits out, tertiary grey = context.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Gotchas
|
|
155
|
+
|
|
156
|
+
**1. The name has no `Sc` prefix.** Autocomplete for `Sc…` will not find it.
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
// WRONG — not exported
|
|
160
|
+
import { ScInvoiceHistoryMobile } from "@streamoid/ui";
|
|
161
|
+
|
|
162
|
+
// RIGHT
|
|
163
|
+
import { InvoiceHistoryMobile } from "@streamoid/ui";
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**2. No props spread at all.** The signature is
|
|
167
|
+
`({ invoiceId, date, amount, className })` — nothing else reaches the DOM.
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
// SILENTLY IGNORED — onClick, style and data-* never land anywhere
|
|
171
|
+
<InvoiceHistoryMobile invoiceId="# 1" onClick={open} style={{ opacity: 0.5 }} data-id="1" />
|
|
172
|
+
|
|
173
|
+
// RIGHT — wrap it
|
|
174
|
+
<div onClick={open} data-id="1"><InvoiceHistoryMobile invoiceId="# 1" /></div>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
(In TypeScript the extra props are a compile error; in a plain-JS host they are
|
|
178
|
+
silently dropped.)
|
|
179
|
+
|
|
180
|
+
**3. The amount is unconditionally green.** `--alias-text-and-icons-success` is
|
|
181
|
+
hardcoded, so refunds, credit notes and `$0.00` all read as positive.
|
|
182
|
+
|
|
183
|
+
**4. Demo defaults, including a rupee amount.** Rendering it bare gives
|
|
184
|
+
`# INV-2800-2026 / Nov 9th, 2025 / ₹320.89`, which looks like real data.
|
|
185
|
+
|
|
186
|
+
**5. Nothing is formatted for you.** No currency, no locale grouping, no date parsing,
|
|
187
|
+
no `#` prefix. Three strings in, three strings out.
|
|
188
|
+
|
|
189
|
+
**6. No status.** Paid / open / void / failed cannot be expressed. If you need it,
|
|
190
|
+
put it in `invoiceId` or `date`, or move to a row component that has a badge.
|
|
191
|
+
|
|
192
|
+
**7. `align-items: flex-start`** on the root, so the amount aligns to the **top** of
|
|
193
|
+
the row, level with `invoiceId`, not vertically centred.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## In the wild
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
// cxo-dashboard src/app/components/mobile-billing-content.tsx:231
|
|
201
|
+
<InvoiceHistoryMobile
|
|
202
|
+
key={inv._id}
|
|
203
|
+
invoiceId={`# ${inv.meta?.stripeInvoiceId ?? inv._id}`}
|
|
204
|
+
date={formatDate(inv.createdAt)}
|
|
205
|
+
amount={
|
|
206
|
+
inv.amountPaid != null
|
|
207
|
+
? `$${inv.amountPaid.toFixed(2)}`
|
|
208
|
+
: `$${(inv.amount / 100).toFixed(2)}`
|
|
209
|
+
}
|
|
210
|
+
/>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Related
|
|
216
|
+
|
|
217
|
+
- `UsageHistoryMobile` — the sibling row for the "Usage history" tab.
|
|
218
|
+
- `ScInvoiceHistoryMobile` (`src/SC-InvoiceHistory-Mobile/`) — the unexported earlier
|
|
219
|
+
copy; do not import it.
|
|
220
|
+
- `ScBillingHistoryHeader` / `ScBillingHistoryTableList` — the desktop table.
|
|
221
|
+
- `ScPlanDetailsCardMobile` / `ScCreditsUsageCardMobile` — the cards above the tabs.
|
|
222
|
+
- `ScTabSwitcher` / `ScTabComp` — what toggles invoice vs usage.
|