Kurs vibe coding z AI · Moduł 8: Slack Clone

Panel analityczny - metryki i wykresy

3 min czytania
W tej lekcji3

Kapitan chce wiedzieć, czy Ship's Comms żyje: ile osób zagląda, ile klika i ilu robi to, na czym nam zależy. Bez liczb to żegluga we mgle. Zbudujemy panel analityczny z kartami statystyk i wykresem, czyli tablicę nawigacyjną na mostku.

Co mierzymy

Najpierw słowniczek. Views to wyświetlenia, clicks to kliknięcia, a conversions to konwersje, czyli wykonanie celu, np. rejestracja albo zakup. Każdy kolejny krok robi mniej osób, więc konwersji jest zwykle najmniej. Dlatego przy każdej liczbie pokazujemy też zmianę względem poprzedniego okresu, bo sama liczba nie powie, czy płyniemy w dobrą stronę.

Wykresy z Recharts

Do wykresów użyjemy biblioteki Recharts, która udostępnia wykresy jako komponenty Reacta. Instalacja to jedno polecenie w terminalu:

npm install recharts

Polecenie dopisuje recharts do zależności w package.json, ale niczego jeszcze nie rysuje. README biblioteki zaleca doinstalować też pakiet react-is w wersji zgodnej z Twoim Reactem. Wykres liniowy składasz potem z klocków:

import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip } from 'recharts';

function ViewsChart() {
  const data = [
    { date: '2024-01', views: 400 },
    { date: '2024-02', views: 300 },
    { date: '2024-03', views: 600 },
    { date: '2024-04', views: 800 }
  ];

  return (
    <LineChart width={600} height={300} data={data}>
      <CartesianGrid strokeDasharray="3 3" />
      <XAxis dataKey="date" />
      <YAxis />
      <Tooltip />
      <Line type="monotone" dataKey="views" stroke="#3b82f6" />
    </LineChart>
  );
}

Dane to tablica obiektów, a Recharts odczytuje z nich pola po nazwie podanej w dataKey. XAxis bierze miesiąc z dataKey="date", a Line rysuje wartości z dataKey="views". CartesianGrid dodaje przerywaną siatkę, Tooltip pokazuje wartość po najechaniu, a type="monotone" wygładza linię. Dane się nie zmieniają, zmienia się tylko ich obraz. Stałe width={600} łatwo wychodzi poza ekran telefonu, więc żeby wykres wypełniał szerokość rodzica, owiń go w ResponsiveContainer. W Next.js z App Routerem umieść wykres w komponencie oznaczonym 'use client', bo Recharts działa w przeglądarce.

Karty statystyk

Nad wykresem stoją karty z najważniejszymi liczbami:

function StatsGrid() {
  const stats = [
    { label: 'Total Views', value: '12.5K', change: '+12%', up: true },
    { label: 'Clicks', value: '3.2K', change: '+5%', up: true },
    { label: 'Conversions', value: '450', change: '-2%', up: false }
  ];

  return (
    <div className="grid grid-cols-3 gap-4">
      {stats.map(stat => (
        <div key={stat.label} className="border rounded p-4 bg-white">
          <p className="text-sm text-gray-600">{stat.label}</p>
          <p className="text-3xl font-bold">{stat.value}</p>
          <p className={`text-sm ${stat.up ? 'text-green-600' : 'text-red-600'}`}>
            {stat.change}
          </p>
        </div>
      ))}
    </div>
  );
}

Każda karta (stat) ma cztery pola: label, value, change i up. Gdy up jest prawdą, zmiana jest zielona (text-green-600), a przy spadku czerwona (text-red-600). Kolor wybiera wyrażenie warunkowe w className, więc dane nie muszą znać kolorów. W przykładzie zmiana jest wpisana na sztywno, a w prawdziwym panelu wyliczysz ją z dwóch okresów:

const changePercent = Math.round(((current - previous) / previous) * 100);
const up = changePercent >= 0;

Dla 450 konwersji wobec 460 miesiąc wcześniej wychodzi -2%, więc up to false. Same liczby zostają nietknięte, powstają z nich tylko dwa nowe pola.

Prosty wykres słupkowy zbudujesz nawet bez biblioteki, z samych elementów div. Wysokość słupka to procent największej wartości:

const maxViews = Math.max(...chartData.map(d => d.views));

function barHeight(views: number) {
  return `${(views / maxViews) * 100}%`;
}

Najwyższy słupek dostaje 100%, pozostałe proporcjonalnie mniej. Wynik przypiszesz w style={{ height: barHeight(d.views) }}, a dane wejściowe zostają nietknięte.

Rada Kapitana Redbearda: zanim narysujesz wykres, zapytaj, jaką decyzję ma ułatwić. Ładna krzywa, z której nic nie wynika, to tylko ozdoba na maszcie.

Zapamiętaj: karta pokazuje liczbę i kierunek zmiany, a wykres pokazuje trend. W edytorze poniżej masz panel analityczny z kartami statystyk, a zadania sprawdzą bibliotekę wykresów i karty statystyk.

Kod do tej lekcji: App.jsx
// Panel analityczny - karty statystyk i wykres słupkowy
import { useState } from 'react';

const stats = [
  { label: 'Wyświetlenia', value: '12.5K', change: '+12%', up: true },
  { label: 'Kliknięcia', value: '3.2K', change: '+5%', up: true },
  { label: 'Konwersje', value: '450', change: '-2%', up: false }
];

const chartData = [
  { month: 'Sty', views: 400 },
  { month: 'Lut', views: 300 },
  { month: 'Mar', views: 600 },
  { month: 'Kwi', views: 800 }
];

export default function App() {
  const maxViews = Math.max(...chartData.map(d => d.views));

  return (
    <div className="min-h-screen bg-gray-100 p-8">
      <div className="max-w-4xl mx-auto">
        <h1 className="text-2xl font-bold mb-6">Panel analityczny</h1>

        {/* Siatka kart statystyk */}
        <div className="grid grid-cols-3 gap-4 mb-8">
          {stats.map(stat => (
            <div key={stat.label} className="border rounded p-4 bg-white">
              <p className="text-sm text-gray-600">{stat.label}</p>
              <p className="text-3xl font-bold">{stat.value}</p>
              <p className={`text-sm ${stat.up ? 'text-green-600' : 'text-red-600'}`}>
                {stat.change}
              </p>
            </div>
          ))}
        </div>

        {/* Prosty wykres słupkowy */}
        <div className="bg-white border rounded p-6">
          <h2 className="font-semibold mb-4">Wyświetlenia w kolejnych miesiącach</h2>
          <div className="flex items-end gap-4 h-48">
            {chartData.map(d => (
              <div key={d.month} className="flex-1 flex flex-col items-center justify-end">
                <div
                  className="w-full bg-blue-500 rounded-t"
                  style={{ height: `${(d.views / maxViews) * 100}%` }}
                ></div>
                <span className="text-xs text-gray-600 mt-2">{d.month}</span>
                <span className="text-xs font-semibold">{d.views}</span>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Której biblioteki używamy do wykresów w dashboardzie?

  2. 2. Jakie pola ma pojedyncza karta statystyk (stat)?

To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.

Zadania praktyczne w grze

  • Edytor kodu

    Analytics dashboard z kartami i wykresem

  • Układanie w poziomie

    Ułóż polecenie instalacji biblioteki wykresów (jedna linia, lewo-prawo):

Przydatne artykuły