Claude Code Plugins

Community-maintained marketplace

Feedback

Reusable React/JavaScript patterns for Somali dialect classifier dashboard. Covers Chart.js integration, data card components, filter patterns, responsive layouts, and dashboard-specific UI patterns. Auto-invokes when building dashboard components, charts, data visualizations, or dashboard UI.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name dashboard-patterns
description Reusable React/JavaScript patterns for Somali dialect classifier dashboard. Covers Chart.js integration, data card components, filter patterns, responsive layouts, and dashboard-specific UI patterns. Auto-invokes when building dashboard components, charts, data visualizations, or dashboard UI.
allowed-tools Read, Grep

Dashboard Patterns for Somali Dialect Classifier

Chart.js Integration

Pattern 1: Bar Chart Component

function DialectDistributionChart({ data }) {
  const chartRef = useRef(null);
  const chartInstance = useRef(null);

  useEffect(() => {
    if (!chartRef.current) return;

    // Destroy previous chart
    if (chartInstance.current) {
      chartInstance.current.destroy();
    }

    const ctx = chartRef.current.getContext('2d');

    chartInstance.current = new Chart(ctx, {
      type: 'bar',
      data: {
        labels: ['Northern', 'Southern', 'Central'],
        datasets: [{
          label: 'Records',
          data: [data.northern, data.southern, data.central],
          backgroundColor: ['#33BBEE', '#0077BB', '#66CCEE']  // Data colors
        }]
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        plugins: {
          legend: {
            display: false
          }
        }
      }
    });

    return () => {
      if (chartInstance.current) {
        chartInstance.current.destroy();
      }
    };
  }, [data]);

  return (
    <div className="chart-container" style={{ height: '300px' }}>
      <canvas ref={chartRef}></canvas>
    </div>
  );
}

Pattern 2: Line Chart with Time Series

function MetricsOverTimeChart({ timeSeriesData }) {
  const chartRef = useRef(null);

  useEffect(() => {
    if (!chartRef.current) return;

    const ctx = chartRef.current.getContext('2d');

    new Chart(ctx, {
      type: 'line',
      data: {
        labels: timeSeriesData.map(d => d.date),
        datasets: [{
          label: 'Accuracy',
          data: timeSeriesData.map(d => d.accuracy),
          borderColor: '#33BBEE',  // Data cyan
          backgroundColor: 'rgba(51, 187, 238, 0.1)',
          tension: 0.4
        }]
      },
      options: {
        responsive: true,
        plugins: {
          tooltip: {
            callbacks: {
              label: (context) => `Accuracy: ${(context.parsed.y * 100).toFixed(1)}%`
            }
          }
        }
      }
    });
  }, [timeSeriesData]);

  return <canvas ref={chartRef}></canvas>;
}

Data Card Components

Pattern 1: Metric Card

function MetricCard({ label, value, trend, trendDirection }) {
  return (
    <div className="data-card">
      <div className="data-card__label">{label}</div>
      <div className="data-card__value">{value.toLocaleString()}</div>
      {trend && (
        <div className="data-card__trend">
          {trendDirection === 'up' && <span className="trend-icon">↑</span>}
          {trendDirection === 'down' && <span className="trend-icon">↓</span>}
          <span className="data-card__trend-text">{trend}</span>
        </div>
      )}
    </div>
  );
}

// Usage
<MetricCard
  label="Total Records"
  value={12345}
  trend="+12.5% from last month"
  trendDirection="up"
/>

Pattern 2: Grid of Metric Cards

function MetricsGrid({ metrics }) {
  return (
    <div className="metrics-grid">
      {metrics.map((metric, idx) => (
        <MetricCard
          key={idx}
          label={metric.label}
          value={metric.value}
          trend={metric.trend}
          trendDirection={metric.trendDirection}
        />
      ))}
    </div>
  );
}

// CSS
.metrics-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 1.5rem;
  margin-bottom: 2rem;
}

Filter Patterns

Pattern 1: Toggle Capsule Filter

function DialectFilter({ selected, onChange }) {
  const dialects = ['All', 'Northern', 'Southern', 'Central'];

  return (
    <div className="data-toggle">
      {dialects.map(dialect => (
        <button
          key={dialect}
          className={`data-toggle__option ${
            selected === dialect ? 'data-toggle__option--active' : ''
          }`}
          onClick={() => onChange(dialect)}
        >
          {dialect}
        </button>
      ))}
    </div>
  );
}

Pattern 2: Date Range Filter

