복합 스탯 카드
지표 하나를 값, 앞 기간 대비, 흐름 차트, 하위 지표 줄까지 한 장에 담는 표시 카드다. StatCard 가 지금 값을 보여 준다면 이 카드는 그 값이 어떻게 나왔는지까지 보여 준다. 머리는 StatCard 와 같은 조각이라 두 카드를 한 화면에 섞어도 값 크기와 색이 같다. 차트 종류, 기간, 하위 지표 계산은 호출자가 정하고 카드는 넘겨받은 것을 세 칸에 배치한다.
상태
최근 8주
- 취소
- 31건
- 객단가
- 3.3만
- 재구매
- 41%
최근 8주
- 취소
- 객단가
- 재구매
최근 14일
이 기간에는 기록이 없다. 마지막 기록 9월 2일
- 대상
- 5곳
- 기록한 날
- 0 / 30일
- 마지막
- 9월 2일
- 신규
- 214명
- 복귀
- 87명
차트 칸은 chart 로 정한다. 넘기지 않으면 칸이 없고, null · false 를 넘기면 칸을 남긴 채 chartEmpty 문구를 chartHeight 높이로 보인다. 그래서 격자 안에서 데이터가 빈 카드도 옆 카드와 높이가 맞는다.
활용 방법
축 있는 차트
차트 칸에는 deux 차트를 그대로 넣는다. 막대에 마우스를 올리면 그날 값이 툴팁으로 뜬다. 넓은 카드는 value, 24rem 보다 좁은 카드는 compactValue 를 보인다.
최근 14일 · 날마다 매출
10/4
매출182만
10/5
매출240만
10/6
매출96만
10/7
매출310만
10/8
매출275만
10/9
매출120만
10/10
매출88만
10/11
매출402만
10/12
매출356만
10/13
매출298만
10/14
매출330만
10/15
매출141만
10/16
매출260만
10/17
매출384만
- 주문
- 1,284건
- 객단가
- 3.3만
- 환불
- 12건
<CompositeStatCardicon={<Wallet size={16} />}tone="accent"label="이번 달 매출"valuePrefix="₩"value="42,800,000"compactValue="4,280만"comparison="지난달 같은 날 3,910만"trend={{ direction: "up", label: "+9%" }}chartLabel="최근 14일 · 날마다 매출"chart={<BarChart categories={days} series={[{ label: "매출", values: sales, tone: "primary" }]} height={120} />}chartHeight={120}metrics={[{ label: "주문", value: "1,284", valueSuffix: "건" },{ label: "객단가", value: "3.3만" },{ label: "환불", value: 12, valueSuffix: "건", tone: "warning" },]}/>
내려가야 좋은 지표
trend.tone 이 배지 색을 정한다. 응답 시간처럼 내려가야 좋은 지표는 down 에 success 를, 비용처럼 올라가면 나쁜 지표는 up 에 danger 를 준다. 기준선은 차트의 references 가 그린다.
최근 8주 · 기준 250ms
1주
p95262ms
2주
p95248ms
3주
p95251ms
4주
p95230ms
5주
p95214ms
6주
p95205ms
7주
p95196ms
8주
p95182ms
- 오류율
- 0.4%
- 요청
- 1.2M
- 느린 요청
- 38건
최근 8주
1주
비용38만
2주
비용41만
3주
비용40만
4주
비용39만
5주
비용44만
6주
비용47만
7주
비용52만
8주
비용55만
- 연산
- 121만
- 저장
- 48만
- 전송
- 23만
<CompositeStatCardlabel="응답 시간 p95"value={182}valueSuffix="ms"trend={{ direction: "down", label: "-7%", tone: "success" }}chart={<LineChart series={[{ label: "p95", points: p95 }]} labels={weeks} height={120} references={[{ value: 250, label: "기준" }]} />}chartHeight={120}metrics={[...]}/><CompositeStatCardlabel="이번 달 서버 비용"value="1,920,000"trend={{ direction: "up", label: "+19%", tone: "danger" }}.../>
분포 띠와 지표 링크
차트 칸에 DistBar 를 넣으면 전체가 어떤 상태로 나뉘는지 보인다. metrics[].href 를 준 칸은 값이 링크가 되어, 눌러서 그 조건으로 걸러진 목록으로 간다.
<CompositeStatCardicon={<Inbox size={16} />}tone="danger"label="처리 대기 문의"value={48}valueSuffix="건"chartLabel="답변 기한까지"chart={<DistBar segments={segments} legend height={12} />}metrics={[{ label: "기한 지남", value: 5, valueSuffix: "건", tone: "danger", href: "?due=over" },{ label: "오늘", value: 7, valueSuffix: "건", tone: "warning", href: "?due=today" },{ label: "담당 없음", value: 3, valueSuffix: "건", href: "?assignee=none" },]}/>
비율과 게이지
한도가 정해진 지표는 큰 값에 쓴 양을, Gauge 에 한도 대비 비율을 둔다. 같은 숫자를 두 번 쓰지 않는다. 하위 지표가 둘이면 두 칸으로 나뉜다.
- 남음
- 280GB
- 이번 달 증가
- 42GB
<CompositeStatCardicon={<GaugeIcon size={16} />}tone="warning"label="저장 공간"value={720}valueSuffix="GB"comparison="한도 1TB"chart={<Gauge percent={72} label="사용" tone="warning" size={150} />}metrics={[{ label: "남음", value: 280, valueSuffix: "GB" },{ label: "이번 달 증가", value: 42, valueSuffix: "GB", tone: "warning" },]}/>
링크는 머리만
href 를 주면 머리(라벨과 값)만 링크가 되고 라벨 옆에 화살표가 붙는다. 카드 전체를 링크로 감싸지 않는 것은 차트 칸과 지표 칸에도 링크가 들어가기 때문이다. 링크 안의 링크는 HTML 에서 허용되지 않는다.
<CompositeStatCardlabel="이번 주 주문"value={1284}valueSuffix="건"href="?view=orders"chart={<Sparkline points={weekly} width={280} height={40} />}metrics={[{ label: "취소", value: 31, valueSuffix: "건", href: "?status=canceled" },{ label: "환불", value: 12, valueSuffix: "건", href: "?status=refunded" },]}/>
지표 네 칸
하위 지표는 네 칸까지 그리고 다섯 번째부터는 그리지 않는다. 칸 수에 따라 열이 정해지고 카드 폭으로 접힌다. 4칸은 20rem 보다 좁으면 2 × 2, 3칸은 16rem 보다 좁으면 한 줄에 하나다. 전체 화면(⤢)으로 열면 4칸이 한 줄로 선다.
- 웹
- 388
- 앱
- 201
- 초대
- 41
- 기타
- 12
<CompositeStatCardlabel="이번 달 가입"value={642}valueSuffix="명"metrics={[{ label: "웹", value: 388 },{ label: "앱", value: 201 },{ label: "초대", value: 41 },{ label: "기타", value: 12 },]}/>
격자에 늘어놓기
카드는 배치를 맡지 않는다. 격자는 호출자가 짠다. 차트 칸이 남는 높이를 채우므로 한 줄의 카드는 하위 지표 줄이 같은 높이에 맞춰진다.
최근 14일
10/4
주문182
10/5
주문240
10/6
주문96
10/7
주문310
10/8
주문275
10/9
주문120
10/10
주문88
10/11
주문402
10/12
주문356
10/13
주문298
10/14
주문330
10/15
주문141
10/16
주문260
10/17
주문384
- 취소
- 31건
- 객단가
- 3.3만
- 재구매
- 41%
최근 14일
이 기간에는 기록이 없다
- 대상
- 5곳
- 기록한 날
- 0 / 30일
- 마지막
- 9월 2일
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4"><CompositeStatCard ... chart={<BarChart ... height={100} />} chartHeight={100} /><CompositeStatCard ... chart={hasData && <BarChart ... height={100} />} chartHeight={100} chartEmpty="이 기간에는 기록이 없다" /></div>
언제 쓰지 않나
- · 값 하나와 추세만 보이면 된다 → StatCard. 작은 스파크라인까지는 StatCard 의 chart 슬롯으로 충분하다.
- · 회사 · 서비스 같은 대상 하나에 지표가 여럿 붙는다 → DataStatCard.
- · 차트가 주인공이고 값은 제목 옆 보조다 → Panel 안에 차트를 둔다.
- · 지표 여러 개를 행 · 열로 견준다 → DataTable · DataGrid.
속성
| 이름 | 타입 | 기본값 | 설명 |
|---|---|---|---|
| label | string | - | 카드 정체성 텍스트 |
| value | number | string | undefined | - | 큰 값. undefined면 '-' |
| icon | ReactNode | - | 없으면 아이콘 자리를 그리지 않는다 |
| tone | "neutral" | "accent" | "success" | "warning" | "danger" | "neutral" | 아이콘 박스 의미색 |
| valuePrefix | string | - | 값 앞 단위 (₩, $ 등) |
| valueSuffix | string | - | 값 뒤 단위 (건, % 등) |
| compactValue | number | string | - | 머리 폭 24rem 미만에서 value 대신 보일 짧은 값 |
| sign | "positive" | "negative" | - | 값 색·부호: positive=green "+", negative=red "-" |
| comparison | string | - | 값 옆 비교 문구 |
| description | string | - | 값 아래 보조 설명 |
| trend | { direction: "up" | "down"; label: string; tone?: "success" | "danger" | "neutral" } | - | 추세 배지. tone이 없으면 up=success, down=danger |
| chart | ReactNode | - | 차트 칸. undefined면 칸 없음, null · false면 chartEmpty를 보인다 |
| chartLabel | string | - | 차트 칸 위 작은 제목 |
| chartEmpty | ReactNode | "데이터 없음" | 차트가 비었을 때 문구 |
| chartHeight | number | 96 | 비었을 때 · 로딩 때 차트 칸 높이(px). 넘기는 차트의 height와 맞춘다 |
| metrics | { label: string; value: number | string | undefined; valueSuffix?: string; tone?: "success" | "warning" | "danger"; href?: string }[] | - | 하위 지표 1~4칸. 4칸은 좁은 카드에서 2 × 2 |
| href | string | - | 머리를 링크로. onClick보다 우선 |
| onClick | MouseEventHandler | - | 머리를 버튼으로(href 없을 때) |
| loading | boolean | false | 값 · 차트 칸 · 지표 값 자리를 pulse로 |
| className | string | - | root(div)에 붙는다. native props와 ref도 root로 간다 |