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

fieldboard

v0.1.0

Published

A dispatch board for field service work — resource timeline, unassigned backlog, travel-aware scheduling, and risk detection for React.

Downloads

28

Readme

Fieldboard

CI

A dispatch board for field service work — the kind where a crew has to physically drive to the job.

Generic resource schedulers can draw a timeline. None of them know that your plumber can't be in two suburbs at once, or that the person you just assigned isn't ticketed for the work. Fieldboard does.

import { DispatchBoard } from "fieldboard";
import "fieldboard/styles.css";

<DispatchBoard technicians={crew} jobs={jobs} onJobsChange={setJobs} />;

The stylesheet is prebuilt — you do not need Tailwind in your project to use the board. To avoid a flash of the wrong theme, run the theme script before first paint:

import { THEME_SCRIPT } from "fieldboard";

<script dangerouslySetInnerHTML={{ __html: THEME_SCRIPT }} />;

MIT licensed. React 19, Tailwind v4, no backend required.


What makes it a field service board

| | | |---|---| | Travel-aware scheduling | Jobs know where they are. The board won't offer a slot the crew can't drive to in time, and flags one that's already impossible. | | Skill matching | A technician holds trades. Assigning work they aren't ticketed for is flagged — never blocked, because dispatchers override for good reasons. | | An unassigned backlog | The tray is a first-class surface, not an afterthought. Virtualised, so 500 waiting jobs stay fast. | | Risk detection | Double-booked, late, unreachable, unticketed, outside shift — ranked, so a block shows the worst problem rather than all of them. | | Shift boundaries | Working hours are a real constraint, not decoration. Per-technician capacity bars show who has room. | | Bulk assignment | "Fill this technician's day" places as many selected jobs as actually fit and tells you which didn't. | | Knows what you may not do | readOnly and canEdit remove the affordances rather than greying them, so nothing is offered that would only fail later. | | Speaks your words | Every string is replaceable through messages, as whole sentences with named holes — so a translation can reorder them, not just swap them. Counts use Intl.PluralRules. | | Reads in your locale | Times, hours and durations follow the locale you pass — 16:30 in Berlin, 4:30p in Denver — with the full form reserved for screen readers. | | Built for two dispatchers | Every change is emitted as an intent your server can reconcile, external edits are announced, and a drag whose job moved underneath is refused. | | Undo that names itself | Every change is reversible and labelled — "Scheduled 4 jobs with Cole Vargas · Undo". ⌘Z, multi-step, with redo. |

Installing

npm install fieldboard

The data you provide

type Tech = {
  id: string;
  name: string;
  initials: string;
  skills: Trade[];        // every trade they're ticketed for
  shiftStartMin: number;  // minutes from midnight — 480 is 08:00
  shiftEndMin: number;
};

type Job = {
  id: string;
  ref: string;
  customer: string;
  address: string;
  lat: number;            // used for travel time and the map
  lng: number;
  durationMin: number;
  trade: Trade;
  urgent: boolean;
  techId: string | null;  // null means it's in the tray
  startMin: number | null;
  dayOffset: number | null; // days from today; null while unassigned
  state: "planned" | "active" | "done";
};

Props

| Prop | Type | Notes | |---|---|---| | technicians | Tech[] | required | | jobs | Job[] | controlled mode; pair with onJobsChange | | onJobsChange | (jobs: Job[]) => void | | | defaultJobs | Job[] | uncontrolled mode instead of jobs | | travel | TravelEstimator | see below | | trends | { unassigned, atRisk, late, unscheduledHours } | seven days of history for the KPI deltas |

Travel time

By default the board estimates travel from straight-line distance, inflated for real roads:

createTravelEstimator({
  kmPerHour: 32,    // average urban speed
  detourFactor: 1.3, // nobody drives in a straight line
  minimumMin: 5,     // parking, finding the door, signing in
});

That works with no API key. For real road distances, pass your own — the board never computes travel itself:

<DispatchBoard travel={(from, to) => myRoutingService(from, to)} … />

Risk ranking

A job shows one problem: the worst one. In order —

  1. unassigned — sitting in the tray
  2. late — should have started, hasn't (today only)
  3. overlap — double-booked, costs you two jobs
  4. unreachable — can't drive there in time; slips everything after
  5. skill-mismatch — not ticketed for the trade
  6. outside-shift — overtime, expensive but the work still happens

Theming

Three themes ship — Ivory, Cabin, Emerald — set with data-theme on <html>. Everything is CSS custom properties, so a fourth is one block:

[data-theme="yours"] {
  --void: …;      /* canvas */
  --slate: …;     /* chrome */
  --drawer: …;    /* the tray, a plane above the board */
  --brass: …;     /* selection and focus */
  --hivis: …;     /* at risk */
  --late: …;      /* late */
}

Colour means trouble and nothing else. A healthy board is grey.

Using the scheduling logic on its own

Every rule is a pure function you can call server-side:

import { riskOf, earliestFreeSlot, canDo } from "fieldboard";

const slot = earliestFreeSlot(tech, job, jobs, { travel, notBefore: now });
const problem = riskOf(job, { jobs, techs, nowMin: now });

Development

This is an npm workspace:

packages/fieldboard   the library — what npm install gives you
apps/demo             the example app in the screenshots
npm install
npm run build:lib   # build the package (tsc + Tailwind CSS)
npm run dev         # demo at http://localhost:3111
npm test            # 48 tests over the scheduling logic
npm run typecheck

How this project is built

Fieldboard follows the Human-Centric Product Harness — an executable process kept in .ai/. It defines the product context, the design system, the review roles, and the gates a change must pass.

Its central rule:

A change that compiles, type-checks and passes tests is not done until the intended human can perceive the situation, understand it, act confidently, see what happened, and remain in control.

Contributors and coding agents should start at .ai/README.md. The harness/ and policies/ directories are project-agnostic and can be lifted into another codebase.

Not built yet

Honest list, so nobody adopts this expecting more than it does:

  • Address geocoding. A job created through the form has no coordinates, so travel to it can't be estimated until you set lat/lng.
  • Calendar view. The tab exists and says so.
  • Recurring jobs, multi-day jobs, SLA windows, overtime rules.
  • Persistence. The board is a controlled component; storage is yours.
  • Translations. Fieldboard ships English and the means to replace every word of it. It bundles no other language, because it could not verify or maintain one.
  • Authorisation. readOnly and canEdit shape what the board offers. They are not a security boundary and cannot be — a removed button is not a permission. Your server must enforce the same rules against the same user.
  • Conflict resolution. The board reports conflicts and gives you the intent to reconcile them. Merging is your server's job — it has one, the board does not.

Licence

MIT — see LICENSE.