WorkQueue

Työjono

Asiat ryhmiteltyinä vasemmalla, valitun sisältö oikealla; poikkeuskortti kertoo mikä on vialla ja kuka korjaa.

Käyttöpaikat

Suoraan: palvelu tuo osan itse. Välillisesti: osa tulee mukaan toisen paketin osan kautta, ja sen muutos näkyy palvelussa silti. Laskettu julkaisun yhteydessä 23.10.2026.

Ei käytössä palveluissa
Ostohinta ei vastaa sovittua
Ostaja
Odottaa

Syötteen hinta 26,13 € poikkeaa sovitusta 25,80 €:sta. Ostaja päättää, kumpi on voimassa.

Esimerkki valitulla teemalla ja sävyllä; vaihda ne yläpalkista.

Käyttö

Paketin README:n luku sellaisenaan.

IssueCard — poikkeuskortti

Mikä on vialla, kenen korjattava ja mikä korjaa sen. Tuotannon hälytyslista näytti Pythonin poikkeuksen ja kaksi nappia, *Kuittaa* ja *Hiljennä* — kumpikaan ei korjaa mitään.

<IssueCard
  avatar={<Avatar name="Akkuvoima Oy" tone="identity" />}
  title="Akkuvoima Oy"
  meta="Ehdokkaat · Manuaalinen"
  owner="Meidän korjattava"
  ownerTone="self"
  status="Jäsennysasetus puuttuu"
  statusTone="error"
  description="Tiedostolle «uutuudet syksy.xlsx» ei ole jäsennysasetusta. Toimittaja on todennäköisesti vaihtanut nimen."
  footnote="Havaittu 7 pv sitten · viimeisin onnistunut 9 pv sitten"
  technical="ValueError: XLSX ingest config not found for input '…'"
  actions={<Button size="sm">Käytä aiempaa asetusta</Button>}
/>
  • owner + ownerTone: self piirtää warning-merkin ja vaatii toimenpiteen tältä käyttäjältä; other on ääriviiva ja tiedoksi. Painike seuraa samaa sääntöä: self-kortille täytetty, other-kortille variant="outline".
  • technical on <details>-elementissä: taitettuna, ilman tilaa, saatavilla ilman JavaScriptiä. Pitkä polku ei levitä korttia (break-all).
  • children on paikka riville avautuvalle lomakkeelle, esimerkiksi puuttuvan osoitteen syötölle.

Palvelinrenderöitävä. Listaa varten kääri <ul className="divide-y">:n sisään <li>:nä, kortti ei piirrä omaa reunaansa.

WorkQueue — työjono

Asiat ryhmiteltyinä vasemmalla, valitun sisältö oikealla. Lista kertoo mitä on jäljellä, valittu kohta yhden asian kerrallaan, ja päätös siirtää seuraavaan. Ryhmän nimi on kuka päättää (Ostaja, Hinnoittelu, Katselmoija), ja hint kertoo miksi ryhmä ei koske tätä käyttäjää.

"use client";
const [valittu, setValittu] = useState("hinta");

<div className="grid items-start gap-6 lg:grid-cols-[19rem_minmax(0,1fr)]">
  <WorkQueue
    label="Muutosjono"
    className="lg:sticky lg:top-20"
    value={valittu}
    onValueChange={setValittu}
    groups={[
      { label: "Ostaja", items: [{ id: "hinta", title: "Ostohinta ei vastaa sovittua", description: "syöte 26,13 € · sovittu 25,80 €", tone: "pending" }] },
      { label: "Toimittaja", hint: "ei toimenpiteitä meiltä", items: [{ id: "yksikko", title: "Koko ilman yksikköä", tone: "info" }] },
      { label: "Säännöt ratkaisivat", items: [{ id: "sovittu", title: "Ostohinta vastaa sovittua", tone: "none" }] },
    ]}
  />
  <Card>{/* valitun sisältö */}</Card>
</div>
  • Sävy on tila, ei prioriteetti: pending odottaa tämän käyttäjän päätöstä, done on päätetty, info odottaa jotakuta muuta, none ei vaadi mitään. Kiireellisyys näytetään järjestyksellä.
  • ↑/↓ ja j/k siirtävät valintaa ja kohdistuksen; aria-current="true" kertoo valitun ruudunlukijalle.
  • Vain lista. Sisältöalue, asettelu ja «seuraavaan avoimeen» -siirto kuuluvat kuluttajalle, koska ne riippuvat siitä mitä asiat ovat. Demo /v3/muutokset näyttää koko reseptin.

Tuonti ja lähde

import { WorkQueue, IssueCard } from "@karkkainen/ui";
  • src/components/work-queue.tsx
  • src/components/issue-card.tsx