Show SMS sent breakdown by type in admin and business settings.

Data rows for total/mass/singular/auth/auto/admin; balance stays a credit data row.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-09-09 11:31:18 +03:30
co-authored by Cursor
parent 6bc57f26ed
commit dbc17b2589
8 changed files with 194 additions and 16 deletions
@@ -88,6 +88,64 @@
color: var(--text-primary);
}
.dataRow {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
padding: 12px 14px;
border-radius: var(--radius-sm, 10px);
border: 1px solid transparent;
}
.dataRowOk {
background: rgba(var(--primary-rgb, 15 118 110) / 0.1);
border-color: rgba(var(--primary-rgb, 15 118 110) / 0.28);
}
.dataRowOk .dataRowLabel,
.dataRowOk .dataRowValue {
color: var(--primary);
}
.dataRowLow {
background: rgba(239, 68, 68, 0.1);
border-color: rgba(239, 68, 68, 0.28);
}
.dataRowLow .dataRowLabel,
.dataRowLow .dataRowValue {
color: #dc2626;
}
.dataRowNeutral {
background: rgba(15, 23, 42, 0.04);
border-color: rgba(15, 23, 42, 0.1);
}
.dataRowNeutral .dataRowLabel,
.dataRowNeutral .dataRowValue {
color: var(--text-primary);
}
.dataRowLabel {
font-size: 13px;
font-weight: 500;
}
.dataRowValue {
font-size: 15px;
font-weight: 700;
font-variant-numeric: tabular-nums;
text-align: end;
}
.dataRowStack {
display: flex;
flex-direction: column;
gap: 8px;
}
.entityGroupLabel {
font-size: 12px;
font-weight: 500;
+49 -15
View File
@@ -2108,13 +2108,20 @@ export function BusinessesPage() {
</p>
</div>
<div className={styles.field}>
<label>Current SMS balance</label>
<input
value={`${(smsSettingsBusiness?.smsBalance ?? 0).toLocaleString('en-US')} credits`}
readOnly
disabled
<div
className={`${styles.dataRow} ${
(smsSettingsBusiness?.smsBalance ?? 0) < 400
? styles.dataRowLow
: styles.dataRowOk
}`}
role="status"
aria-label="Current SMS credit balance"
/>
>
<span className={styles.dataRowLabel}>Current SMS balance</span>
<strong className={styles.dataRowValue}>
{(smsSettingsBusiness?.smsBalance ?? 0).toLocaleString('en-US')} SMS credits
</strong>
</div>
<p className={styles.helperText}>
Prepaid SMS credits. 1 credit = one 64-character segment per recipient (OTP,
customer SMS, partner SMS). New businesses start with 1,000 credits. Values below
@@ -2156,16 +2163,43 @@ export function BusinessesPage() {
</p>
</div>
<div className={styles.field}>
<label>SMS sent</label>
<input
value={(smsSettingsBusiness?.smsSentCount ?? 0).toLocaleString('en-US')}
readOnly
disabled
aria-label="SMS messages sent"
/>
<div className={styles.dataRowStack} role="group" aria-label="SMS sent by type">
<div
className={`${styles.dataRow} ${styles.dataRowNeutral}`}
role="status"
>
<span className={styles.dataRowLabel}>SMS sent (total)</span>
<strong className={styles.dataRowValue}>
{(smsSettingsBusiness?.smsSentCount ?? 0).toLocaleString('en-US')}
</strong>
</div>
{(
[
['Mass send', smsSettingsBusiness?.smsSentByType?.mass],
['Singular', smsSettingsBusiness?.smsSentByType?.singular],
['Auth service', smsSettingsBusiness?.smsSentByType?.auth],
[
'Auto customer',
smsSettingsBusiness?.smsSentByType?.autoCustomer,
],
['Admin notification', smsSettingsBusiness?.smsSentByType?.admin],
] as const
).map(([label, value]) => (
<div
key={label}
className={`${styles.dataRow} ${styles.dataRowNeutral}`}
role="status"
>
<span className={styles.dataRowLabel}>{label}</span>
<strong className={styles.dataRowValue}>
{(value ?? 0).toLocaleString('en-US')}
</strong>
</div>
))}
</div>
<p className={styles.helperText}>
Lifetime messages billed for this business (one per recipient). Updated automatically
when SMS is charged or refunded.
Lifetime messages billed (one per recipient), split by type. Auto customer and
admin notification are reserved for upcoming flows.
</p>
</div>
<div style={{ height: 12 }} />
+8
View File
@@ -44,6 +44,14 @@ export interface BusinessListItem {
smsBalance: number
/** Lifetime SMS messages billed (one per recipient; decremented on refund). */
smsSentCount: number
/** Lifetime SMS messages billed by category. */
smsSentByType: {
mass: number
singular: number
auth: number
autoCustomer: number
admin: number
}
}
export type MigrateFromOldEntity =