function DateRangeFilter({ startDate, endDate, onChange }) {
  return (
    <div className="filter-group">
      <label>Date Range</label>
      <div className="date-inputs">
        <input
          type="date"
          value={startDate}
          onChange={(e) => onChange({ start: e.target.value, end: endDate })}
          className="form__input"
        />
        <span>to</span>
        <input
          type="date"
          value={endDate}
          onChange={(e) => onChange({ start: startDate, end: e.target.value })}
          className="form__input"
        />
      </div>
    </div>
  );
}

Data Fetching Patterns

Pattern 1: Custom Hook for Data Fetching

function useDataFetch(endpoint) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchData() {
      try {
        setLoading(true);
        const response = await fetch(endpoint);
        if (!response.ok) throw new Error('Failed to fetch');
        const json = await response.json();
        setData(json);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }

    fetchData();
  }, [endpoint]);

  return { data, loading, error };
}

// Usage
function DashboardView() {
  const { data, loading, error } = useDataFetch('/api/metrics');

  if (loading) return <LoadingSpinner />;
  if (error) return <ErrorMessage message={error} />;

  return <MetricsGrid metrics={data} />;
}

Loading & Empty States

Pattern 1: Loading Skeleton

function ChartSkeleton() {
  return (
    <div className="chart-skeleton">
      <div className="skeleton-bar" />
      <div className="skeleton-bar" />
      <div className="skeleton-bar" />
    </div>
  );
}

// CSS
.skeleton-bar {
  height: 200px;
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: loading 1.5s infinite;
  border-radius: 8px;
  margin-bottom: 1rem;
}

@keyframes loading {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

Pattern 2: Empty State

function EmptyState({ message, action }) {
  return (
    <div className="empty-state">
      <svg className="empty-state__icon" width="64" height="64">
        {/* Icon SVG */}
      </svg>
      <p className="empty-state__message">{message}</p>
      {action && (
        <button className="btn btn--primary" onClick={action.onClick}>
          {action.label}
        </button>
      )}
    </div>
  );
}

// Usage
<EmptyState
  message="No data available. Upload your first dataset to get started."
  action={{
    label: "Upload Dataset",
    onClick: handleUpload
  }}
/>

Responsive Layout Patterns

Pattern 1: Responsive Grid

.dashboard-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 1.5rem;
  padding: 2rem;
}

.dashboard-grid__full {
  grid-column: 1 / -1;
}

.dashboard-grid__half {
  grid-column: span 6;
}

.dashboard-grid__third {
  grid-column: span 4;
}

@media (max-width: 768px) {
  .dashboard-grid__half,
  .dashboard-grid__third {
    grid-column: 1 / -1;
  }
}

Error Handling

Pattern 1: Error Boundary

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }

  componentDidCatch(error, errorInfo) {
    console.error('Dashboard error:', error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return (
        <div className="error-message">
          <h2>Something went wrong</h2>
          <p>{this.state.error.message}</p>
          <button onClick={() => window.location.reload()}>
            Reload Dashboard
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

// Usage
<ErrorBoundary>
  <DashboardView />
</ErrorBoundary>

Performance Optimization

Pattern 1: Memoized Chart Component

const MemoizedChart = React.memo(({ data }) => {
  return <DialectDistributionChart data={data} />;
}, (prevProps, nextProps) => {
  // Only re-render if data actually changed
  return JSON.stringify(prevProps.data) === JSON.stringify(nextProps.data);
});

Pattern 2: Debounced Filter

function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => clearTimeout(handler);
  }, [value, delay]);

  return debouncedValue;
}

// Usage in search filter
function SearchFilter() {
  const [searchTerm, setSearchTerm] = useState('');
  const debouncedSearch = useDebounce(searchTerm, 300);

  useEffect(() => {
    if (debouncedSearch) {
      fetchFilteredData(debouncedSearch);
    }
  }, [debouncedSearch]);

  return (
    <input
      type="text"
      value={searchTerm}
      onChange={(e) => setSearchTerm(e.target.value)}
      placeholder="Search..."
    />
  );
}

When This Skill Activates

This skill auto-invokes when you mention:

  • Dashboard, dashboard components, dashboard UI
  • Chart.js, charts, data visualization
  • React patterns, React components, React hooks
  • Data cards, metric cards, KPI cards
  • Filters, filtering, toggle filters
  • Loading states, empty states, skeletons
  • Responsive layout, grid layout
  • Data fetching, API integration

Version: 1.0.0 Last Updated: 2025-11-06 Project: Somali Dialect Classifier Dashboard