Skip to main content

@slopit/dashboard-ui API Reference

React components and utilities for the slopit analytics dashboard. This package provides a complete UI for viewing and analyzing behavioral data collected by the slopit TypeScript adapters and processed by the Python backend.

Installation​

pnpm add @slopit/dashboard-ui

Peer Dependencies​

The React components require React 18 or 19:

pnpm add react react-dom

Web Component Usage​

The package also exports a web component that can be used without React:

<script type="module" src="https://unpkg.com/@slopit/dashboard-ui/dist/slopit-dashboard-wc.js"></script>
<link rel="stylesheet" href="https://unpkg.com/@slopit/dashboard-ui/style.css">

<slopit-dashboard
api-url="http://localhost:8000/api/v1"
study-id="my-study"
theme="dark">
</slopit-dashboard>

Quick Start​

import { Dashboard } from "@slopit/dashboard-ui";
import "@slopit/dashboard-ui/style.css";

function App() {
return (
<Dashboard
config={{
apiUrl: "http://localhost:8000/api/v1",
studyId: "my-study",
enableWebSocket: true,
theme: "system",
}}
/>
);
}

Components​

Dashboard​

Main dashboard component combining session list, detail view, and analytics panels.

interface DashboardProps {
config: DashboardConfig;
}

interface DashboardConfig {
apiUrl: string;
studyId?: string;
theme?: "light" | "dark" | "system";
pollingInterval?: number;
enableWebSocket?: boolean;
}

Props:

PropertyTypeDefaultDescription
config.apiUrlstring(required)Base URL for the slopit API
config.studyIdstring-Study identifier to display in header
config.theme"light" | "dark" | "system""system"Color theme
config.pollingIntervalnumber-Polling interval in milliseconds (if WebSocket disabled)
config.enableWebSocketbooleantrueEnable real-time updates via WebSocket
<Dashboard
config={{
apiUrl: "http://localhost:8000/api/v1",
studyId: "experiment-001",
enableWebSocket: true,
theme: "dark",
}}
/>

SessionList​

Table displaying sessions with verdict badges and pagination.

interface SessionListProps {
apiClient: DashboardApiClient;
onSessionSelect?: (sessionId: string) => void;
filters?: SessionFilters;
pageSize?: number;
}

Props:

PropertyTypeDefaultDescription
apiClientDashboardApiClient(required)API client instance
onSessionSelect(sessionId: string) => void-Callback when a session row is clicked
filtersSessionFilters-Filter options for the list
pageSizenumber20Number of sessions per page
import { SessionList, createApiClient } from "@slopit/dashboard-ui";

const client = createApiClient("http://localhost:8000/api/v1");

<SessionList
apiClient={client}
onSessionSelect={(id) => navigate(`/sessions/${id}`)}
filters={{ verdict: "suspicious" }}
pageSize={25}
/>

SessionDetail​

Detailed view of a single session with trials and flags.

interface SessionDetailProps {
apiClient: DashboardApiClient;
sessionId: string;
onBack?: () => void;
}

Props:

PropertyTypeDescription
apiClientDashboardApiClientAPI client instance
sessionIdstringSession ID to display
onBack() => voidCallback for back navigation
<SessionDetail
apiClient={client}
sessionId="abc123def456"
onBack={() => navigate("/sessions")}
/>

AnalysisSummary​

Summary cards showing overall analysis statistics.

interface AnalysisSummaryProps {
summary: AnalysisSummaryData;
}

interface AnalysisSummaryData {
totalSessions: number;
analyzedSessions: number;
verdictDistribution: Record<VerdictType, number>;
flagDistribution: Record<string, number>;
averageConfidence: number;
}
<AnalysisSummary
summary={{
totalSessions: 150,
analyzedSessions: 142,
verdictDistribution: { clean: 100, suspicious: 32, flagged: 10 },
flagDistribution: { paste_detected: 25, timing_anomaly: 15 },
averageConfidence: 0.82,
}}
/>

VerdictBadge​

Badge displaying the verdict status with optional confidence score.

interface VerdictBadgeProps {
verdict: VerdictType;
confidence?: number;
showConfidence?: boolean;
className?: string;
}

type VerdictType = "clean" | "suspicious" | "flagged";

Props:

PropertyTypeDefaultDescription
verdictVerdictType(required)Verdict status to display
confidencenumber-Confidence score (0 to 1)
showConfidencebooleanfalseShow confidence as percentage
classNamestring-Additional CSS class
<VerdictBadge verdict="suspicious" confidence={0.75} showConfidence />

