Files
GMW/design/patterns/07-data-visualization.md
T
asepharyana d59b59a7a7 feat: migrate frontend to Astro + expand AI moderation + backend admin/runtime config
Frontend:
- migrate from Vite to Astro (astro.config.mjs, pages/, layouts/)
- add admin panel, settings page, command palette, error boundary
- refactor App.tsx, MascotChatbot, Sidebar, Header, DashboardLayout
- update API client, WebSocket, auth, dashboard features

Backend:
- add admin module and config routes
- refactor middlewares, Redis connection, WebSocket server/bridge
- add runtime config loader

Discord Gateway:
- refactor AI moderation: circuit breaker, concurrency limiter, fallback processor
- add media analysis client, Seaxng search, user profile learner
- add new drizzle migration

Shared:
- extend database schema, add new config fields
2026-07-02 00:02:41 +07:00

4.2 KiB

Data Visualization — Painting with Numbers

"The greatest value of a picture is when it forces us to notice what we never expected to see." — John Tukey


🎯 Filosofi Data Visual

Data visualisasi di BETE adalah cerita tentang data yang:

  1. Jujur — Tidak memanipulasi sumbu atau skala
  2. Kontekstual — Setiap angka punya pembanding
  3. Hierarkis — Overview dulu, detail kemudian

🎨 Chart Color Palette

:root {
  /* Sequential (single hue) */
  --chart-blue-1: oklch(0.85 0.060 255);
  --chart-blue-2: oklch(0.70 0.100 255);
  --chart-blue-3: oklch(0.55 0.150 255);
  --chart-blue-4: oklch(0.40 0.150 255);
  --chart-blue-5: oklch(0.30 0.120 255);

  /* Categorical */
  --chart-cat-1: oklch(0.55 0.175 255);    /* Blue */
  --chart-cat-2: oklch(0.60 0.130 145);    /* Green */
  --chart-cat-3: oklch(0.65 0.150 50);     /* Orange */
  --chart-cat-4: oklch(0.55 0.165 25);     /* Red */
  --chart-cat-5: oklch(0.50 0.100 285);    /* Purple */
  --chart-cat-6: oklch(0.65 0.120 200);    /* Cyan */
  --chart-cat-7: oklch(0.60 0.110 350);    /* Pink */
  --chart-cat-8: oklch(0.70 0.100 85);     /* Yellow */
}

📊 Chart Types & Usage

1. Stat Card (KPI)

interface StatCardProps {
  label: string;
  value: number | string;
  trend?: { direction: 'up' | 'down' | 'flat'; percentage: number; period: string; };
  icon: ReactNode;
  color?: 'primary' | 'success' | 'warning' | 'destructive';
}

Layout:

┌─────────────────────┐
│ [icon]    Label      │
│ 1,234     ▲ 12.3%   │
│          vs last wk  │
└─────────────────────┘

2. Line Chart (Trend)

Use: Message volume per day, moderation per hour Rules: Y-axis dari 0. Gradient subtle below line. Hover tooltip.

3. Bar Chart (Comparison)

Use: Top channels, severity distribution Rules: Horizontal untuk >5 kategori. Max 20 bars.

4. Donut Chart (Composition)

Use: Message type, severity breakdown Rules: Max 6 segmen. <3% collaps ke "Other". Center = total.

5. Heatmap Calendar (Activity)

Use: User activity by day/hour Rules: Sumbu X = hari, Y = jam. Satu warna accent.


📐 Chart Styling Tokens

.chart-container {
  --chart-padding: var(--sp-4);
  --chart-label-size: var(--fs-xs);
  --chart-tick-count: 5;
  --chart-grid-opacity: 0.1;
  --chart-line-width: 2px;
}

.chart-tooltip {
  background: var(--clr-surface-overlay);
  backdrop-filter: blur(8px);
  border: 1px solid var(--clr-border);
  border-radius: var(--rd-md);
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-sm);
  box-shadow: var(--sh-elevated);
}

🔍 Drill-down Pattern

function MessageTrendChart() {
  const [granularity, setGranularity] = useState<'daily' | 'hourly' | '15min'>('daily');

  const handlePointClick = (date: Date) => {
    if (granularity === 'daily') setGranularity('hourly');
    else if (granularity === 'hourly') setGranularity('15min');
  };

  return (
    <ChartCard title="Message Volume"
      onBack={granularity !== 'daily' ? () => setGranularity('daily') : undefined}>
      <LineChart data={data} granularity={granularity} onClick={handlePointClick} />
    </ChartCard>
  );
}

⚠️ Anti-Patterns

Truncated Y-axis

// ❌ Y axis mulai dari 50, memperbesar perbedaan
const options = { yAxis: { min: 50 } };
// ✅ Mulai dari 0
const options = { yAxis: { min: 0 } };

Terlalu banyak warna

// ❌ JANGAN — setiap bar beda warna
<Bar data={data} fill={['#ff0000', '#00ff00', '#0000ff', ...]} />
// ✅ Sequential scale
<Bar data={data} colorScale="sequential" />

3D charts — mendistorsi persepsi

// ❌ JANGAN
<PieChart><Pie data={data} style={{ filter: 'drop-shadow(...)' }} /></PieChart>
// ✅ 2D
<PieChart><Pie data={data} /></PieChart>

🔗 Referensi

Sumber Konsep
Recharts React chart library
Chartability Accessible charts
Tufte Minimalist chart design

"Angka adalah ingatan yang terukur — setiap titik data adalah kisah yang menanti." ❄️🩵