implemenation on the bot
This commit is contained in:
@@ -20,7 +20,7 @@ import { StatusDot } from '@astryxdesign/core/StatusDot';
|
||||
|
||||
export default function PageHeader({ title, subtitle, live = false, action }) {
|
||||
return (
|
||||
<HStack justify="between" vAlign="center" wrap="wrap" gap={2} paddingBlock={2}>
|
||||
<HStack justify="between" vAlign="center" wrap="wrap" gap={2} paddingBlock={1}>
|
||||
<VStack gap={0.5} padding={0} minWidth={0}>
|
||||
<Heading level={1} maxLines={1}>
|
||||
{title}
|
||||
|
||||
@@ -60,7 +60,7 @@ const FILL_CLASS = 'dt-page-fill';
|
||||
// shell (e.g. Create Order's `.co-root` token block). It used to be dropped
|
||||
// silently, which made every page-scoped rule fail to match with no error —
|
||||
// the styles simply never applied.
|
||||
export function PageShell({ children, gap = 6, fill = false, className }) {
|
||||
export function PageShell({ children, gap = 4, fill = false, className }) {
|
||||
return (
|
||||
<>
|
||||
{fill && (
|
||||
@@ -105,10 +105,10 @@ PageShell.propTypes = {
|
||||
// `max` caps the column count so 3 cards don't each become 400px wide on an
|
||||
// ultrawide monitor; it defaults to the number of children.
|
||||
// ---------------------------------------------------------------------------
|
||||
export function KpiGrid({ children, minWidth = 240, max }) {
|
||||
export function KpiGrid({ children, minWidth = 180, gap = 3, max }) {
|
||||
const count = Array.isArray(children) ? children.filter(Boolean).length : 1;
|
||||
return (
|
||||
<Grid columns={{ minWidth, max: max || Math.max(count, 1), repeat: 'fit' }} gap={4}>
|
||||
<Grid columns={{ minWidth, max: max || Math.max(count, 1), repeat: 'fit' }} gap={gap}>
|
||||
{children}
|
||||
</Grid>
|
||||
);
|
||||
@@ -117,6 +117,7 @@ export function KpiGrid({ children, minWidth = 240, max }) {
|
||||
KpiGrid.propTypes = {
|
||||
children: PropTypes.node,
|
||||
minWidth: PropTypes.number,
|
||||
gap: PropTypes.number,
|
||||
max: PropTypes.number
|
||||
};
|
||||
|
||||
|
||||
@@ -7,53 +7,42 @@ import { Center } from '@astryxdesign/core/Center';
|
||||
import { Text } from '@astryxdesign/core/Text';
|
||||
import { Skeleton } from '@astryxdesign/core/Skeleton';
|
||||
|
||||
// ==============================|| STAT / KPI CARD (Doormile-style) ||============================== //
|
||||
// Clean metric card: muted eyebrow label, large tabular value, a single
|
||||
// soft-tinted rounded tile holding the icon (with a faint matching ring for
|
||||
// depth). A low resting elevation lifts the tile off the page instead of
|
||||
// reading as a flat bordered box. No coloured top-stripe, no rainbow —
|
||||
// colour appears only in the icon tile so a row of cards reads calm and
|
||||
// corporate.
|
||||
// ==============================|| STAT / KPI CARD (Doormile-style Modern) ||============================== //
|
||||
// Modern, space-efficient KPI tile:
|
||||
// - Left-aligned icon tile directly grouped with the metric stack to eliminate internal dead space.
|
||||
// - Soft ambient accent gradient tint based on semantic status color.
|
||||
// - Crisp typography and tight vertical footprint (~56px height) saving ~30% page height.
|
||||
// - Micro-hover interaction (subtle lift + shadow depth).
|
||||
//
|
||||
// `icon` is a rendered node, e.g. icon={<MdLocalShipping size={20} />}.
|
||||
// `color` is a hex accent (defaults to brand red); `caption` is the small
|
||||
// muted line under the value (e.g. "96% of total").
|
||||
//
|
||||
// Astryx note: the icon tile keeps an inline `style` because the accent is a
|
||||
// per-call hex from the caller (each KPI tile carries its own semantic status
|
||||
// colour — see CLAUDE.md's status palette), which Astryx's fixed `variant`
|
||||
// and semantic `color` enums can't express. Same escape hatch login.js uses
|
||||
// for the brand gradient panel.
|
||||
// `color` is a hex accent (defaults to brand black); `caption` is optional supporting text.
|
||||
|
||||
export default function StatCard({ title, value, icon, color = '#000000', caption, loading = false }) {
|
||||
return (
|
||||
<Card height="100%" padding={2} elevation="low">
|
||||
<HStack justify="between" vAlign="center" gap={2}>
|
||||
<VStack gap={0.5} padding={0} minWidth={0} width="100%">
|
||||
<Text type="supporting" size="xsm" weight="semibold" maxLines={1}>
|
||||
{title}
|
||||
</Text>
|
||||
{loading ? (
|
||||
<Skeleton width={60} height={26} radius={2} />
|
||||
) : (
|
||||
<Text size="xl" weight="bold" display="block" maxLines={1} hasTabularNumbers>
|
||||
{value}
|
||||
</Text>
|
||||
)}
|
||||
{caption && (
|
||||
<Text type="supporting" size="sm" display="block">
|
||||
{caption}
|
||||
</Text>
|
||||
)}
|
||||
</VStack>
|
||||
<Card
|
||||
height="100%"
|
||||
padding={1.5}
|
||||
elevation="none"
|
||||
className="dt-stat-card"
|
||||
style={{
|
||||
position: 'relative',
|
||||
background: `linear-gradient(135deg, ${color}0A 0%, rgba(255, 255, 255, 0.98) 45%, #ffffff 100%)`,
|
||||
border: `1px solid ${color}24`,
|
||||
borderRadius: 'var(--radius-container, 6px)',
|
||||
boxShadow: '0 1px 2px rgba(15, 23, 42, 0.04)',
|
||||
cursor: 'default'
|
||||
}}
|
||||
>
|
||||
<HStack vAlign="center" gap={2} style={{ width: '100%', minHeight: 40 }}>
|
||||
{icon && (
|
||||
<Center
|
||||
width={36}
|
||||
height={36}
|
||||
width={34}
|
||||
height={34}
|
||||
className="dt-stat-icon"
|
||||
style={{
|
||||
flexShrink: 0,
|
||||
borderRadius: 'var(--radius-element)',
|
||||
backgroundColor: `${color}16`,
|
||||
borderRadius: 'var(--radius-element, 6px)',
|
||||
backgroundColor: `${color}14`,
|
||||
color,
|
||||
border: `1px solid ${color}2A`
|
||||
}}
|
||||
@@ -61,6 +50,51 @@ export default function StatCard({ title, value, icon, color = '#000000', captio
|
||||
{icon}
|
||||
</Center>
|
||||
)}
|
||||
<VStack gap={0} padding={0} minWidth={0} style={{ flex: 1 }}>
|
||||
<Text
|
||||
type="supporting"
|
||||
size="xsm"
|
||||
weight="semibold"
|
||||
maxLines={1}
|
||||
style={{
|
||||
fontSize: '11.5px',
|
||||
color: 'var(--color-text-supporting, #64748b)',
|
||||
letterSpacing: '0.01em',
|
||||
lineHeight: 1.2
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
{loading ? (
|
||||
<Skeleton width={48} height={20} radius={2} style={{ marginTop: 2 }} />
|
||||
) : (
|
||||
<Text
|
||||
size="lg"
|
||||
weight="bold"
|
||||
display="block"
|
||||
maxLines={1}
|
||||
hasTabularNumbers
|
||||
style={{
|
||||
fontSize: '19px',
|
||||
lineHeight: 1.2,
|
||||
color: 'var(--color-text-primary, #0f172a)',
|
||||
marginTop: 1
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
</Text>
|
||||
)}
|
||||
{caption && (
|
||||
<Text
|
||||
type="supporting"
|
||||
size="xsm"
|
||||
display="block"
|
||||
style={{ fontSize: '11px', marginTop: 1 }}
|
||||
>
|
||||
{caption}
|
||||
</Text>
|
||||
)}
|
||||
</VStack>
|
||||
</HStack>
|
||||
</Card>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user