FlagList​

List of flags with severity indicators.

interface FlagListProps {
flags: VerdictFlag[];
groupByAnalyzer?: boolean;
}

interface VerdictFlag {
type: string;
severity: "low" | "medium" | "high";
message: string;
analyzer: string;
trialIndex?: number;
}

Props:

PropertyTypeDefaultDescription
flagsVerdictFlag[](required)Array of flags to display
groupByAnalyzerbooleanfalseGroup flags by analyzer name
<FlagList
flags={verdict.flags}
groupByAnalyzer
/>

TrialTimeline​

Timeline view of trials with expandable behavioral data.

interface TrialTimelineProps {
trials: SlopitTrial[];
onTrialSelect?: (trialIndex: number) => void;
}

Props:

PropertyTypeDescription
trialsSlopitTrial[]Array of trials to display
onTrialSelect(trialIndex: number) => voidCallback when a trial is expanded
<TrialTimeline
trials={session.trials}
onTrialSelect={(i) => console.log("Selected trial", i)}
/>

ConfidenceHistogram​

Histogram showing distribution of confidence scores.

interface ConfidenceHistogramProps {
scores: number[];
bins?: number;
title?: string;
}

Props:

PropertyTypeDefaultDescription
scoresnumber[](required)Array of confidence scores (0 to 1)
binsnumber10Number of histogram bins
titlestring-Chart title
<ConfidenceHistogram
scores={[0.2, 0.4, 0.6, 0.8, 0.95]}
bins={10}
title="Confidence Distribution"
/>

FlagDistribution​

Horizontal bar chart showing flag type distribution.

interface FlagDistributionProps {
distribution: Record<string, number>;
title?: string;
}
<FlagDistribution
distribution={{
paste_detected: 15,
excessive_blur: 8,
timing_anomaly: 3,
}}
title="Flag Distribution"
/>

ConnectionStatus​

Visual indicator for WebSocket connection status.

interface ConnectionStatusProps {
state: ConnectionState;
showLabel?: boolean;
}

type ConnectionState = "connecting" | "connected" | "disconnected" | "error";
const { connectionState } = useWebSocket({ url: "ws://localhost:8000/ws" });

<ConnectionStatus state={connectionState} showLabel />

Hooks​

useWebSocket​

Hook for managing WebSocket connection to the dashboard backend.

function useWebSocket(options: UseWebSocketOptions): UseWebSocketReturn;

interface UseWebSocketOptions {
url: string;
reconnect?: boolean;
reconnectInterval?: number;
maxReconnectAttempts?: number;
onSessionNew?: (data: SessionNewEventData) => void;
onVerdictComputed?: (data: VerdictComputedEventData) => void;
onSyncProgress?: (data: SyncProgressEventData) => void;
onError?: (error: Event) => void;
}

interface UseWebSocketReturn {
connectionState: ConnectionState;
lastMessage: WebSocketMessage | null;
connect: () => void;
disconnect: () => void;
}

Options:

PropertyTypeDefaultDescription
urlstring(required)WebSocket server URL
reconnectbooleantrueAuto-reconnect on disconnect
reconnectIntervalnumber3000Milliseconds between reconnect attempts
maxReconnectAttemptsnumber5Maximum reconnect attempts
onSessionNewfunction-Callback for new session events
onVerdictComputedfunction-Callback for verdict computed events
onSyncProgressfunction-Callback for sync progress events
onErrorfunction-Callback for connection errors

Returns:

PropertyTypeDescription
connectionStateConnectionStateCurrent connection state
lastMessageWebSocketMessage | nullMost recent message received
connect() => voidManually connect
disconnect() => voidManually disconnect
const { connectionState, lastMessage } = useWebSocket({
url: "ws://localhost:8000/ws",
onSessionNew: (data) => {
console.log("New session:", data.sessionId);
refreshSessionList();
},
onVerdictComputed: (data) => {
console.log("Verdict for", data.sessionId, ":", data.verdict);
},
});

API Client​

DashboardApiClient​

API client for dashboard REST endpoints.

