Slotting 1.0.0

Embedding the components

Four custom elements bring slotting into your own pages:

Element What it is
<slotting-station> The operator's scan screen: scan a unit, see the bin to put it in, very large, on a drawing of the wall
<slotting-wall> A drawing of one wall with its bins (free, held, disabled, highlighted), and optionally its put-to-light lights
<slotting-setup> The bin setup editor: import (DXF, CSV, grid, photo), preview, edit the bins in a table, validate, save
<slotting-wave-monitor> The progress of a wave: stats, the walls with the held bins, the orders, updated every few seconds

They are standard custom elements in one ES module, with no framework runtime, so they work in plain HTML and in Angular, React, Vue, Svelte and every other framework. The station, the setup editor and the monitor call the install's API themselves (api-base); the wall draws what you give it, and calls the API only to show the lights.

Loading the script#

One ES module defines all four components. Your install serves it at two URLs, both with Access-Control-Allow-Origin: *:

URL Caching Use
/widget/1.0.0/slotting-components.js public, max-age=31536000, immutable Production: pin the version and add SRI
/widget/slotting-components.js no-cache Always the installed version; fine for development

For this install (version 1.0.0), with its Subresource Integrity hash:

html
<script type="module" src="https://slotting.internal.example/widget/1.0.0/slotting-components.js"
        integrity="sha384-1hTDsE/7ThzvFzX8J+xwUx0AjF16gdOWnKgPbt25wtRtY34ViTnLgJWgo3mfCZpU" crossorigin="anonymous"></script>

Replace the host with your install's. The hash belongs to this version: after an upgrade, take the new one from these pages on the upgraded install, or compute it from the versioned URL:

bash
curl -s https://slotting.internal.example/widget/1.0.0/slotting-components.js | openssl dgst -sha384 -binary | openssl base64 -A

The station#

