kit

Reordering

Built revision v17.10.0

React
Topics

Human support

Bring a missing role, layout, or state, or a rendering against the standard, to the session working on kit. Name the package and the entry.

On List

Reordering

Rows in an order you set, one selected, and Main hall waiting for the order to be saved.

Seats 120

Save the room order before moving Main hall.

Move

A table of records grouped by standing, the last group closed, whose Move dialog offers the groups, and one row whose move was refused.

Requests for Saturday mornings by standing
RequestHirer responsible for the request
Confirmed 2
Choir rehearsal
Oakmere Singers
Yoga class
Priya Shah
Waiting 2
Toddler group
Tom Reid
Chess club

Chess club was not moved to Confirmed: Saturday mornings are fully booked.

Ana Lima
Declined 0

Move

Records with identifier subjects.

Records by number
RecordName
PD/RCT/0001
Saturday hire
PD/RCT/0001/REVISION/00000000000000000000000000000000000000000000000000000001
Revised Saturday hire

Identifier subjects in the rows print form.

Records by number
RecordName
Saturday hire
Revised Saturday hire

A total without a supporting price.

Hire price
ItemPrice
Room hire
£680.00
Total due
£680.00

A total with a credit card price underneath.

Hire price
ItemPrice
Room hire
£680.00
Total due
£680.00
Price when paying by credit card, including the processing fee: £701.94

The same total, printed as labelled rows.

Hire price
ItemPrice
Room hire
£680.00
Total due
£680.00
Price when paying by credit card, including the processing fee: £701.94

A total with a credit card price beneath its name.

Hire price
ItemPrice
Room hire
£680.00
Total duePrice when paying by credit card, including the processing fee: £701.94
£680.00

The Move dialog, opened on its own.

Source
type element
type event
type detail = {dispatcher: element}
type request = {detail: detail}
@val @scope("document") external findById: string => Nullable.t<element> = "getElementById"
@new external requestFrom: (string, request) => event = "CustomEvent"
@send external dispatch: (element, event) => bool = "dispatchEvent"
@get external currentTarget: ReactEvent.Mouse.t => element = "currentTarget"

let room = (~root, ~slug, ~name, ~seats, ~checked=?, ~href, ()) => {
  let row = `${root}-${slug}`
  let subject = row ++ "-subject"
  let row: KitReact.Roles.Row.props = {
    props: {id: row},
    handle: true,
    selection: ?checked->Option.map(checked => <>
      <KitReact.Roles.SelectionControl checked props={{name: "room", value: slug}} />
      <span>{React.string("Select " ++ name)}</span>
    </>),
    subject: React.string(name),
    subjectHref: href,
    subjectProps: {id: subject},
    facts: [{key: "seats", content: Words([KitReact.Words.Text("Seats "), KitReact.Words.Amount(KitReact.Amount.count(Specimen_Reader.reader, seats))]), props: {}}],
    actions: [KitReact.Roles.Row.Move(KitReact.Roles.Reordering.Move.role({row, subject, state: ?(slug == "main-hall" ? Some(KitReact.ActionState.Inert({id: row ++ "-move", words: [KitReact.Words.Text("Save the room order before moving Main hall.")]})) : None)}))],
  }
  row
}

let positions = (root, names) => names->Array.map(((slug, name)): KitReact.Roles.Reordering.position => {id: `${root}-${slug}`, name})

let reorder = (order, root, {rows, place}: KitReact.Reordering.move) => {
  let slugOf = row => row->String.slice(~start=String.length(root) + 1)
  let moving = rows->Array.map(slugOf)
  let rest = order->Array.filter(((slug, _)) => !(moving->Array.includes(slug)))
  let carried = order->Array.filter(((slug, _)) => moving->Array.includes(slug))
  let split = (target, after) => {
    let at = rest->Array.findIndex(((slug, _)) => slug == slugOf(target)) + (after ? 1 : 0)
    rest->Array.slice(~start=0, ~end=at)->Array.concat(carried)->Array.concat(rest->Array.slice(~start=at))
  }
  switch place {
  | Before(target) => split(target, false)
  | After(target) => split(target, true)
  | Into(_) => order
  }
}

let standing = [("confirmed", "Confirmed"), ("waiting", "Waiting"), ("declined", "Declined")]
let requests = [("choir", "Choir rehearsal", "confirmed", "Oakmere Singers"), ("yoga", "Yoga class", "confirmed", "Priya Shah"), ("toddlers", "Toddler group", "waiting", "Tom Reid"), ("chess", "Chess club", "waiting", "Ana Lima")]