class DashboardApiClient {
constructor(config: ApiClientConfig);

// Session endpoints
listSessions(page?: number, pageSize?: number, filters?: SessionFilters): Promise<PaginatedResponse<SessionSummary>>;
getSession(sessionId: string): Promise<SlopitSession>;
getSessionTrials(sessionId: string): Promise<SlopitTrial[]>;
getSessionVerdict(sessionId: string): Promise<SessionVerdict>;

// Analysis endpoints
getAnalysisSummary(): Promise<AnalysisSummary>;
getFlagDistribution(): Promise<Record<string, number>>;
getVerdictDistribution(): Promise<Record<string, number>>;
triggerBatchAnalysis(sessionIds?: string[]): Promise<{ taskId: string }>;

// Export endpoints
exportSessions(options: ExportOptions): Promise<Blob>;

// JATOS integration
connectJatos(url: string, token: string): Promise<{ status: string }>;
listJatosStudies(): Promise<Array<{ id: string; title: string }>>;
syncJatos(studyId: string): Promise<{ taskId: string }>;
getJatosStatus(): Promise<{ connected: boolean; url?: string }>;

// Prolific integration
connectProlific(token: string): Promise<{ status: string }>;
listProlificStudies(): Promise<Array<{ id: string; name: string; status: string }>>;
getProlificRecommendations(studyId: string): Promise<Array<{
participantId: string;
sessionId: string;
verdict: string;
recommendation: "approve" | "reject" | "review";
}>>;
}

interface ApiClientConfig {
baseUrl: string;
headers?: Record<string, string>;
}

createApiClient​

Factory function for creating an API client instance.

function createApiClient(baseUrl: string): DashboardApiClient;
import { createApiClient } from "@slopit/dashboard-ui";

const client = createApiClient("http://localhost:8000/api/v1");

// List sessions with filtering
const sessions = await client.listSessions(1, 20, { verdict: "flagged" });

// Get session details
const session = await client.getSession("abc123");
const trials = await client.getSessionTrials("abc123");
const verdict = await client.getSessionVerdict("abc123");

// Export data
const blob = await client.exportSessions({
format: "csv",
includeTrials: true,
filters: { verdict: "flagged" },
});

Types​

Session Types​

interface SessionSummary {
sessionId: string;
participantId?: string;
startTime: string;
endTime?: string;
trialCount: number;
verdict?: VerdictType;
confidence?: number;
flagCount: number;
}

interface SessionVerdict {
sessionId: string;
verdict: VerdictType;
confidence: number;
flags: VerdictFlag[];
analyzedAt: string;
}

type VerdictType = "clean" | "suspicious" | "flagged";

Filter Types​

interface SessionFilters {
verdict?: VerdictType;
minConfidence?: number;
maxConfidence?: number;
startDate?: string;
endDate?: string;
participantId?: string;
}

Response Types​

interface PaginatedResponse<T> {
items: T[];
total: number;
page: number;
pageSize: number;
hasMore: boolean;
}

Export Types​

interface ExportOptions {
format: "csv" | "json";
includeTrials?: boolean;
includeMetrics?: boolean;
filters?: SessionFilters;
}

WebSocket Event Types​

type WebSocketEventType = "session.new" | "verdict.computed" | "sync.progress";

interface WebSocketMessage<T = unknown> {
type: WebSocketEventType;
data: T;
}

interface SessionNewEventData {
sessionId: string;
participantId?: string;
timestamp: string;
}

interface VerdictComputedEventData {
sessionId: string;
verdict: VerdictType;
confidence: number;
flagCount: number;
}

interface SyncProgressEventData {
source: "jatos" | "prolific";
progress: number;
total: number;
status: "in_progress" | "completed" | "error";
message?: string;
}

Web Component​

The package exports a custom element <slopit-dashboard> for use without React.

Attributes​

AttributeTypeDefaultDescription
api-urlstring"http://localhost:8000/api/v1"Base URL for the API
study-idstring-Study identifier
theme"light" | "dark" | "system""system"Color theme
polling-intervalnumber-Polling interval in milliseconds
enable-websocket"true" | "false""true"Enable WebSocket updates

Usage​

<!DOCTYPE html>
<html>
<head>
<script type="module">
import "@slopit/dashboard-ui/web-component";
</script>
<link rel="stylesheet" href="@slopit/dashboard-ui/style.css">
</head>
<body>
<slopit-dashboard
api-url="http://localhost:8000/api/v1"
study-id="my-study"
theme="dark"
enable-websocket="true">
</slopit-dashboard>
</body>
</html>

Shadow DOM​

The web component uses Shadow DOM for style isolation. The component injects its own styles into the shadow root, so external stylesheets do not affect the dashboard appearance.