html
<slotting-station wave-id="WAVE-0815" operator="station-1"></slotting-station>
Attribute / property
wave-id / waveId The wave to scan into. Required
layout-id / layoutId Only when two layouts have a wave with the same id
operator / operator Sent with every scan (the station or the operator's name)
no-keyboard On handhelds: don't open the on-screen keyboard when the scan field takes the focus
api-base, theme See Loading the script and Styling
js
const station = document.querySelector('slotting-station');
station.addEventListener('put', e => console.log(`put ${e.detail.sku} into ${e.detail.bin.id}`));
station.addEventListener('order-complete', e => console.log(`order ${e.detail.orderId} complete:`, e.detail.bins.map(b => b.id)));
station.addEventListener('scan-error', e => {
    if (e.detail.kind === 'aside') {
        console.warn(`put ${e.detail.sku} aside: ${e.detail.message}`);
    }
});

Confirmed puts at the station#

In a wave with confirmPuts (Confirmed puts) the station asks for the bin scan by itself; nothing changes in your page:

  1. The operator scans an item. The target bin is shown with Scan bin A-01-01 to confirm (parts target-pending and confirm-prompt, colour --sl-target-pending).
  2. The operator puts the unit in and scans the bin's label. The station sends it to POST /api/waves/{waveId}/confirm, shows the put as confirmed (part target-confirmed) and fires confirm. A put split over several bins asks for each bin in turn.
  3. A wrong bin shows "Wrong bin" with the bin to use, and the station keeps waiting (scan-error with code wrong_bin).
  4. An item scanned meanwhile is not sent: the station asks to confirm the put first, or to press Undo (scan-error with code confirm_first).
  5. The confirmation of the order's last put fires order-complete and lists the order's bins.

After a reload of the page, the station picks up where it was: it reads the wave's pending puts (GET /api/waves/{waveId}/pending) and asks again for the oldest one of its operator (of anyone, without operator). It reads them again each time a put is fully confirmed, so a put whose scan answer never reached the station (a timeout, then the operator scanned the next unit) is asked for next instead of staying pending. If the operator presses the bin's light button instead of scanning the label, the station notices it (it polls the light state every 1.5 seconds while a put is pending) and goes on. pendingConfirmation is the put waiting for its bin scan: { scanId, waveId, orderId, sku, quantity, bins, result }, with bins the bins still to confirm; null otherwise.

js
station.addEventListener('confirm', e => console.log(`confirmed ${e.detail.bin.id} for ${e.detail.orderId}`));

The wall#

html
<slotting-wall wall-id="A" order-id="ORD-1001"></slotting-wall>

Set layout (the layout JSON, from GET /api/layouts/{layoutId}) and bins (the occupancy, from GET /api/layouts/{layoutId}/bins) as properties, not attributes. The wall is drawn to the element's width, its height capped by --sl-wall-height.

Attribute / property
layout The layout (property only)
wall-id / wallId The wall to draw; default the layout's first wall
bins The occupancy (property only); bins without an entry are free
highlight Bin ids to highlight, pulsing (steady with reduced motion), as an array (property only)
order-id / orderId The order whose bins are emphasised
selected The selected (outlined) bin
lights / lights Poll and show the layout's lights
lights-interval / lightsInterval How often to poll the lights, in milliseconds (default 1000, at least 500)
simulate-buttons / simulateButtons A click on a lit bin presses its light button
layout-id / layoutId The layout whose lights are polled; default the id of layout
api-base, theme See Loading the script and Styling

Clicking a bin fires bin-select with { binId, wallId, bin, state }.

Lights on the wall#

html
<slotting-wall lights simulate-buttons wall-id="A"></slotting-wall>

With lights, the wall polls GET {api-base}/api/layouts/{layout-id}/lights (the light state) and shows the lit bins: put pulsing in --sl-light-put with the quantity, complete steady in --sl-light-complete (steady, with a thicker outline, for users who prefer reduced motion). It pauses while the page is hidden, stops when the element is removed, and backs off when the install can't be reached (the message is in part lights-status). Without lights the wall never calls the API.

With simulate-buttons, a click on a lit bin (or Enter or Space on it) presses its button, as a light module would: POST /api/lights/press, which confirms the bin's pending put or releases its complete order (Button presses). Then it fires light-press with { layoutId, binId, wallId, address, state, result, error }: result is the press route's answer, or error ({ status, code, message }) when it failed, for example not_pending. A press the server ignored (result.action ignored: it repeated a press of a moment ago) is not an error. While a bin's press is in flight, and for one second after its answer, further clicks and keys on that bin are ignored, so a double click doesn't confirm the next put or release the order. Use it as a simulator, or as a touch-screen alternative to the buttons. lightStates is the light state of the last poll.

The setup editor#

html
<slotting-setup layout-id="PUTWALL-A"></slotting-setup>

With layout-id, the editor loads that layout and Save replaces it (PUT); without it, Save creates a new layout (POST). Users import walls (a DXF or CSV file, a grid form, or a photo with their measurements), see them on the wall drawing, edit the bins in a table, validate and save. Files are read in the browser and sent to the install as JSON; imports return drafts and never save. The photo import is disabled, with an explanation, when the install has no AI provider configured (Bin setups).

The wave monitor#

html
<slotting-wave-monitor wave-id="WAVE-0815" interval="5000"></slotting-wave-monitor>

Shows the wave's stats, its walls with the held bins and the list of orders with a status filter. It polls GET /api/waves/{waveId} every interval milliseconds (default 3000, at least 1000) while the page is visible, until the wave is closed. refresh() polls at once; every poll fires wave-update with the wave. layout-id is needed only when two layouts have a wave with the same id.

Events#

All events are CustomEvents that bubble and are composed, so a listener on document sees them too.

Event From detail
scan station { sku, quantity, scanId }: a code was scanned, the request is about to be sent
put station The scan's answer (ScanResult, as in the API reference)
confirm station The confirmation's answer (ConfirmResult), for each confirmed bin (Confirmed puts)
order-complete station { orderId, bins, result, confirm? }: the order is complete; bins nearest first. result is the scan that completed it (with confirmed puts: the scan whose confirmation did), confirm that confirmation (absent when the light button confirmed it)
scan-error station { sku, quantity, scanId, status, code, message, kind }; kind is aside, retry, blocked or error, code the API's code (also wrong_bin, not_pending) or offline, timeout, client_error, confirm_first (an item scanned while a put waits for its bin scan; not sent)
bin-select wall { binId, wallId, bin, state }
light-press wall { layoutId, binId, wallId, address, state, result, error } (with simulate-buttons)
layout-imported setup { source, layout, warnings }, source = dxf, csv, grid or photo
layout-saved setup { layout, created }
wave-update monitor The wave (WaveState)

License#

The components that call the API read GET {api-base}/v1/info (cached for 10 minutes) and:

After installing a license key, clearInstallInfo() (exported by the module) makes the next component ask again.

Frameworks#

The pattern is the same everywhere: load the script once, let the framework pass the custom elements through, set object values (layout, bins, highlight) as DOM properties, set the rest as attributes, and listen to the events.