let request = (~root, ~href, ~standing=?, (slug, name, group, hirer)): KitReact.Table.RowsTable.row => {
  let row = `${root}-${slug}`
  let subject = row ++ "-subject"
  {
    subject: <a href> {React.string(name)} </a>,
    facts: [{content: Content(React.string(hirer)), props: {}}],
    expansion: None,
    props: {id: row},
    subjectProps: {id: subject},
    group: `${root}-${group}`,
    ?standing,
    actions: [KitReact.Table.Move(KitReact.Roles.Reordering.Move.role({row, subject}))],
  }
}

let refused = ((slug, _, _, _)) =>
  slug == "chess" ? Some(KitReact.Table.Failed({words: [KitReact.Words.Text("Chess club was not moved to Confirmed: Saturday mornings are fully booked.")]})) : None

let standingGroup = (~root, ~expanded=true, (slug, name), count): KitReact.Table.group => {
  let state: KitReact.Roles.Stamp.state = switch slug {
  | "confirmed" => Settled
  | "waiting" => Attention
  | _ => Neutral
  }
  {
    id: `${root}-${slug}`,
    value: <KitReact.Roles.Stamp state> {React.string(name)} </KitReact.Roles.Stamp>,
    count,
    expanded,
  }
}

let standingTable = (~caption, ~rows, ~groups): KitReact.Table.RowsTable.props => {
  caption: React.string(caption),
  subjectHeading: "Request",
  columnHeadings: [{label: "Hirer responsible for the request"}],
  rows,
  groups,
  handle: true,
}

let moveGroup = (root, (slug, name)): KitReact.Roles.Reordering.group => {id: `${root}-${slug}`, name}

let standingGroups = (root): KitReact.Roles.Reordering.groups => {
  first: moveGroup(root, standing[0]->Option.getUnsafe),
  rest: standing->Array.slice(~start=1)->Array.map(group => moveGroup(root, group)),
}

let standingPositions = (root, requests) => requests->Array.map(((slug, name, group, _)): KitReact.Roles.Reordering.position => {id: `${root}-${slug}`, name, group: `${root}-${group}`})

let regroup = (order, root, {rows, place}: KitReact.Reordering.move) => {
  let slugOf = row => row->String.slice(~start=String.length(root) + 1)
  let moving = rows->Array.map(slugOf)
  let rest = order->Array.filter(((slug, _, _, _)) => !(moving->Array.includes(slug)))
  let carriedTo = group => order->Array.filter(((slug, _, _, _)) => moving->Array.includes(slug))->Array.map(((slug, name, _, hirer)) => (slug, name, group, hirer))
  let beside = (target, after) => {
    let at = rest->Array.findIndex(((slug, _, _, _)) => slug == slugOf(target))
    let (_, _, group, _) = rest[at]->Option.getUnsafe
    let at = at + (after ? 1 : 0)
    rest->Array.slice(~start=0, ~end=at)->Array.concat(carriedTo(group))->Array.concat(rest->Array.slice(~start=at))
  }
  switch place {
  | Before(target) => beside(target, false)
  | After(target) => beside(target, true)
  | Into(group) => rest->Array.concat(carriedTo(slugOf(group)))
  }
}

let count = (order, group) => order->Array.filter(((_, _, held, _)) => held == group)->Array.length->Int.toString

let rooms = [("main-hall", ("Main hall", 120, false)), ("garden-room", ("Garden room", 30, true)), ("committee-room", ("Committee room", 12, false))]
let names = order => order->Array.map(((slug, (name, _, _))) => (slug, name))

