fix(admin): rupee formatting for avg revenue, per-tier storage bars

- Avg per Family now shows ₹ with en-IN locale instead of $
- Storage table: paid families show progress bar vs 25 GB limit (was "Unlimited")
- Column header changed from "% of free (1 GiB)" to "% of limit"
- Bar label shows "X% of 1 GiB" (free) or "X% of 25 GB" (paid)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Manohar Gupta 2026-06-06 15:52:48 +05:30
parent 4d29ef89a0
commit bf82ef3364
3 changed files with 17 additions and 15 deletions

File diff suppressed because one or more lines are too long

View file

@ -117,7 +117,7 @@ export default function AdminDashboard() {
<div className="text-gray-400 text-sm">Free Families</div>
</div>
<div>
<div className="text-2xl font-bold text-amber-400">${overview.avgRevenuePerUser}</div>
<div className="text-2xl font-bold text-amber-400">{(overview.avgRevenuePerUser || 0).toLocaleString("en-IN")}</div>
<div className="text-gray-400 text-sm">Avg per Family</div>
</div>
</div>

View file

@ -116,12 +116,16 @@ export default function AdminStorage() {
<th className="px-4 py-3 text-left text-sm font-medium">Tier</th>
<th className="px-4 py-3 text-right text-sm font-medium">Objects</th>
<th className="px-4 py-3 text-right text-sm font-medium">Storage</th>
<th className="px-4 py-3 text-left text-sm font-medium w-48">% of free (1 GiB)</th>
<th className="px-4 py-3 text-left text-sm font-medium w-56">% of limit</th>
</tr></thead>
<tbody className="divide-y divide-gray-700">
{families.map(f => {
const pct = Math.min(Math.round(f.fraction * 100), 999);
const barColor = f.overLimit ? "bg-rose-500" : f.fraction >= 0.8 ? "bg-amber-500" : "bg-emerald-500";
const limit = f.isPaid ? 25 * 1024 * 1024 * 1024 : 1_073_741_824;
const fraction = f.bytes / limit;
const pct = Math.min(Math.round(fraction * 100), 999);
const isOver = fraction >= 1;
const barColor = isOver ? "bg-rose-500" : fraction >= 0.8 ? "bg-amber-500" : "bg-emerald-500";
const limitLabel = f.isPaid ? "25 GB" : "1 GiB";
return (
<tr key={f.id} className="hover:bg-gray-750">
<td className="px-4 py-3 font-medium">{f.name}</td>
@ -134,16 +138,14 @@ export default function AdminStorage() {
</td>
<td className="px-4 py-3 text-right text-sm font-medium">{fmtBytes(f.bytes)}</td>
<td className="px-4 py-3">
{f.isPaid ? (
<span className="text-xs text-rose-400">Unlimited (paid)</span>
) : (
<div className="flex items-center gap-2">
<div className="flex-1 bg-gray-700 rounded-full h-2 overflow-hidden">
<div className={`h-full ${barColor}`} style={{ width: `${Math.min(pct, 100)}%` }} />
</div>
<span className={`text-xs w-12 text-right ${f.overLimit ? "text-rose-400 font-bold" : "text-gray-400"}`}>{pct}%</span>
<div className="flex items-center gap-2">
<div className="flex-1 bg-gray-700 rounded-full h-2 overflow-hidden">
<div className={`h-full ${barColor}`} style={{ width: `${Math.min(pct, 100)}%` }} />
</div>
)}
<span className={`text-xs w-24 text-right ${isOver ? "text-rose-400 font-bold" : "text-gray-400"}`}>
{pct}% of {limitLabel}
</span>
</div>
</td>
</tr>
);
@ -155,7 +157,7 @@ export default function AdminStorage() {
</div>
<p className="text-xs text-gray-600">
Usage = SUM(size_bytes) over memories + attachments, matching the quota enforced in the app.
Usage = SUM(size_bytes) over memories + attachments. Limits: free = 1 GiB · paid = 25 GB.
Objects missing a recorded size aren&apos;t counted in bytes. R2 cost is storage only (egress is free on R2).
</p>
</div>