npm package: @millerbyte/react-logging
Description: React components for MillerByte Logging data visualization.
Latest version: 0.1.7

# @millerbyte/react-logging

Drop-in React components for MillerByte Logging data with TanStack Query + TanStack Table, designed for dashboards, admin panels, and observability UIs.

## Install

```bash
npm install @millerbyte/react-logging @tanstack/react-query @tanstack/react-table @tanstack/react-virtual
```

## Requirements

- React 18+
- `@tanstack/react-query` for server-mode fetching
- `@tanstack/react-table` and `@tanstack/react-virtual` for tables and virtualized rendering

## Styles

Import the package styles once in your app:

```tsx
import "@millerbyte/react-logging/styles.css";
```

## Quick start (recommended)

Use the built-in provider + client helper to reduce setup and boilerplate:

```tsx
import { LoggingProvider, SessionList, createLoggingClient } from "@millerbyte/react-logging";

const loggingClient = createLoggingClient({
	baseUrl: "/api/logging",
	headers: () => {
		if (typeof window === "undefined") {
			return {};
		}
		const token = window.localStorage.getItem("token");
		return token ? { Authorization: `Bearer ${token}` } : {};
	}
});

export const SessionsPage = () => (
	<LoggingProvider>
		<SessionList fetchSessions={loggingClient.fetchSessions} enablePolling pollingInterval={15000} />
	</LoggingProvider>
);
```

If your app already owns a `QueryClient`, pass it in:

```tsx
import { LoggingProvider, SessionList, createLoggingClient } from "@millerbyte/react-logging";
import { QueryClient } from "@tanstack/react-query";

const queryClient = new QueryClient();
const loggingClient = createLoggingClient({ baseUrl: "/api/logging" });

export const SessionsPage = () => (
	<LoggingProvider client={queryClient}>
		<SessionList fetchSessions={loggingClient.fetchSessions} />
	</LoggingProvider>
);
```

## Quick start (server mode)

Use `fetch*` props and let the components manage pagination, sorting, and polling with TanStack Query.

```tsx
import { SessionList, buildLoggingQueryParams } from "@millerbyte/react-logging";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const queryClient = new QueryClient();

const fetchSessions = async (params) => {
	const query = buildLoggingQueryParams(params);
	const response = await fetch(`/api/logging/sessions?${query}`);
	if (!response.ok) {
		throw new Error("Failed to load sessions.");
	}
	return response.json();
};

export const SessionsPage = () => (
	<QueryClientProvider client={queryClient}>
		<SessionList fetchSessions={fetchSessions} enablePolling pollingInterval={15000} />
	</QueryClientProvider>
);
```

## Quick start (client mode)

Pass arrays directly when you already have the data in memory.

```tsx
import { ActionList } from "@millerbyte/react-logging";

export const ActionsPage = ({ actions }) => <ActionList actions={actions} />;
```

## Data shape

Server-mode fetchers must return:

```ts
type PaginatedResponse<T> = {
	data: T[];
	totalCount: number;
	pageIndex: number;
	pageSize: number;
	totalPages: number;
};
```

DTOs are exported from the package and should be used when possible:

```ts
import type { ApiActionDto, LoginSessionDto } from "@millerbyte/react-logging";
```

Conventions:
- Dates are ISO strings (UTC)
- Durations are milliseconds
- Dictionaries are `Record<string, unknown>`

## Client helper options

`createLoggingClient` accepts:

- `baseUrl` (default: `/api/logging`)
- `headers` (static headers or async function returning headers)
- `requestInit` (static `RequestInit` or function that receives `{ path, method }` for per-request overrides like `signal`)
- `responseCase` (`"camel"` default for ASP.NET JSON, `"pascal"` if your API already matches DTO casing and uses a Pascal-cased pagination wrapper)

When using `responseCase: "pascal"`, paginated responses are expected to look like:

```ts
type PascalPaginatedResponse<T> = {
	Data: T[];
	TotalCount: number;
	PageIndex: number;
	PageSize: number;
	TotalPages: number;
};
```

## Pagination, sorting, and filters

Server-mode `fetch*` functions receive:

```ts
type QueryParams = {
	pageIndex: number;
	pageSize: number;
	sorting: SortingState;
	filters: FilterState;
};
```

Filters include:
- `searchText`, `userId`, `tenantId`, `sessionId`
- `statusCodes`, `methods`, `isActive`
- `dateRange` (`from`, `to`)

## Polling

Polling is optional and controlled via props:

```tsx
<SessionList fetchSessions={fetchSessions} enablePolling pollingInterval={15000} />
```

## Custom labels and state renderers

Override labels and UI states to match your app:

```tsx
<ActionList
	fetchActions={fetchActions}
	labels={{ loading: "Loading actions...", empty: "No actions yet.", error: "Something broke." }}
	renderStates={{
		loading: () => <div>Loading…</div>,
		empty: () => <div>Nothing to show.</div>,
		error: (error) => <div>{String(error)}</div>
	}}
/>;
```

## Data adapters

If your API shape differs from the DTOs, map to DTOs with a `dataAdapter`:

```tsx
import { SessionList } from "@millerbyte/react-logging";

const adapter = {
	toDto: (raw) => ({
		Id: raw.id,
		UserId: raw.user_id,
		TenantId: raw.tenant_id,
		LoginTime: raw.login_time,
		LastActivityTime: raw.last_activity_time,
		IsActive: raw.active,
		ActionCount: raw.action_count
	})
};

export const SessionsPage = ({ sessions }) => <SessionList sessions={sessions} dataAdapter={adapter} />;
```

## Formatters

Provide custom formatting for dates, durations, and numbers:

```tsx
<ActionList
	fetchActions={fetchActions}
	formatters={{
		date: (value) => new Date(value).toLocaleString(),
		durationMs: (value) => `${value ?? 0} ms`,
		number: (value) => value.toLocaleString()
	}}
/>
```

## Components

- `SessionList`
- `ActionList`
- `SessionDetail`
- `ActionDetail`
- `Timeline`
- `Analytics`
- `Filters`

## Theming

Override CSS variables from `src/styles/theme.css` in your app:

```css
:root {
	--mb-color-primary: hsl(217 91% 60%);
	--mb-color-surface: hsl(210 40% 98%);
}
```

## Publishing

From `Web/logging/millerbyte-logging-react/`:

```bash
npm login
npm version patch
npm publish --access public
```

Notes:
- Scoped packages are private by default unless `--access public` is used.
- Use semver (patch/minor/major) and keep release notes for consumers.