@react.component
let make = () => {
  let (roomOrder, setRoomOrder) = React.useState(() => rooms)
  let (requestOrder, setRequestOrder) = React.useState(() => requests)
  let totalPrice: KitReact.Words.t = [KitReact.Words.Amount(KitReact.Amount.money(Specimen_Reader.reader, ~amount="680.00", ~currency="GBP"))]
  let cardPrice: KitReact.Supporting.t = [{pieces: [KitReact.Words.Text("Price when paying by credit card, including the processing fee: "), KitReact.Words.Amount(KitReact.Amount.money(Specimen_Reader.reader, ~amount="701.94", ~currency="GBP"))]}]
  <>
  <p className="specimen-case-line">{React.string("Rows in an order you set, one selected, and Main hall waiting for the order to be saved.")}</p>
  <KitReact.Roles.Reordering
    id="reordering-rooms"
    ordering=Ordered({placeLabel: "Place it", rowLabel: "Room"})
    onMove={move => setRoomOrder(order => reorder(order, "reordering-rooms", move))}
    closeLabel="Keep the order"
    positions={positions("reordering-rooms", names(roomOrder))}
    rows={roomOrder->Array.map(((slug, (name, seats, checked))) =>
      room(~root="reordering-rooms", ~slug, ~name, ~seats, ~checked, ~href="#role-reordering", ())
    )}
  />
  <p className="specimen-case-line">{React.string("A table of records grouped by standing, the last group closed, whose Move dialog offers the groups, and one row whose move was refused.")}</p>
  <KitReact.Roles.Reordering.Table
    id="reordering-standing"
    ordering=Ordered({placeLabel: "Place it", rowLabel: "Request", grouping: {groups: standingGroups("reordering-standing"), label: "Standing"}})
    onMove={move => setRequestOrder(order => regroup(order, "reordering-standing", move))}
    closeLabel="Keep the order"
    positions={standingPositions("reordering-standing", requestOrder)}
    table={standingTable(
      ~caption="Requests for Saturday mornings by standing",
      ~rows=requestOrder->Array.map(row => request(~root="reordering-standing", ~href="#role-reordering", ~standing=?refused(row), row)),
      ~groups=[
        standingGroup(~root="reordering-standing", standing[0]->Option.getUnsafe, count(requestOrder, "confirmed")),
        standingGroup(~root="reordering-standing", standing[1]->Option.getUnsafe, count(requestOrder, "waiting")),
        standingGroup(~root="reordering-standing", ~expanded=false, standing[2]->Option.getUnsafe, count(requestOrder, "declined")),
      ],
    )}
  />
  <p className="specimen-case-line">{React.string("Records with identifier subjects.")}</p>
  <KitReact.Table.RowsTable
    props={{id: "records-identifier-subjects"}}
    caption={React.string("Records by number")}
    subjectHeading="Record"
    columnHeadings={[{label: "Name"}]}
    rows={[
      {subject: <a href="#role-reordering"><code>{React.string("PD/RCT/0001")}</code></a>, facts: [{content: Content(React.string("Saturday hire"))}], expansion: None},
      {subject: <a href="#role-reordering"><code>{React.string("PD/RCT/0001/REVISION/00000000000000000000000000000000000000000000000000000001")}</code></a>, facts: [{content: Content(React.string("Revised Saturday hire"))}], expansion: None},
    ]}
  />
  <p className="specimen-case-line">{React.string("Identifier subjects in the rows print form.")}</p>
  <KitReact.Table.RowsTable
    props={{id: "records-identifier-subjects-rows"}}
    printForm=Rows
    caption={React.string("Records by number")}
    subjectHeading="Record"
    columnHeadings={[{label: "Name"}]}
    rows={[
      {subject: <a href="#role-reordering"><code>{React.string("PD/RCT/0001")}</code></a>, facts: [{content: Content(React.string("Saturday hire"))}], expansion: None},
      {subject: <a href="#role-reordering"><code>{React.string("PD/RCT/0001/REVISION/00000000000000000000000000000000000000000000000000000001")}</code></a>, facts: [{content: Content(React.string("Revised Saturday hire"))}], expansion: None},
    ]}
  />
  <p className="specimen-case-line">{React.string("A total without a supporting price.")}</p>
  <KitReact.Table.RowsTable
    props={{id: "records-total"}}
    caption={React.string("Hire price")}
    subjectHeading="Item"
    columnHeadings={[{label: "Price", kind: KitReact.Table.RowsHeadingKind.Amount}]}
    rows={[{subject: React.string("Room hire"), facts: [{content: Words(totalPrice)}], expansion: None}]}
    total={{subject: React.string("Total due"), facts: [Some({content: Words(totalPrice)})]}}
  />
  <p className="specimen-case-line">{React.string("A total with a credit card price underneath.")}</p>
  <KitReact.Table.RowsTable
    props={{id: "records-total-supporting"}}
    caption={React.string("Hire price")}
    subjectHeading="Item"
    columnHeadings={[{label: "Price", kind: KitReact.Table.RowsHeadingKind.Amount}]}
    rows={[{subject: React.string("Room hire"), facts: [{content: Words(totalPrice)}], expansion: None}]}
    total={{subject: React.string("Total due"), facts: [Some({content: Words(totalPrice), supporting: cardPrice})]}}
  />
  <p className="specimen-case-line">{React.string("The same total, printed as labelled rows.")}</p>
  <KitReact.Table.RowsTable
    props={{id: "records-total-supporting-rows"}}
    printForm=Rows
    caption={React.string("Hire price")}
    subjectHeading="Item"
    columnHeadings={[{label: "Price", kind: KitReact.Table.RowsHeadingKind.Amount}]}
    rows={[{subject: React.string("Room hire"), facts: [{content: Words(totalPrice)}], expansion: None}]}
    total={{subject: React.string("Total due"), facts: [Some({content: Words(totalPrice), supporting: cardPrice})]}}
  />
  <p className="specimen-case-line">{React.string("A total with a credit card price beneath its name.")}</p>
  <KitReact.Table.ComparisonTable
    props={{id: "comparison-total-supporting"}}
    caption={React.string("Hire price")}
    subjectHeading={{label: "Item"}}
    columnHeadings={[{label: "Price", kind: KitReact.Table.HeadingKind.Amount}]}
    rows={[{subject: React.string("Room hire"), facts: [{content: Words(totalPrice)}], expansion: None}]}
    total={{subject: React.string("Total due"), facts: [{content: Words(totalPrice)}], subjectSupporting: cardPrice}}
  />
  <p className="specimen-case-line">{React.string("The Move dialog, opened on its own.")}</p>
  <KitReact.Roles.SecondaryAction props={{type_: "button", onClick: event => {
    let dispatcher = currentTarget(event)
    findById("reordering-rooms-move")->Nullable.toOption->Option.forEach(dialog =>
      ignore(dialog->dispatch(requestFrom("kit:modal-open", {detail: {dispatcher: dispatcher}})))
    )
  }}}>{React.string("Show the Move dialog")}</KitReact.Roles.SecondaryAction>
</>
}

