# Shift planner

A React block (a whole screen) in [Beamline](https://beamline.io/)'s [HR & payroll](https://beamline.io/components/apps/hr-payroll) app. Live demo: https://beamline.io/components/shift-planner

A workplace's week of staff shifts against what it needs: a row per person with their week's hours against their contract, each day a 24-hour track with its shifts drawn from start to end (an overnight shift running into the next morning, its hours counted on the start day or split at midnight, said); above, each need's people against its count, short ones marked; a shift made or changed in place with its break as any time, in a window or at a set time, checked against the shift; conflicts that block publishing (overlaps, impossible breaks) apart from warnings (short rest, weekly hours over contract), filterable; automatic filling that proposes shifts drawn as proposals and says the needs no one could fill, waiting for accept or discard; and Publish, asked in place, with who was told.

## Use it for

- Scheduling hourly staff for a shop, café, clinic or warehouse week by week against staffing needs.
- Publishing a schedule only when nothing blocks it, with proposals reviewed before they count.

## Not for

- A repeating pattern and who it applies to ([shift-rotations](https://beamline.io/components/shift-rotations)).
- Meetings and events on a calendar ([team-calendar](https://beamline.io/components/team-calendar)).

## Anatomy

- header: the mark, <workplace> shifts, the week
- needs: per day each need (role, window) with assigned of required and proposed, short ones marked
- week: a row per person (name, role, hours this week against the contract), a cell per day with a 24-hour track (shifts as bars, yesterday's overnight spill lighter, proposals outlined, conflicts red), each shift as a button (start–end, next day), add a shift on an empty day
- editor: who and which day, From and To, the hours worked or the overnight attribution in words, A break (any time, in a window, at a set time; minutes, at, between and; paid), the break in words or what's wrong, Remove, Cancel, Save shift
- conflicts: the count and how many block, All/Blocking, each with Blocks or Warning; Overnight hours count (start day · split at midnight)
- publishing: open needs, Fill open needs, Accept n proposals / Discard, the needs no one could fill with why; the publish state and Publish the week (asked in place), who was told

## States

- loading
- an overnight shift (start day or split)
- a break outside the shift, too long, window too short
- overlap (blocks), short rest (warning), over weekly hours (warning)
- needs short; proposals pending (publish waits); unfilled with reasons
- published (who was told, who couldn't be reached)

## Keyboard

- Tab through the shifts and the add buttons; Enter opens the editor
- Publish asks in place: Enter confirms, Escape backs out

## Motion

Words and the editor fade in; nothing moves at rest.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `people / weekStart / needs / workplace` | `Person[] / string / Need[] / string` | — | People with role, weekly limit and least rest; the week's first day; the needs (role, window, how many). |
| `shifts / defaultShifts / onShiftsChange` | `Shift[]` | — | Each shift's person, start day, start and end (an end at or before the start is the next day), role, break, and whether it is only proposed. |
| `attribution / defaultAttribution / onAttributionChange` | `"startDay" \| "calendarDay"` | — | Which day owns overnight hours. |
| `onAutoFill / onPublish` | `(open) => Promise<{ proposals, unfilled }> / (shifts) => Promise<{ notified, failed }>` | — | onAutoFill: Propose shifts for the needs not covered; resolve with the proposals and the needs no one could fill. onPublish: Publish the week; resolve with who was told. Reject: still a draft, said. |
| `people` (required) | `Person[]` | — |  |
| `weekStart` (required) | `string` | — | The week's first day (ISO date). |
| `shifts` | `Shift[]` | — |  |
| `defaultShifts` | `Shift[]` | — |  |
| `onShiftsChange` | `((s: Shift[]) => void)` | — |  |
| `needs` (required) | `Need[]` | — | What the workplace needs this week. |
| `attribution` | `Attribution` | — |  |
| `defaultAttribution` | `Attribution` | — |  |
| `onAttributionChange` | `((a: Attribution) => void)` | — |  |
| `onAutoFill` | `((openNeeds: Need[]) => Promise<{ proposals: Shift[]; unfilled: { needId: string; reaso…` | — | Propose shifts for the needs not covered; resolve with the proposals and the needs no one could fill. |
| `onPublish` | `((shifts: Shift[]) => Promise<{ notified: number; failed: number; }>)` | — | Publish the week; resolve with who was told. Reject: still a draft, said. |
| `workplace` (required) | `string` | — | The workplace's name. |
| `canManage` | `boolean` | — |  |
| `loading` | `boolean` | — |  |
| `product` | `string` | — |  |
| `className` | `string` | — | Classes on the documented visual root; no override by default. |
| `ref` | `Ref<HTMLDivElement>` | — | Receives the documented native target or imperative handle; omitted by default. |

## Built from

- [Avatar](https://beamline.io/components/avatar.md)
- [Button](https://beamline.io/components/button.md)
- [Checkbox](https://beamline.io/components/checkbox.md)
- [Confirm morph](https://beamline.io/components/confirm-morph.md)
- [Label](https://beamline.io/components/label.md)
- [Number field](https://beamline.io/components/number-field.md)
- [Segmented control](https://beamline.io/components/segmented-control.md)
- [Select](https://beamline.io/components/select.md)
- [Skeleton](https://beamline.io/components/skeleton.md)
- [Spinner](https://beamline.io/components/spinner.md)
- [Switch](https://beamline.io/components/switch.md)
- [Text morph](https://beamline.io/components/text-morph.md)
- [Tooltip](https://beamline.io/components/tooltip.md)

## Dependencies

`lucide-react`

## Import

```tsx
import { ShiftPlanner } from "@beamline/shift-planner";
```

## Get it

Shift planner is part of Beamline UI. Install the parts you need in your React app as compiled packages or editable source. [Get Beamline](https://beamline.io/checkout?pack=complete) · [Connect your agent](https://beamline.io/connect)

## More in HR & payroll

- [Shift rotations](https://beamline.io/components/shift-rotations): Rotating shift patterns from pattern to people: a pattern of one to four weeks painted day by day with a shift template, a rest day or nothing (pick the…
- [Roster publishing](https://beamline.io/components/roster-publish): The week's roster before it goes out: areas down the side and days across, each shift a chip with who works it and its hours, its state its tone (not…
- [My shift](https://beamline.io/components/my-shift): The worker's own shift on their phone: today drawn to scale (the plan's areas as blocks on an hour axis, under them what was worked: on-plan time in…
- [Timesheet review](https://beamline.io/components/timesheet-review): Timesheets for a manager: the day's counts at the head (scheduled, on shift, late, on break, to approve), the ones that filter being buttons, an…
- [Hours ledger](https://beamline.io/components/hours-ledger): One worker's pay cycle of shifts, for the worker or their payroll admin: the wall-clock span and the paid hours as separate columns, a night shift…
- [Payroll run](https://beamline.io/components/payroll-run): A pay period from report to submission, one payroll account at a time (a worker on two accounts is on both runs, never combined): the workers on one…
- [Payroll cycles](https://beamline.io/components/payroll-cycles): The client's side of an outsourced payroll across countries: every entity's current cycle on one board, its stage drawn as steps, the one thing that…
- [Payroll approvals](https://beamline.io/components/payroll-approvals): Payroll approvals for a company paying through a payroll provider. Expenses are split by whose move it is (you, another approver, the employee answering…
- [Benefit changes](https://beamline.io/components/benefit-changes): An employee's benefit changes after a life event. The centre puts their turn first: each task (a proof to upload, with its deadline in the warning tint…
- [Hiring pipeline](https://beamline.io/components/hiring-pipeline): A recruiting team's pipeline across every open job. Stage groups (reviewing, interviewing, closing) are counted across jobs above job sections that fold…
- [Recruiter home](https://beamline.io/components/recruiter-home): A recruiter's own work, in one place. Upcoming interviews with who sits in, today's first; feedback owed, overdue first and on the danger tone, written…
- [Stage reports](https://beamline.io/components/stage-reports): Reports on how applications move through stages, built from each application's stage intervals. By week: applications present in each stage (or from each…
- [Link booking](https://beamline.io/components/link-booking): A candidate's booking link for one stage of one job: a column with the job, the stage's length, who they meet (each with their role), where and what the…
- [Team & members](https://beamline.io/components/team-members): The people page of a workspace: seats used against the plan, members and pending invitations in one list with search and a role filter, role changes that…

Part of [Beamline UI: polished, editable React components and complete screens for custom applications](https://beamline.io/). [Build your own style from any look; tokens, fonts, radii, motion; not only colours](https://beamline.io/docs/customize).