Integration with Python Backend​

The dashboard UI connects to the slopit Python backend API. See the Python package documentation for backend setup.

Backend Requirements​

The dashboard expects the following API endpoints:

EndpointMethodDescription
/sessionsGETList sessions with pagination
/sessions/:idGETGet session details
/sessions/:id/trialsGETGet session trials
/sessions/:id/verdictGETGet session verdict
/analysis/summaryGETGet analysis summary
/analysis/flagsGETGet flag distribution
/wsWebSocketReal-time updates

CORS Configuration​

When running the dashboard on a different origin than the API, configure CORS on the backend:

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"],
allow_methods=["*"],
allow_headers=["*"],
)

Example: Custom Dashboard Page​

import { useState, useEffect } from "react";
import {
createApiClient,
useWebSocket,
SessionList,
SessionDetail,
AnalysisSummary,
ConnectionStatus,
type AnalysisSummaryData,
} from "@slopit/dashboard-ui";
import "@slopit/dashboard-ui/style.css";

const API_URL = "http://localhost:8000/api/v1";

function CustomDashboard() {
const client = createApiClient(API_URL);
const [selectedSession, setSelectedSession] = useState<string | null>(null);
const [summary, setSummary] = useState<AnalysisSummaryData | null>(null);

const { connectionState } = useWebSocket({
url: "ws://localhost:8000/ws",
onSessionNew: () => loadSummary(),
onVerdictComputed: () => loadSummary(),
});

const loadSummary = async () => {
const data = await client.getAnalysisSummary();
setSummary(data);
};

useEffect(() => {
loadSummary();
}, []);

return (
<div>
<header>
<h1>My Study Dashboard</h1>
<ConnectionStatus state={connectionState} />
</header>

{summary && <AnalysisSummary summary={summary} />}

{selectedSession ? (
<SessionDetail
apiClient={client}
sessionId={selectedSession}
onBack={() => setSelectedSession(null)}
/>
) : (
<SessionList
apiClient={client}
onSessionSelect={setSelectedSession}
/>
)}
</div>
);
}

CSS Customization​

The dashboard uses CSS custom properties for theming. Override these in your stylesheet:

.slopit-dashboard-root {
--slopit-bg: #ffffff;
--slopit-bg-secondary: #f8f9fa;
--slopit-text: #212529;
--slopit-text-secondary: #6c757d;
--slopit-border: #dee2e6;
--slopit-primary: #0d6efd;
--slopit-clean: #28a745;
--slopit-suspicious: #ffc107;
--slopit-flagged: #dc3545;
--slopit-space-sm: 0.5rem;
--slopit-space-md: 1rem;
--slopit-radius-sm: 0.25rem;
--slopit-radius-md: 0.375rem;
}

/* Dark theme overrides */
[data-theme="dark"] .slopit-dashboard-root {
--slopit-bg: #1a1a1a;
--slopit-bg-secondary: #2d2d2d;
--slopit-text: #f8f9fa;
--slopit-border: #495057;
}

Exports Summary​

Components​

  • Dashboard: Main dashboard with all features
  • SessionList: Paginated session table
  • SessionDetail: Single session view
  • AnalysisSummary: Statistics cards
  • VerdictBadge: Verdict status badge
  • FlagList: Flag listing with severity
  • TrialTimeline: Trial visualization
  • ConfidenceHistogram: Confidence distribution chart
  • FlagDistribution: Flag type bar chart
  • ConnectionStatus: WebSocket status indicator

Hooks​

  • useWebSocket: WebSocket connection management

API Client​

  • DashboardApiClient: REST API client class
  • createApiClient(): Factory function

Types​

All component props types are exported:

  • DashboardProps, DashboardConfig
  • SessionListProps, SessionDetailProps
  • AnalysisSummaryProps, VerdictBadgeProps
  • FlagListProps, TrialTimelineProps
  • ConfidenceHistogramProps, FlagDistributionProps
  • ConnectionStatusProps
  • UseWebSocketOptions, UseWebSocketReturn, ConnectionState
  • ApiClientConfig
  • VerdictType, SessionVerdict, VerdictFlag
  • SessionSummary, SessionFilters
  • PaginatedResponse, ExportOptions
  • WebSocketEventType, WebSocketMessage
  • SessionNewEventData, VerdictComputedEventData, SyncProgressEventData
  • AnalysisSummaryData (renamed from AnalysisSummary to avoid component name conflict)