ui improvement

This commit is contained in:
2026-09-17 17:57:21 +05:30
parent 2cd048e6f0
commit 115a06a02c
79 changed files with 3574 additions and 2186 deletions

View File

@@ -1,99 +1,89 @@
import type { ReactNode } from 'react';
import { Card } from '@astryxdesign/core/Card';
import { HStack } from '@astryxdesign/core/HStack';
import { Text } from '@astryxdesign/core/Text';
import { VStack } from '@astryxdesign/core/VStack';
import './kpiCard.css';
/**
* A KPI tile — the console's headline figure.
*
* ── There were two of these, and neither worked ─────────────────────────────
*
* This component put an icon inline with the label and CENTRED the value
* underneath it, which is why a row of tiles never lined up: a centred number
* sits in a different place in every tile depending on how long it is.
* `console.css` had a second, unrelated implementation — `.kpi` — with a
* brand-tinted icon tile on the left and the text stacked beside it. The two
* appeared on the same screen. The second was the better design and this is
* now that design, for both.
*
* ── `note` is rendered again, and that is the real change ───────────────────
*
* The line under the figure — "142 app · 38 counter", "12% of app orders" —
* was removed from the tile at some point but never removed from the CALLERS:
* 86 of them still compute and pass it, and it was being thrown away. That is
* the half of a KPI that carries the meaning. A bare `0` under "Unsynced
* bills" reads as "no data" when it means everything is in the books, and `42`
* under "Total orders" says nothing about which channel they came through.
*
* ── `tone` does something now ───────────────────────────────────────────────
*
* All five tones used to map to `var(--color-brand)`, so the prop was
* decorative — every tile was purple whether it reported takings or a failure.
* The tone now colours the icon tile only: the figure itself stays in ink,
* because a number that changes colour with its own value is hard to compare
* against the tile beside it. Brand remains the default, so a tile that is
* merely reporting is not shouting.
*/
export type KpiTone = 'accent' | 'success' | 'warning' | 'error' | 'neutral';
const TONE_COLOR: Record<KpiTone, string> = {
accent: 'var(--color-brand)',
success: 'var(--color-brand)',
warning: 'var(--color-brand)',
error: 'var(--color-brand)',
neutral: 'var(--color-brand)',
};
export interface KpiCardProps {
/** Small-caps label. Say what it is, not what it means. */
label: string;
value: string;
/**
* Accepted and ignored, like `fill` below.
*
* This was the sub-pill — where the number got its meaning, since "1 of 84
* orders" says something "1.2%" does not. The pill was removed from the tile
* deliberately; 87 call sites across 21 files still pass the text, so the
* prop stays declared to keep them compiling and is not read.
*
* That means every one of those 87 strings is now written and never shown.
* Either drop them from the callers or bring the pill back — right now it is
* neither, and this comment is here so that is visible rather than a puzzle
* for whoever next wonders where "84 orders" went.
* The line under the figure — what the number is made of, or what it is a
* share of. This is where a KPI gets its meaning; see the note above.
*/
note?: string;
/**
* Colours the icon tile. `neutral` and `accent` are both the brand — a tile
* reports by default and only says more when something is actually wrong.
*/
tone?: KpiTone;
icon?: ReactNode;
/**
* Accepted and ignored.
*
* The underline bar this used to drive was removed from the tile. Six call
* sites still pass it, so the prop stays declared to keep them compiling —
* it is not read. Either drop it from the callers or restore the bar; right
* now it is neither, and this comment is here so that is visible.
* The 2px underline bar this drove was removed from the tile deliberately —
* a proportion bar under five tiles turned the strip into a chart. Six call
* sites still pass it, so the prop stays declared to keep them compiling.
* Drop it from those callers and this can go.
*/
fill?: number;
}
/**
* A KPI tile.
*
* Label and icon, then a large tabular value. The sub-pill and the 2px tone
* underline were both removed from the tile; `note` and `fill` still arrive
* from the call sites and are ignored — see the props above. Sized to sit 4–6
* across rather than 4 — a console is glanced at all day, not read once.
*/
export function KpiCard({ label, value, tone = 'neutral', icon }: KpiCardProps) {
const color = TONE_COLOR[tone];
export function KpiCard({ label, value, note, tone = 'neutral', icon }: KpiCardProps) {
return (
<Card padding={0} elevation="low">
<VStack gap={1} padding={2} style={{ minHeight: 108 }}>
{/* Icon first, then the label; the pill sits opposite it. Both are
fixed to this row so tiles line up whether or not they carry one. */}
{/* A fixed height, so a label that wraps to two lines (a long note
squeezes it) does not push that tile's value below its neighbours'. */}
<HStack justify="between" align="center" gap={1} style={{ minHeight: 32 }}>
<HStack gap={1} align="center" style={{ minWidth: 0 }}>
{icon ? (
<span style={{ color, display: 'flex', flex: 'none' }} aria-hidden>
{icon}
</span>
) : null}
<Text
type="label"
size="xsm"
color="secondary"
style={{ textTransform: 'uppercase', letterSpacing: '0.09em', lineHeight: 1.35 }}
>
{label}
</Text>
</HStack>
<div className="kpi-card" data-tone={tone}>
{/* Label and icon share the top row; the figure gets the full width of
the card underneath them.
{/* Note pill removed as requested */}
</HStack>
The icon used to sit in a column of its own to the LEFT, and at the
width these tiles actually get — five across a 1,224px column, less
340px whenever Nearle Buddy is open — a 40px tile plus its gutter
left about 134px for the number. "₹1,24,500" does not fit in 134px at
24px bold, so it wrapped, and it wrapped mid-figure: the first tile
on the Console read "₹1,24,50" on one line and "0" on the next. */}
<div className="kpi-card-head">
<span className="kpi-card-label">{label}</span>
{icon ? (
<span className="kpi-card-icon" aria-hidden>
{icon}
</span>
) : null}
</div>
<HStack justify="center" align="center" style={{ flex: 1 }}>
<Text
as="div"
hasTabularNumbers
style={{ color, fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 24, lineHeight: 1.1 }}
>
{value}
</Text>
</HStack>
</VStack>
</Card>
<span className="kpi-card-value">{value}</span>
{note ? <span className="kpi-card-note">{note}</span> : null}
</div>
);
}