Reordering, moving

Show it carried

Source
@react.component
let make = () =>
  <>
  <KitReact.Roles.Reordering.Table
    id="reordering-moving-standing"
    ordering=Ordered({placeLabel: "Place it", rowLabel: "Request", grouping: {groups: Role_Reordering.standingGroups("reordering-moving-standing"), label: "Standing"}})
    onMove={_ => ()}
    closeLabel="Keep the order"
    positions={Role_Reordering.standingPositions("reordering-moving-standing", Role_Reordering.requests)}
    table={Role_Reordering.standingTable(
      ~caption="Requests for Saturday mornings by standing",
      ~rows=Role_Reordering.requests->Array.map(request =>
        Role_Reordering.request(~root="reordering-moving-standing", ~href="../#role-reordering-moving", request)
      ),
      ~groups=[
        Role_Reordering.standingGroup(~root="reordering-moving-standing", Role_Reordering.standing[0]->Option.getUnsafe, "2"),
        Role_Reordering.standingGroup(~root="reordering-moving-standing", Role_Reordering.standing[1]->Option.getUnsafe, "2"),
        Role_Reordering.standingGroup(~root="reordering-moving-standing", Role_Reordering.standing[2]->Option.getUnsafe, "0"),
      ],
    )}
  />
  <p className="specimen-case-line">{React.string("Rooms waiting for their order to be checked.")}</p>
  <details>
    <summary>{React.string("Move a room")}</summary>
    <KitReact.Roles.Reordering
      id="reordering-inert-rooms"
      ordering=Ordered({placeLabel: "Place it", rowLabel: "Room"})
      onMove={_ => ()}
      closeLabel="Keep the order"
      moveState=Inert({id: "reordering-inert-rooms-commit", words: [KitReact.Words.Text("Move waits until the room order has been checked.")]})
      positions={Role_Reordering.positions("reordering-inert-rooms", [("garden-room", "Garden room"), ("committee-room", "Committee room")])}
      rows={[("garden-room", "Garden room", 30), ("committee-room", "Committee room", 12)]->Array.map(((slug, name, seats)) =>
        Role_Reordering.room(~root="reordering-inert-rooms", ~slug, ~name, ~seats, ~href="../#role-reordering-moving", ())
      )}
    />
  </details>
  </>