npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@downpat/admin-ui

v0.0.2

Published

React admin UI components for managing DownPat exercises

Readme

@downpat/admin-ui

React admin UI components for managing DownPat exercises. This package provides a self-contained admin interface that can be mounted into any React application.

Installation

npm install @downpat/admin-ui

Peer Dependencies

  • react >= 18.0.0
  • react-dom >= 18.0.0 (optional, required for mountAdminUI)

Quick Start

Option 1: Mount Function (Recommended)

The simplest way to add the admin UI to your application:

import { mountAdminUI } from '@downpat/admin-ui';
import '@downpat/admin-ui/styles';

const admin = mountAdminUI({
  target: '#admin-root',
  apiBaseUrl: '/api/downpat',
  getAuthToken: async () => localStorage.getItem('token'),
  availableModels: ['gpt-4', 'gpt-3.5-turbo'],
  basePath: '/admin',
  onNavigate: (path) => {
    // Sync with your router (optional)
    window.history.pushState(null, '', path);
  },
});

// Navigate programmatically
admin.navigate('/exercises/new');

// Clean up when done
admin.unmount();

Option 2: React Component

For more control, use the React components directly:

import { AdminApp } from '@downpat/admin-ui';
import '@downpat/admin-ui/styles';

function AdminPage() {
  return (
    <AdminApp
      config={{
        apiBaseUrl: '/api/downpat',
        getAuthToken: async () => localStorage.getItem('token'),
        availableModels: ['gpt-4', 'gpt-3.5-turbo'],
      }}
    />
  );
}

Option 3: Controlled Routing

If you want to control routing externally (e.g., with react-router):

import { ControlledAdminApp } from '@downpat/admin-ui';
import '@downpat/admin-ui/styles';
import { useLocation } from 'react-router-dom';

function AdminPage() {
  const location = useLocation();
  const adminPath = location.pathname.replace('/admin', '') || '/';

  return (
    <ControlledAdminApp
      config={{
        apiBaseUrl: '/api/downpat',
        getAuthToken: async () => localStorage.getItem('token'),
        availableModels: ['gpt-4', 'gpt-3.5-turbo'],
        onNavigate: (path) => navigate(path),
      }}
      path={adminPath}
    />
  );
}

Configuration

AdminUIConfig

| Property | Type | Required | Description | |----------|------|----------|-------------| | apiBaseUrl | string | Yes | Base URL for API requests (e.g., /api/downpat) | | getAuthToken | () => Promise<string \| null> | Yes | Function to get the current auth token | | availableModels | string[] | No | AI models available for exercise configuration (default: DEFAULT_AVAILABLE_MODELS from @downpat/core) | | basePath | string | No | Base path for admin routes (default: /admin) | | onNavigate | (path: string) => void | No | Callback when navigation occurs | | onTestExercise | (exerciseId: string, slug: string) => void | No | Callback when user clicks "Test" on an exercise |

Styling

Import the CSS file to apply default styles:

import '@downpat/admin-ui/styles';

CSS Variables

Customize the appearance by overriding CSS variables:

:root {
  --downpat-admin-input-bg: #ffffff;
  --downpat-admin-input-border: #d1d5db;
  --downpat-admin-input-focus: #3b82f6;
  --downpat-admin-btn-primary-bg: #3b82f6;
  --downpat-admin-btn-primary-hover: #2563eb;
}

Dark Mode

The admin UI supports dark mode via [data-theme="dark"] or .dark class:

<html data-theme="dark">

CSS Namespace

All CSS classes use the downpat- prefix to avoid conflicts with your application styles:

  • .downpat-admin-app - Main container
  • .downpat-exercise-form - Form wrapper
  • .downpat-btn, .downpat-input, etc. - UI elements

This BEM-like naming convention minimizes collision risk without requiring CSS Modules. If you need complete style isolation, wrap the admin UI in a Shadow DOM container.

API Requirements

The admin UI expects these API endpoints:

| Method | Endpoint | Description | |--------|----------|-------------| | GET | /exercises/with-metadata | List all exercises with metadata | | GET | /exercises/:slug | Get single exercise | | POST | /exercises | Create exercise | | PUT | /exercises/:exerciseId | Update exercise | | DELETE | /exercises/:slug | Delete exercise | | POST | /exercises/:slug/publish | Publish exercise | | POST | /exercises/:slug/unpublish | Unpublish exercise | | POST | /exercises/:slug/restore | Restore draft from published |

All endpoints require Authorization: Bearer <token> header.

Available Exports

Main Exports

  • mountAdminUI(options) - Mount admin UI into a DOM element
  • AdminApp - Main React component with internal routing
  • ControlledAdminApp - React component with external routing control

Hooks

  • useAdminContext() - Access admin config and navigation
  • useAdminAPI() - Access the API client

Low-level Components

For advanced customization:

  • ExerciseForm - Exercise create/edit form
  • ExerciseList - Exercise listing table
  • ExerciseListPage - Full list page
  • ExerciseEditorPage - Full editor page

Utilities

  • createAdminAPIClient(apiBaseUrl, getAuthToken) - Create API client instance

Navigation Architecture

The admin UI manages its own routing state internally:

  • Internal navigation: Users navigate via links and buttons within the UI
  • onNavigate callback: Notification hook for syncing with your router (one-way)
  • navigate() method: For programmatic navigation from outside the admin UI
const admin = mountAdminUI({ ... });

// Listen for URL changes and sync
window.addEventListener('popstate', () => {
  const path = window.location.pathname.replace('/admin', '');
  admin.navigate(path);
});

Routes

| Path | Description | |------|-------------| | / or /exercises | Exercise list | | /exercises/new | Create new exercise | | /exercises/:slug/edit | Edit existing exercise |

Client-Side Slicing

The API client supports slicing cached data for UI pagination via getExercisesSliced():

import { createAdminAPIClient } from '@downpat/admin-ui';

const api = createAdminAPIClient('/api/downpat', getToken);

// Get first slice (50 items by default)
const slice1 = await api.getExercisesSliced();
console.log(slice1.items, slice1.total, slice1.hasMore);

// Get next slice
const slice2 = await api.getExercisesSliced({ limit: 50, offset: 50 });

Important: This is CLIENT-SIDE slicing with a 30-second cache. All exercises are fetched from the server, then sliced locally. This is useful for UI pagination but does NOT reduce server load. True server-side pagination would require backend support.

Known Limitations

  • Query parameters: The internal router does not parse query parameters
  • Trailing slashes: /exercises/ and /exercises are treated differently
  • No server-side pagination: getExercisesSliced() fetches all data then slices client-side; true server-side pagination requires backend support

For applications with complex routing needs, use ControlledAdminApp with your preferred router.

License

MIT