@elaraai/e3-ui 1.0.98 → 1.0.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -2
- package/dist/src/allocation/adapters.d.ts +1 -1
- package/dist/src/allocation/adapters.js +1 -1
- package/dist/src/allocation/index.d.ts +46 -90
- package/dist/src/allocation/index.d.ts.map +1 -1
- package/dist/src/allocation/index.js +5 -5
- package/dist/src/allocation/index.js.map +1 -1
- package/dist/src/bind/data-indexed.d.ts +273 -51
- package/dist/src/bind/data-indexed.d.ts.map +1 -1
- package/dist/src/bind/data-indexed.js +233 -56
- package/dist/src/bind/data-indexed.js.map +1 -1
- package/dist/src/bind/data.d.ts +48 -9
- package/dist/src/bind/data.d.ts.map +1 -1
- package/dist/src/bind/data.js +48 -3
- package/dist/src/bind/data.js.map +1 -1
- package/dist/src/bind/record.d.ts +61 -24
- package/dist/src/bind/record.d.ts.map +1 -1
- package/dist/src/bind/record.js +17 -57
- package/dist/src/bind/record.js.map +1 -1
- package/dist/src/calendar/index.d.ts +55 -95
- package/dist/src/calendar/index.d.ts.map +1 -1
- package/dist/src/calendar/index.js +10 -6
- package/dist/src/calendar/index.js.map +1 -1
- package/dist/src/flowchart/index.d.ts +35 -31
- package/dist/src/flowchart/index.d.ts.map +1 -1
- package/dist/src/flowchart/index.js +35 -31
- package/dist/src/flowchart/index.js.map +1 -1
- package/dist/src/flowchart/library.d.ts +108 -84
- package/dist/src/flowchart/library.d.ts.map +1 -1
- package/dist/src/flowchart/library.js +108 -84
- package/dist/src/flowchart/library.js.map +1 -1
- package/dist/src/flowchart/payload.d.ts +39 -107
- package/dist/src/flowchart/payload.d.ts.map +1 -1
- package/dist/src/flowchart/payload.js +3 -3
- package/dist/src/flowchart/payload.js.map +1 -1
- package/dist/src/flowchart/values.d.ts +35 -31
- package/dist/src/flowchart/values.d.ts.map +1 -1
- package/dist/src/flowchart/values.js +35 -31
- package/dist/src/flowchart/values.js.map +1 -1
- package/dist/src/flowchart/window.d.ts +0 -12
- package/dist/src/flowchart/window.d.ts.map +1 -1
- package/dist/src/flowchart/window.js +3 -2
- package/dist/src/flowchart/window.js.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/internal.d.ts +7 -3
- package/dist/src/internal.d.ts.map +1 -1
- package/dist/src/internal.js +12 -3
- package/dist/src/internal.js.map +1 -1
- package/dist/src/plan/assemble.d.ts +8 -0
- package/dist/src/plan/assemble.d.ts.map +1 -1
- package/dist/src/plan/assemble.js +1 -1
- package/dist/src/plan/assemble.js.map +1 -1
- package/dist/src/plan/builders.d.ts +1518 -2
- package/dist/src/plan/builders.d.ts.map +1 -1
- package/dist/src/plan/builders.js +248 -114
- package/dist/src/plan/builders.js.map +1 -1
- package/dist/src/plan/event-rows.d.ts +9 -14
- package/dist/src/plan/event-rows.d.ts.map +1 -1
- package/dist/src/plan/event-rows.js +6 -46
- package/dist/src/plan/event-rows.js.map +1 -1
- package/dist/src/plan/factories.d.ts +72 -4
- package/dist/src/plan/factories.d.ts.map +1 -1
- package/dist/src/plan/factories.js +147 -1
- package/dist/src/plan/factories.js.map +1 -1
- package/dist/src/plan/index.d.ts +147 -125
- package/dist/src/plan/index.d.ts.map +1 -1
- package/dist/src/plan/index.js +135 -122
- package/dist/src/plan/index.js.map +1 -1
- package/dist/src/plan/ir.d.ts +1105 -44
- package/dist/src/plan/ir.d.ts.map +1 -1
- package/dist/src/plan/ir.js +5 -0
- package/dist/src/plan/ir.js.map +1 -1
- package/dist/src/plan/library.d.ts +65 -42
- package/dist/src/plan/library.d.ts.map +1 -1
- package/dist/src/plan/library.js +65 -42
- package/dist/src/plan/library.js.map +1 -1
- package/dist/src/plan/nested-checks.d.ts +146 -0
- package/dist/src/plan/nested-checks.d.ts.map +1 -0
- package/dist/src/plan/nested-checks.js +468 -0
- package/dist/src/plan/nested-checks.js.map +1 -0
- package/dist/src/plan/nested-patch.d.ts +35 -0
- package/dist/src/plan/nested-patch.d.ts.map +1 -0
- package/dist/src/plan/nested-patch.js +143 -0
- package/dist/src/plan/nested-patch.js.map +1 -0
- package/dist/src/plan/nested-read.d.ts +3250 -0
- package/dist/src/plan/nested-read.d.ts.map +1 -0
- package/dist/src/plan/nested-read.js +163 -0
- package/dist/src/plan/nested-read.js.map +1 -0
- package/dist/src/plan/nested.d.ts +280 -0
- package/dist/src/plan/nested.d.ts.map +1 -0
- package/dist/src/plan/nested.js +6 -0
- package/dist/src/plan/nested.js.map +1 -0
- package/dist/src/plan/ordered-source.d.ts +1030 -1
- package/dist/src/plan/ordered-source.d.ts.map +1 -1
- package/dist/src/plan/ordered-source.js +48 -6
- package/dist/src/plan/ordered-source.js.map +1 -1
- package/dist/src/plan/over.d.ts +111 -75
- package/dist/src/plan/over.d.ts.map +1 -1
- package/dist/src/plan/over.js +111 -75
- package/dist/src/plan/over.js.map +1 -1
- package/dist/src/plan/pick.d.ts +15 -3
- package/dist/src/plan/pick.d.ts.map +1 -1
- package/dist/src/plan/pick.js +75 -36
- package/dist/src/plan/pick.js.map +1 -1
- package/dist/src/plan/plan.d.ts +2719 -558
- package/dist/src/plan/plan.d.ts.map +1 -1
- package/dist/src/plan/plan.js +14 -2
- package/dist/src/plan/plan.js.map +1 -1
- package/dist/src/plan/reads.d.ts +421 -0
- package/dist/src/plan/reads.d.ts.map +1 -0
- package/dist/src/plan/reads.js +143 -0
- package/dist/src/plan/reads.js.map +1 -0
- package/dist/src/plan/refs.d.ts +111 -75
- package/dist/src/plan/refs.d.ts.map +1 -1
- package/dist/src/plan/refs.js +111 -75
- package/dist/src/plan/refs.js.map +1 -1
- package/dist/src/plan/root.d.ts +10 -5
- package/dist/src/plan/root.d.ts.map +1 -1
- package/dist/src/plan/root.js +76 -13
- package/dist/src/plan/root.js.map +1 -1
- package/dist/src/plan/series.d.ts +5140 -4331
- package/dist/src/plan/series.d.ts.map +1 -1
- package/dist/src/plan/series.js +1026 -264
- package/dist/src/plan/series.js.map +1 -1
- package/dist/src/plan/shade.d.ts +981 -0
- package/dist/src/plan/shade.d.ts.map +1 -0
- package/dist/src/plan/shade.js +60 -0
- package/dist/src/plan/shade.js.map +1 -0
- package/dist/src/plan/types.d.ts +178 -18
- package/dist/src/plan/types.d.ts.map +1 -1
- package/dist/src/plan/types.js +69 -19
- package/dist/src/plan/types.js.map +1 -1
- package/dist/src/query/builder.d.ts +3 -3
- package/dist/src/query/builder.d.ts.map +1 -1
- package/dist/src/query/builder.js +1 -1
- package/dist/src/query/library.d.ts +3 -3
- package/dist/src/query/library.d.ts.map +1 -1
- package/dist/src/query/library.js +1 -1
- package/dist/src/query/queries.d.ts +2 -2
- package/dist/src/query/queries.js +1 -1
- package/dist/src/query/saved.d.ts +8 -4
- package/dist/src/query/saved.d.ts.map +1 -1
- package/dist/src/query/saved.js +8 -4
- package/dist/src/query/saved.js.map +1 -1
- package/dist/src/query/types.d.ts +8 -3
- package/dist/src/query/types.d.ts.map +1 -1
- package/dist/src/query/types.js +8 -3
- package/dist/src/query/types.js.map +1 -1
- package/dist/src/roster/index.d.ts +47 -91
- package/dist/src/roster/index.d.ts.map +1 -1
- package/dist/src/roster/index.js +6 -6
- package/dist/src/roster/index.js.map +1 -1
- package/dist/src/roster/targets.d.ts +8 -4
- package/dist/src/roster/targets.d.ts.map +1 -1
- package/dist/src/roster/targets.js +9 -5
- package/dist/src/roster/targets.js.map +1 -1
- package/dist/src/schedule/cell-checks.js +3 -3
- package/dist/src/schedule/cell-checks.js.map +1 -1
- package/dist/src/schedule/days.d.ts +1 -1
- package/dist/src/schedule/days.js +1 -1
- package/dist/src/schedule/events.d.ts +10 -6
- package/dist/src/schedule/events.d.ts.map +1 -1
- package/dist/src/schedule/events.js +252 -228
- package/dist/src/schedule/events.js.map +1 -1
- package/dist/src/schedule/patch.d.ts +1 -1
- package/dist/src/schedule/patch.js +1 -1
- package/dist/src/schedule/periods.d.ts +8 -4
- package/dist/src/schedule/periods.d.ts.map +1 -1
- package/dist/src/schedule/periods.js +1 -1
- package/dist/src/schedule/periods.js.map +1 -1
- package/dist/src/schedule/record-window.d.ts +38 -58
- package/dist/src/schedule/record-window.d.ts.map +1 -1
- package/dist/src/schedule/record-window.js +15 -16
- package/dist/src/schedule/record-window.js.map +1 -1
- package/dist/src/schedule/resources.d.ts +1 -1
- package/dist/src/schedule/session.d.ts +1 -1
- package/dist/src/schedule/session.js +1 -1
- package/dist/src/schedule/types.d.ts +29 -1
- package/dist/src/schedule/types.d.ts.map +1 -1
- package/dist/src/schedule/types.js +16 -1
- package/dist/src/schedule/types.js.map +1 -1
- package/dist/src/schematic/index.d.ts +19 -9
- package/dist/src/schematic/index.d.ts.map +1 -1
- package/dist/src/schematic/index.js +19 -9
- package/dist/src/schematic/index.js.map +1 -1
- package/dist/src/schematic/kinds.d.ts +4 -4
- package/dist/src/schematic/kinds.js +9 -9
- package/dist/src/schematic/kinds.js.map +1 -1
- package/dist/src/schematic/payload.d.ts +38 -88
- package/dist/src/schematic/payload.d.ts.map +1 -1
- package/dist/src/schematic/payload.js +4 -4
- package/dist/src/schematic/payload.js.map +1 -1
- package/dist/src/schematic/window.d.ts +8 -7
- package/dist/src/schematic/window.d.ts.map +1 -1
- package/dist/src/schematic/window.js +22 -19
- package/dist/src/schematic/window.js.map +1 -1
- package/dist/src/sheet/bridge.d.ts +19 -0
- package/dist/src/sheet/bridge.d.ts.map +1 -1
- package/dist/src/sheet/bridge.js +28 -21
- package/dist/src/sheet/bridge.js.map +1 -1
- package/dist/src/sheet/index.d.ts +66 -46
- package/dist/src/sheet/index.d.ts.map +1 -1
- package/dist/src/sheet/index.js +66 -46
- package/dist/src/sheet/index.js.map +1 -1
- package/dist/src/sheet/library.d.ts +13 -8
- package/dist/src/sheet/library.d.ts.map +1 -1
- package/dist/src/sheet/library.js +13 -8
- package/dist/src/sheet/library.js.map +1 -1
- package/dist/src/sheet/record.d.ts +1 -1
- package/dist/src/sheet/record.js +2 -2
- package/dist/src/sheet/record.js.map +1 -1
- package/dist/src/sheet/registers.d.ts +152 -128
- package/dist/src/sheet/registers.d.ts.map +1 -1
- package/dist/src/sheet/registers.js +152 -128
- package/dist/src/sheet/registers.js.map +1 -1
- package/dist/src/sheet/root.d.ts +4 -1
- package/dist/src/sheet/root.d.ts.map +1 -1
- package/dist/src/sheet/root.js +15 -3
- package/dist/src/sheet/root.js.map +1 -1
- package/dist/src/sheet/sheet.d.ts +39 -113
- package/dist/src/sheet/sheet.d.ts.map +1 -1
- package/dist/src/sheet/sheet.js +7 -5
- package/dist/src/sheet/sheet.js.map +1 -1
- package/dist/src/sheet/sub-rows.d.ts +106 -76
- package/dist/src/sheet/sub-rows.d.ts.map +1 -1
- package/dist/src/sheet/sub-rows.js +106 -76
- package/dist/src/sheet/sub-rows.js.map +1 -1
- package/dist/src/sheet/types.d.ts +19 -62
- package/dist/src/sheet/types.d.ts.map +1 -1
- package/dist/src/studio/builder.d.ts +3 -153
- package/dist/src/studio/builder.d.ts.map +1 -1
- package/dist/src/studio/builder.js +1 -1
- package/dist/src/studio/canvas.d.ts +5 -4
- package/dist/src/studio/canvas.d.ts.map +1 -1
- package/dist/src/studio/canvas.js +8 -7
- package/dist/src/studio/canvas.js.map +1 -1
- package/dist/src/studio/component.d.ts +30 -20
- package/dist/src/studio/component.d.ts.map +1 -1
- package/dist/src/studio/component.js +26 -61
- package/dist/src/studio/component.js.map +1 -1
- package/dist/src/studio/index.d.ts +2 -2
- package/dist/src/studio/index.d.ts.map +1 -1
- package/dist/src/studio/index.js +1 -1
- package/dist/src/studio/index.js.map +1 -1
- package/dist/src/studio/inspector.d.ts +10 -39
- package/dist/src/studio/inspector.d.ts.map +1 -1
- package/dist/src/studio/inspector.js +11 -31
- package/dist/src/studio/inspector.js.map +1 -1
- package/dist/src/studio/library.d.ts +3 -169
- package/dist/src/studio/library.d.ts.map +1 -1
- package/dist/src/studio/library.js +1 -1
- package/dist/src/studio/page.d.ts +0 -8
- package/dist/src/studio/page.d.ts.map +1 -1
- package/dist/src/studio/pages.d.ts +17 -591
- package/dist/src/studio/pages.d.ts.map +1 -1
- package/dist/src/studio/pages.js +17 -44
- package/dist/src/studio/pages.js.map +1 -1
- package/dist/src/studio/palette.d.ts +2 -7
- package/dist/src/studio/palette.d.ts.map +1 -1
- package/dist/src/studio/palette.js +4 -5
- package/dist/src/studio/palette.js.map +1 -1
- package/dist/src/studio/publish.d.ts +8 -18
- package/dist/src/studio/publish.d.ts.map +1 -1
- package/dist/src/studio/publish.js +10 -36
- package/dist/src/studio/publish.js.map +1 -1
- package/dist/src/utils/derive.d.ts +12 -12
- package/dist/src/utils/derive.d.ts.map +1 -1
- package/dist/src/utils/derive.js +5 -2
- package/dist/src/utils/derive.js.map +1 -1
- package/dist/src/utils/ordered-source.d.ts.map +1 -1
- package/dist/src/utils/ordered-source.js +14 -5
- package/dist/src/utils/ordered-source.js.map +1 -1
- package/dist/test/allocation/allocation.examples.d.ts +1900 -1598
- package/dist/test/allocation/allocation.examples.d.ts.map +1 -1
- package/dist/test/allocation/allocation.examples.js +179 -89
- package/dist/test/allocation/allocation.examples.js.map +1 -1
- package/dist/test/allocation/optimise.examples.d.ts.map +1 -1
- package/dist/test/allocation/optimise.examples.js +7 -7
- package/dist/test/allocation/optimise.examples.js.map +1 -1
- package/dist/test/bind/data/data.examples.d.ts +1057 -903
- package/dist/test/bind/data/data.examples.d.ts.map +1 -1
- package/dist/test/bind/data/data.examples.js +260 -204
- package/dist/test/bind/data/data.examples.js.map +1 -1
- package/dist/test/bind/record/record.examples.d.ts +457 -426
- package/dist/test/bind/record/record.examples.d.ts.map +1 -1
- package/dist/test/bind/record/record.examples.js +41 -21
- package/dist/test/bind/record/record.examples.js.map +1 -1
- package/dist/test/calendar/calendar-records.examples.d.ts +162 -170
- package/dist/test/calendar/calendar-records.examples.d.ts.map +1 -1
- package/dist/test/calendar/calendar-records.examples.js +20 -12
- package/dist/test/calendar/calendar-records.examples.js.map +1 -1
- package/dist/test/calendar/calendar-windows.examples.d.ts.map +1 -1
- package/dist/test/calendar/calendar-windows.examples.js +12 -12
- package/dist/test/calendar/calendar-windows.examples.js.map +1 -1
- package/dist/test/calendar/calendar.examples.d.ts +1172 -901
- package/dist/test/calendar/calendar.examples.d.ts.map +1 -1
- package/dist/test/calendar/calendar.examples.js +211 -124
- package/dist/test/calendar/calendar.examples.js.map +1 -1
- package/dist/test/calendar/optimise.examples.js +2 -2
- package/dist/test/calendar/optimise.examples.js.map +1 -1
- package/dist/test/examples-package.d.ts +6 -9
- package/dist/test/examples-package.d.ts.map +1 -1
- package/dist/test/examples-package.js +11 -32
- package/dist/test/examples-package.js.map +1 -1
- package/dist/test/flowchart/flowchart.examples.d.ts +3841 -2820
- package/dist/test/flowchart/flowchart.examples.d.ts.map +1 -1
- package/dist/test/flowchart/flowchart.examples.js +211 -121
- package/dist/test/flowchart/flowchart.examples.js.map +1 -1
- package/dist/test/plan/optimise.examples.js +2 -2
- package/dist/test/plan/optimise.examples.js.map +1 -1
- package/dist/test/plan/plan-events.examples.d.ts +4055 -3263
- package/dist/test/plan/plan-events.examples.d.ts.map +1 -1
- package/dist/test/plan/plan-events.examples.js +492 -319
- package/dist/test/plan/plan-events.examples.js.map +1 -1
- package/dist/test/plan/plan-nested.examples.d.ts +1829 -0
- package/dist/test/plan/plan-nested.examples.d.ts.map +1 -0
- package/dist/test/plan/plan-nested.examples.js +321 -0
- package/dist/test/plan/plan-nested.examples.js.map +1 -0
- package/dist/test/plan/plan-records.examples.d.ts +328 -362
- package/dist/test/plan/plan-records.examples.d.ts.map +1 -1
- package/dist/test/plan/plan-records.examples.js +81 -57
- package/dist/test/plan/plan-records.examples.js.map +1 -1
- package/dist/test/plan/plan-windows.examples.d.ts +1756 -1398
- package/dist/test/plan/plan-windows.examples.d.ts.map +1 -1
- package/dist/test/plan/plan-windows.examples.js +161 -128
- package/dist/test/plan/plan-windows.examples.js.map +1 -1
- package/dist/test/plan/plan.examples.d.ts +2238 -5712
- package/dist/test/plan/plan.examples.d.ts.map +1 -1
- package/dist/test/plan/plan.examples.js +839 -968
- package/dist/test/plan/plan.examples.js.map +1 -1
- package/dist/test/query/query.examples.d.ts +5221 -3005
- package/dist/test/query/query.examples.d.ts.map +1 -1
- package/dist/test/query/query.examples.js +169 -118
- package/dist/test/query/query.examples.js.map +1 -1
- package/dist/test/roster/optimise.examples.d.ts.map +1 -1
- package/dist/test/roster/optimise.examples.js +7 -7
- package/dist/test/roster/optimise.examples.js.map +1 -1
- package/dist/test/roster/roster-records.examples.d.ts +364 -392
- package/dist/test/roster/roster-records.examples.d.ts.map +1 -1
- package/dist/test/roster/roster-records.examples.js +40 -26
- package/dist/test/roster/roster-records.examples.js.map +1 -1
- package/dist/test/roster/roster.examples.d.ts +4605 -4308
- package/dist/test/roster/roster.examples.d.ts.map +1 -1
- package/dist/test/roster/roster.examples.js +212 -113
- package/dist/test/roster/roster.examples.js.map +1 -1
- package/dist/test/schedule/schedule.examples.d.ts +2646 -2471
- package/dist/test/schedule/schedule.examples.d.ts.map +1 -1
- package/dist/test/schedule/schedule.examples.js +144 -88
- package/dist/test/schedule/schedule.examples.js.map +1 -1
- package/dist/test/schematic/schematic-records.examples.d.ts +286 -276
- package/dist/test/schematic/schematic-records.examples.d.ts.map +1 -1
- package/dist/test/schematic/schematic-records.examples.js +52 -38
- package/dist/test/schematic/schematic-records.examples.js.map +1 -1
- package/dist/test/schematic/schematic.examples.d.ts +1842 -1491
- package/dist/test/schematic/schematic.examples.d.ts.map +1 -1
- package/dist/test/schematic/schematic.examples.js +240 -134
- package/dist/test/schematic/schematic.examples.js.map +1 -1
- package/dist/test/sheet/optimise.examples.d.ts +102 -95
- package/dist/test/sheet/optimise.examples.d.ts.map +1 -1
- package/dist/test/sheet/optimise.examples.js +7 -3
- package/dist/test/sheet/optimise.examples.js.map +1 -1
- package/dist/test/sheet/sheet-records.examples.d.ts +175 -214
- package/dist/test/sheet/sheet-records.examples.d.ts.map +1 -1
- package/dist/test/sheet/sheet-records.examples.js +29 -17
- package/dist/test/sheet/sheet-records.examples.js.map +1 -1
- package/dist/test/sheet/sheet.examples.d.ts +3642 -3353
- package/dist/test/sheet/sheet.examples.d.ts.map +1 -1
- package/dist/test/sheet/sheet.examples.js +389 -282
- package/dist/test/sheet/sheet.examples.js.map +1 -1
- package/dist/test/studio/component.examples.js +2 -2
- package/dist/test/studio/component.examples.js.map +1 -1
- package/dist/test/studio/page.examples.js +6 -6
- package/dist/test/studio/page.examples.js.map +1 -1
- package/dist/test/studio/studio.examples.d.ts +2329 -873
- package/dist/test/studio/studio.examples.d.ts.map +1 -1
- package/dist/test/studio/studio.examples.js +155 -107
- package/dist/test/studio/studio.examples.js.map +1 -1
- package/package.json +15 -11
|
@@ -4,7 +4,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "@elaraai/e3-ui/jsx-runtime";
|
|
|
4
4
|
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
5
5
|
*/
|
|
6
6
|
/** @jsxImportSource @elaraai/e3-ui */
|
|
7
|
-
import { ArrayType, BooleanType, DateTimeType, DictType, East, FloatType, IntegerType, NullType, OptionType, RecursiveType, StringType, StructType, VariantType, example, none, some, variant, } from "@elaraai/east";
|
|
7
|
+
import { ArrayType, BooleanType, DateTimeType, DictType, East, FloatType, IntegerType, NullType, OptionType, RecursiveType, SortedMap, StringType, StructType, VariantType, compareFor, example, none, some, variant, } from "@elaraai/east";
|
|
8
8
|
import { DragEventType, Editing, EventStateType, State, StatusValueType, Style, UIComponentType } from "@elaraai/east-ui";
|
|
9
9
|
import { Badge, Box, Button, Chart, Configurator, Format, HStack, Progress, Reactive, SegmentGroup, Select, Slice, Sparkline, Text, VStack } from "@elaraai/east-ui";
|
|
10
10
|
import { Data, Plan, Record } from "@elaraai/e3-ui";
|
|
@@ -69,306 +69,309 @@ export const generateTargetHorizon = East.function([IntegerType], ArrayType(Targ
|
|
|
69
69
|
* flagship's Deliveries tab lists, the rail's `deliveries` source narrowing
|
|
70
70
|
* them by hall and risk, its range the canvas's window (#1344).
|
|
71
71
|
*/
|
|
72
|
-
export const planTargetDeliveries = e3.record("plan_target_deliveries", DictType(StringType, TargetHorizonRow),
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
72
|
+
export const planTargetDeliveries = e3.record("plan_target_deliveries", DictType(StringType, TargetHorizonRow), {
|
|
73
|
+
initial: () => East.compile(East.function([IntegerType], DictType(StringType, TargetHorizonRow), ($, count) => {
|
|
74
|
+
const generate = $.const(generateTargetHorizon);
|
|
75
|
+
return generate(count).toDict((_$2, delivery) => delivery.key, (_$2, delivery) => delivery);
|
|
76
|
+
}), [])(36n),
|
|
77
|
+
indexes: (index) => ({
|
|
78
|
+
// The deliveries through their indexes, each ending at the delivery's instant, so every narrowing — a hall, a
|
|
79
|
+
// risk, both — holds the window too.
|
|
80
|
+
plan_target_deliveries_by_at: index.on(["at"]),
|
|
81
|
+
plan_target_deliveries_by_risk_at: index.on(["risk", "at"]),
|
|
82
|
+
plan_target_deliveries_by_hall_at: index.on(["hall", "at"]),
|
|
83
|
+
plan_target_deliveries_by_hall_risk_at: index.on(["hall", "risk", "at"]),
|
|
84
|
+
}),
|
|
85
|
+
});
|
|
86
|
+
/** A job on a press, under its id — the RAW record an ops dataset stores: its
|
|
87
|
+
* phase and optional job ticket, its window, optional sheets, lifecycle state
|
|
88
|
+
* and alert. */
|
|
84
89
|
export const TargetJob = StructType({
|
|
85
|
-
|
|
90
|
+
phase: StringType, ticket: OptionType(StringType),
|
|
86
91
|
start: DateTimeType, end: DateTimeType,
|
|
87
92
|
sheets: OptionType(FloatType), state: EventStateType,
|
|
88
93
|
alert: OptionType(StatusValueType),
|
|
89
94
|
});
|
|
90
|
-
/** A crew's shift — its window, its hours and its lifecycle state. */
|
|
91
|
-
export const TargetShift = StructType({
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
/** A crew's shift, under its id — its window, its hours and its lifecycle state. */
|
|
96
|
+
export const TargetShift = StructType({ from: DateTimeType, to: DateTimeType, hours: FloatType, state: EventStateType });
|
|
97
|
+
/** A van drop, under its id — its week and its lifecycle state. */
|
|
98
|
+
export const TargetAlloc = StructType({ at: DateTimeType, state: EventStateType });
|
|
99
|
+
/** A decision on a run's transition, under its id — when, and whether it is applied. */
|
|
100
|
+
export const TargetDecision = StructType({ at: DateTimeType, applied: BooleanType });
|
|
101
|
+
/** Sheets moving in or out of a press, at their instant — the caption. */
|
|
102
|
+
export const TargetPort = StructType({ label: StringType });
|
|
103
|
+
/** A van's capacity alert, at its week — how bad, and what it says. */
|
|
104
|
+
export const TargetAlert = StructType({ status: StatusValueType, message: StringType });
|
|
105
|
+
/** A tile's look channels, as a bucket row's tile holds them. */
|
|
106
|
+
const TileLook = Plan.Types.BucketEvent.fields;
|
|
107
|
+
/** A tile stored as it draws, under its id: its week, its lane and every channel of its look. */
|
|
108
|
+
export const TargetTile = StructType({
|
|
109
|
+
at: DateTimeType, lane: TileLook.lane, label: TileLook.label, icon: TileLook.icon, state: TileLook.state,
|
|
110
|
+
tone: TileLook.tone, color: TileLook.color, colorPalette: TileLook.colorPalette, stretch: TileLook.stretch,
|
|
111
|
+
content: TileLook.content, animation: TileLook.animation,
|
|
94
112
|
});
|
|
95
|
-
/** A
|
|
96
|
-
export const
|
|
113
|
+
/** A chip stored as it draws, under its id: its window, its text, its state and its icon. */
|
|
114
|
+
export const TargetChip = StructType({ from: DateTimeType, to: DateTimeType, label: StringType, state: EventStateType, icon: Plan.Types.Chip.fields.icon });
|
|
115
|
+
/** A mark on a stream, under its id: its instant, its kind, its icon and its caption. */
|
|
116
|
+
export const TargetMark = StructType({ at: DateTimeType, kind: Plan.Types.EventMarkKind, icon: Plan.Types.EventMark.fields.icon, label: OptionType(StringType) });
|
|
97
117
|
/**
|
|
98
118
|
* The ops row — every series' rows in ONE keyed source, discriminated by the
|
|
99
|
-
* kind variant (the natural ops-dataset shape). The
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* it
|
|
107
|
-
*
|
|
108
|
-
*
|
|
119
|
+
* kind variant (the natural ops-dataset shape). The kind carries a row's own
|
|
120
|
+
* facts; presence — a status, an expand declaration (`detail`) — is per-row
|
|
121
|
+
* Option data the accessors pass through. A row's COLLECTIONS sit at its top,
|
|
122
|
+
* each a Dict a series reads by name (#1436): the things people drag keyed by
|
|
123
|
+
* their ids (`jobs`, `decisions`, `drops`, `tiles`, `shifts`, `chips`,
|
|
124
|
+
* `marks`), a value a week keyed by the week (`ontime`, `load`, `util`,
|
|
125
|
+
* `delivered`, `sheets`), and the rows nested under it. A row that stands in a
|
|
126
|
+
* hall names it (`hall`), and a data step groups those rows into their hall's
|
|
127
|
+
* entry (the `hall` arm, which the dataset itself never stores), its rows its
|
|
128
|
+
* `members`. The row is recursive, so the hierarchy is the data's own (#822):
|
|
129
|
+
* a hall holds its members, and a contract the presses it books (`presses`),
|
|
130
|
+
* to whatever depth the data has. A press names the span series that draws it
|
|
131
|
+
* (`series`): a hall's presses, the proposal flavours, the detail line, a
|
|
132
|
+
* contract rolled up by union or by status, a delivery.
|
|
109
133
|
*/
|
|
110
134
|
export const TargetOpsRow = RecursiveType((self) => StructType({
|
|
111
135
|
hall: OptionType(StringType),
|
|
112
136
|
kind: VariantType({
|
|
113
|
-
kpi: StructType({ name: StringType, headline: StringType, pinned: BooleanType,
|
|
114
|
-
from: DateTimeType, weekly: ArrayType(FloatType), detail: OptionType(Plan.Types.Expand) }),
|
|
137
|
+
kpi: StructType({ name: StringType, headline: StringType, pinned: BooleanType, detail: OptionType(Plan.Types.Expand) }),
|
|
115
138
|
press: StructType({ series: StringType, rate: OptionType(FloatType), sub: OptionType(StringType),
|
|
116
|
-
status: OptionType(StatusValueType), detail: OptionType(Plan.Types.Expand),
|
|
117
|
-
|
|
118
|
-
decisions: ArrayType(Plan.Types.DecisionMark),
|
|
119
|
-
ports: ArrayType(Plan.Types.Port),
|
|
120
|
-
presses: DictType(StringType, self) }),
|
|
121
|
-
load: StructType({ name: StringType, sub: StringType, from: DateTimeType,
|
|
122
|
-
weekly: ArrayType(OptionType(FloatType)), detail: OptionType(Plan.Types.Expand) }),
|
|
139
|
+
status: OptionType(StatusValueType), detail: OptionType(Plan.Types.Expand) }),
|
|
140
|
+
load: StructType({ name: StringType, sub: StringType, detail: OptionType(Plan.Types.Expand) }),
|
|
123
141
|
van: StructType({ name: StringType, region: StringType, sub: OptionType(StringType), detail: OptionType(Plan.Types.Expand),
|
|
124
|
-
lanes: ArrayType(Plan.Types.Lane)
|
|
125
|
-
tiles: ArrayType(Plan.Types.BucketEvent), markers: ArrayType(Plan.Types.CellMarker) }),
|
|
142
|
+
lanes: ArrayType(Plan.Types.Lane) }),
|
|
126
143
|
crew: StructType({ name: StringType, pool: BooleanType, sub: OptionType(StringType), value: OptionType(StringType),
|
|
127
|
-
detail: OptionType(Plan.Types.Expand)
|
|
128
|
-
stream: StructType({ name: StringType, cadence: OptionType(StringType), detail: OptionType(Plan.Types.Expand),
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
sheets: ArrayType(Plan.Types.TableCell) }),
|
|
133
|
-
hall: StructType({ members: DictType(StringType, self) }),
|
|
144
|
+
detail: OptionType(Plan.Types.Expand) }),
|
|
145
|
+
stream: StructType({ name: StringType, cadence: OptionType(StringType), detail: OptionType(Plan.Types.Expand) }),
|
|
146
|
+
tally: StructType({ name: StringType, detail: OptionType(Plan.Types.Expand) }),
|
|
147
|
+
finisher: StructType({ name: StringType }),
|
|
148
|
+
hall: NullType,
|
|
134
149
|
}),
|
|
150
|
+
jobs: DictType(StringType, TargetJob),
|
|
151
|
+
decisions: DictType(StringType, TargetDecision),
|
|
152
|
+
ports: DictType(DateTimeType, TargetPort),
|
|
153
|
+
drops: DictType(StringType, TargetAlloc),
|
|
154
|
+
tiles: DictType(StringType, TargetTile),
|
|
155
|
+
alerts: DictType(DateTimeType, TargetAlert),
|
|
156
|
+
shifts: DictType(StringType, TargetShift),
|
|
157
|
+
chips: DictType(StringType, TargetChip),
|
|
158
|
+
marks: DictType(StringType, TargetMark),
|
|
159
|
+
ontime: DictType(DateTimeType, FloatType),
|
|
160
|
+
load: DictType(DateTimeType, OptionType(FloatType)),
|
|
161
|
+
util: DictType(DateTimeType, FloatType),
|
|
162
|
+
delivered: DictType(DateTimeType, OptionType(FloatType)),
|
|
163
|
+
sheets: DictType(DateTimeType, FloatType),
|
|
164
|
+
presses: DictType(StringType, self),
|
|
165
|
+
members: DictType(StringType, self),
|
|
135
166
|
}));
|
|
167
|
+
/** Every collection an ops row holds, empty: a row names the few it fills. */
|
|
168
|
+
const TARGET_EMPTY = {
|
|
169
|
+
jobs: new Map(), decisions: new Map(), ports: new Map(), drops: new Map(), tiles: new Map(), alerts: new Map(),
|
|
170
|
+
shifts: new Map(), chips: new Map(), marks: new Map(), ontime: new Map(), load: new Map(), util: new Map(),
|
|
171
|
+
delivered: new Map(), sheets: new Map(), presses: new Map(), members: new Map(),
|
|
172
|
+
};
|
|
173
|
+
/** Values keyed by their instants — East keeps a Dict keyed by DateTime as a `SortedMap` in its order. */
|
|
174
|
+
const atInstants = (entries) => new SortedMap(entries, compareFor(DateTimeType));
|
|
175
|
+
/** A value a week from W27 (Monday 29 June 2026), each keyed by its week's instant. */
|
|
176
|
+
const fromW27 = (weekly) => atInstants(weekly.map((v, i) => [new Date(Date.UTC(2026, 5, 29 + 7 * i)), v]));
|
|
136
177
|
/**
|
|
137
178
|
* The ONE ops source — every series' rows in one KEYED collection, RAW: no
|
|
138
|
-
* display strings the accessors can derive, no built elements
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* series list. Weeks W27–W38.
|
|
179
|
+
* display strings the accessors can derive, no built elements. A key is its
|
|
180
|
+
* entry's identity, never its place: a row's id is the series that made it
|
|
181
|
+
* and the path of keys to it (`Plan.ref("presses", "Hall 1", "H1-P03")`),
|
|
182
|
+
* which is what `links`, `popover`, `ui` and `onSelect` speak — the layout is
|
|
183
|
+
* the series list. Weeks W27–W38.
|
|
144
184
|
*/
|
|
145
185
|
export const planTargetOps = e3.input("plan_target_ops", DictType(StringType, TargetOpsRow), variant("value", new Map([
|
|
146
186
|
// ── The pinned KPI ──
|
|
147
|
-
["ontime", { hall: none, kind: variant("kpi", { name: "On-time", headline: "94.2%", pinned: true,
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
// ── Deliveries — Van 1's drops are raw,
|
|
151
|
-
// stored
|
|
152
|
-
// drops, more than its room: a `+n` holds the rest. ──
|
|
153
|
-
["van1", { hall: none, kind: variant("van", { name: "Van 1", region: "local", sub: some("drops/wk"), detail: none, lanes: [],
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
["van2", { hall: none, kind: variant("van", { name: "Van 2", region: "regional", sub: some("day · am/pm"),
|
|
187
|
+
["ontime", { ...TARGET_EMPTY, hall: none, kind: variant("kpi", { name: "On-time", headline: "94.2%", pinned: true,
|
|
188
|
+
detail: some({ height: some("150px"), axis: variant("keep", null) }) }),
|
|
189
|
+
ontime: fromW27([96.1, 96.4, 96.8, 97.0, 96.2, 95.1, 93.4, 91.0, 88.9, 91.4, 93.8, 94.2]) }],
|
|
190
|
+
// ── Deliveries — Van 1's drops are raw, drawn as resting tiles; Van 2's
|
|
191
|
+
// are tiles stored as they draw, in its AM/PM lanes. Van 1's Aug 24 cell
|
|
192
|
+
// holds two drops, more than its room: a `+n` holds the rest. ──
|
|
193
|
+
["van1", { ...TARGET_EMPTY, hall: none, kind: variant("van", { name: "Van 1", region: "local", sub: some("drops/wk"), detail: none, lanes: [] }),
|
|
194
|
+
drops: new Map([
|
|
195
|
+
["a0", { at: new Date("2026-06-29T00:00:00Z"), state: variant("confirmed", null) }],
|
|
196
|
+
["a1", { at: new Date("2026-07-06T00:00:00Z"), state: variant("confirmed", null) }],
|
|
197
|
+
["a2", { at: new Date("2026-07-13T00:00:00Z"), state: variant("proposed", variant("recommended", null)) }],
|
|
198
|
+
["a3", { at: new Date("2026-07-20T00:00:00Z"), state: variant("confirmed", null) }],
|
|
199
|
+
["a4", { at: new Date("2026-07-27T00:00:00Z"), state: variant("confirmed", null) }],
|
|
200
|
+
["a5", { at: new Date("2026-08-03T00:00:00Z"), state: variant("proposed", variant("recommended", null)) }],
|
|
201
|
+
["a6", { at: new Date("2026-08-10T00:00:00Z"), state: variant("proposed", variant("recommended", null)) }],
|
|
202
|
+
["a7", { at: new Date("2026-08-24T00:00:00Z"), state: variant("confirmed", null) }],
|
|
203
|
+
["a8", { at: new Date("2026-08-24T00:00:00Z"), state: variant("proposed", variant("recommended", null)) }],
|
|
204
|
+
]),
|
|
205
|
+
alerts: atInstants([
|
|
206
|
+
[new Date("2026-07-13T00:00:00Z"), { status: variant("warning", null), message: "capacity 90%" }],
|
|
207
|
+
[new Date("2026-08-24T00:00:00Z"), { status: variant("warning", null), message: "capacity breach — 2 drops" }],
|
|
208
|
+
]) }],
|
|
209
|
+
["van2", { ...TARGET_EMPTY, hall: none, kind: variant("van", { name: "Van 2", region: "regional", sub: some("day · am/pm"),
|
|
171
210
|
detail: some({ height: some("112px"), axis: variant("keep", null) }),
|
|
172
|
-
lanes: [{ key: "am", label: some("AM") }, { key: "pm", label: some("PM") }],
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
{ key: "m4", at: variant("time", new Date("2026-07-13T00:00:00Z")), lane: none, label: some("MIXED"), icon: none, state: variant("confirmed", null),
|
|
211
|
+
lanes: [{ key: "am", label: some("AM") }, { key: "pm", label: some("PM") }] }),
|
|
212
|
+
tiles: new Map([
|
|
213
|
+
["m1", { at: new Date("2026-06-29T00:00:00Z"), lane: some("am"), label: none, icon: none, state: variant("confirmed", null),
|
|
214
|
+
tone: none, color: none, colorPalette: none, stretch: none, content: none, animation: none }],
|
|
215
|
+
["m2", { at: new Date("2026-06-29T00:00:00Z"), lane: some("pm"), label: none, icon: none, state: variant("confirmed", null),
|
|
216
|
+
tone: some(variant("warning", null)), color: none, colorPalette: none, stretch: none, content: none, animation: none }],
|
|
217
|
+
["m3", { at: new Date("2026-07-06T00:00:00Z"), lane: some("am"), label: none, icon: none, state: variant("proposed", variant("recommended", null)),
|
|
218
|
+
tone: none, color: none, colorPalette: none, stretch: none, content: none, animation: some(variant("pulse", null)) }],
|
|
219
|
+
["m4", { at: new Date("2026-07-13T00:00:00Z"), lane: none, label: some("MIXED"), icon: none, state: variant("confirmed", null),
|
|
182
220
|
tone: none, color: none, colorPalette: none, stretch: some(variant("horizontal", null)),
|
|
183
|
-
content: some({ horizontal: some(variant("center", null)), vertical: none }), animation: none },
|
|
184
|
-
|
|
221
|
+
content: some({ horizontal: some(variant("center", null)), vertical: none }), animation: none }],
|
|
222
|
+
["m5", { at: new Date("2026-07-20T00:00:00Z"), lane: some("pm"), label: none,
|
|
185
223
|
icon: some({ prefix: "fas", name: "truck", label: none, style: none }),
|
|
186
224
|
state: variant("proposed", variant("recommended", null)),
|
|
187
|
-
tone: none, color: none, colorPalette: none, stretch: none, content: none, animation: none },
|
|
188
|
-
|
|
189
|
-
tone: none, color: none, colorPalette: none, stretch: none, content: none, animation: none },
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
tone: none, color: some("teal.solid"), colorPalette: none, stretch: none, content: none, animation: none },
|
|
194
|
-
|
|
195
|
-
tone: none, color: none, colorPalette: some(variant("brand", null)), stretch: none, content: none, animation: none },
|
|
196
|
-
|
|
197
|
-
|
|
225
|
+
tone: none, color: none, colorPalette: none, stretch: none, content: none, animation: none }],
|
|
226
|
+
["m6", { at: new Date("2026-07-27T00:00:00Z"), lane: some("am"), label: some("PROOF"), icon: none, state: variant("estimated", null),
|
|
227
|
+
tone: none, color: none, colorPalette: none, stretch: none, content: none, animation: none }],
|
|
228
|
+
// The colour channels: `color` is a raw token override,
|
|
229
|
+
// `colorPalette` recolours the whole lifecycle treatment.
|
|
230
|
+
["m7", { at: new Date("2026-08-03T00:00:00Z"), lane: some("am"), label: some("S-A"), icon: none, state: variant("confirmed", null),
|
|
231
|
+
tone: none, color: some("teal.solid"), colorPalette: none, stretch: none, content: none, animation: none }],
|
|
232
|
+
["m8", { at: new Date("2026-08-03T00:00:00Z"), lane: some("pm"), label: some("S-B"), icon: none, state: variant("confirmed", null),
|
|
233
|
+
tone: none, color: none, colorPalette: some(variant("brand", null)), stretch: none, content: none, animation: none }],
|
|
234
|
+
]),
|
|
235
|
+
alerts: atInstants([[new Date("2026-07-13T00:00:00Z"), { status: variant("danger", null), message: "capacity breach" }]]) }],
|
|
198
236
|
// ── The halls' presses and loads: each names its hall, and the data
|
|
199
237
|
// step groups them under it. ──
|
|
200
|
-
["H1-P03", { hall: some("Hall 1"), kind: variant("press", { series: "presses", rate: some(12.0), sub: none,
|
|
201
|
-
status: some(variant("success", null)), detail: none,
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
["H1-P04", { hall: some("Hall 1"), kind: variant("press", { series: "presses", rate: some(12.0), sub: none, status: none,
|
|
238
|
+
["H1-P03", { ...TARGET_EMPTY, hall: some("Hall 1"), kind: variant("press", { series: "presses", rate: some(12.0), sub: none,
|
|
239
|
+
status: some(variant("success", null)), detail: none }),
|
|
240
|
+
jobs: new Map([
|
|
241
|
+
["mr", { phase: "MAKEREADY", ticket: none, start: new Date("2026-06-29T00:00:00Z"), end: new Date("2026-07-06T00:00:00Z"), sheets: none, state: variant("actual", null), alert: none }],
|
|
242
|
+
["j4642", { phase: "RUN", ticket: some("J-4642"), start: new Date("2026-07-06T00:00:00Z"), end: new Date("2026-07-27T00:00:00Z"), sheets: some(96.0), state: variant("in-progress", null), alert: none }],
|
|
243
|
+
["wash", { phase: "WASH-UP", ticket: none, start: new Date("2026-07-27T00:00:00Z"), end: new Date("2026-08-03T00:00:00Z"), sheets: none, state: variant("confirmed", null), alert: none }],
|
|
244
|
+
["j4663", { phase: "RUN", ticket: some("J-4663"), start: new Date("2026-08-03T00:00:00Z"), end: new Date("2026-08-24T00:00:00Z"), sheets: some(88.0), state: variant("proposed", variant("recommended", null)), alert: none }],
|
|
245
|
+
]),
|
|
246
|
+
decisions: new Map([["d1", { at: new Date("2026-08-03T00:00:00Z"), applied: false }]]),
|
|
247
|
+
ports: atInstants([[new Date("2026-07-27T00:00:00Z"), { label: "−24 k sheets" }]]) }],
|
|
248
|
+
["H1-P04", { ...TARGET_EMPTY, hall: some("Hall 1"), kind: variant("press", { series: "presses", rate: some(12.0), sub: none, status: none,
|
|
212
249
|
// The expand declaration — a stored plain-data record; presence is a
|
|
213
250
|
// per-row fact, and the ROOT's expandRender mounts the body.
|
|
214
|
-
detail: some({ height: some("152px"), axis: variant("keep", null) }),
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
],
|
|
228
|
-
decisions: [], ports: [], presses: new Map() }) }],
|
|
251
|
+
detail: some({ height: some("152px"), axis: variant("keep", null) }) }),
|
|
252
|
+
jobs: new Map([
|
|
253
|
+
["j4624", { phase: "RUN", ticket: some("J-4624"), start: new Date("2026-06-29T00:00:00Z"), end: new Date("2026-07-20T00:00:00Z"), sheets: some(112.0), state: variant("actual", null), alert: none }],
|
|
254
|
+
["plates", { phase: "PLATES", ticket: none, start: new Date("2026-07-20T00:00:00Z"), end: new Date("2026-07-27T00:00:00Z"), sheets: none, state: variant("confirmed", null), alert: none }],
|
|
255
|
+
["proof", { phase: "PROOF", ticket: none, start: new Date("2026-07-27T00:00:00Z"), end: new Date("2026-08-10T00:00:00Z"), sheets: none, state: variant("confirmed", null), alert: none }],
|
|
256
|
+
["j4693", { phase: "RUN", ticket: some("J-4693"), start: new Date("2026-08-10T00:00:00Z"), end: new Date("2026-10-05T00:00:00Z"), sheets: some(104.0), state: variant("proposed", variant("recommended", null)), alert: none }],
|
|
257
|
+
]) }],
|
|
258
|
+
["H1-P05", { ...TARGET_EMPTY, hall: some("Hall 1"), kind: variant("press", { series: "presses", rate: some(8.0), sub: none,
|
|
259
|
+
status: some(variant("warning", null)), detail: none }),
|
|
260
|
+
jobs: new Map([
|
|
261
|
+
["j4591", { phase: "PLATES", ticket: some("J-4591"), start: new Date("2026-06-29T00:00:00Z"), end: new Date("2026-07-27T00:00:00Z"), sheets: none, state: variant("actual", null), alert: some(variant("warning", null)) }],
|
|
262
|
+
["wash", { phase: "WASH-UP", ticket: none, start: new Date("2026-08-17T00:00:00Z"), end: new Date("2026-08-31T00:00:00Z"), sheets: none, state: variant("proposed", variant("recommended", null)), alert: none }],
|
|
263
|
+
]) }],
|
|
229
264
|
// A load reading nothing in the week of 17 August: its cell is no data.
|
|
230
|
-
["h1-load", { hall: some("Hall 1"), kind: variant("load", { name: "H1 load", sub: "%/wk",
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
["H2-P11", { hall: some("Hall 2"), kind: variant("press", { series: "presses", rate: some(10.0), sub: none, status: none, detail: none,
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
["
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
["H3-P22", { hall: some("Hall 3"), kind: variant("press", { series: "presses", rate: some(6.0), sub: none, status: none, detail: none,
|
|
246
|
-
jobs: [{ key: "j4906", phase: "RUN", ticket: some("J-4906"), start: new Date("2026-08-10T00:00:00Z"), end: new Date("2026-09-07T00:00:00Z"), sheets: some(54.0), state: variant("proposed", variant("recommended", null)), alert: none }],
|
|
247
|
-
decisions: [], ports: [], presses: new Map() }) }],
|
|
248
|
-
["h3-load", { hall: some("Hall 3"), kind: variant("load", { name: "H3 load", sub: "%/wk", from: new Date("2026-06-29T00:00:00Z"),
|
|
249
|
-
weekly: [some(38.0), some(42.0), some(47.0), some(51.0), some(55.0), some(61.0), some(66.0), some(70.0), some(75.0), some(80.0), some(83.0), some(78.0)],
|
|
250
|
-
detail: none }) }],
|
|
265
|
+
["h1-load", { ...TARGET_EMPTY, hall: some("Hall 1"), kind: variant("load", { name: "H1 load", sub: "%/wk",
|
|
266
|
+
detail: some({ height: some("132px"), axis: variant("keep", null) }) }),
|
|
267
|
+
load: fromW27([some(46.0), some(58.0), some(66.0), some(72.0), some(84.0), some(90.0), some(96.0), none, some(92.0), some(88.0), some(81.0), some(77.0)]) }],
|
|
268
|
+
["H2-P11", { ...TARGET_EMPTY, hall: some("Hall 2"), kind: variant("press", { series: "presses", rate: some(10.0), sub: none, status: none, detail: none }),
|
|
269
|
+
jobs: new Map([["j4723", { phase: "RUN", ticket: some("J-4723"), start: new Date("2026-07-13T00:00:00Z"), end: new Date("2026-08-10T00:00:00Z"), sheets: some(92.0), state: variant("confirmed", null), alert: none }]]) }],
|
|
270
|
+
["H2-P12", { ...TARGET_EMPTY, hall: some("Hall 2"), kind: variant("press", { series: "presses", rate: some(10.0), sub: none, status: none, detail: none }),
|
|
271
|
+
jobs: new Map([["j4594", { phase: "RUN", ticket: some("J-4594"), start: new Date("2026-07-20T00:00:00Z"), end: new Date("2026-08-17T00:00:00Z"), sheets: some(64.0), state: variant("proposed", variant("recommended", null)), alert: none }]]) }],
|
|
272
|
+
["h2-load", { ...TARGET_EMPTY, hall: some("Hall 2"), kind: variant("load", { name: "H2 load", sub: "%/wk", detail: none }),
|
|
273
|
+
load: fromW27([some(46.0), some(52.0), some(58.0), some(61.0), some(66.0), some(72.0), some(78.0), some(84.0), some(90.0), some(96.0), some(98.0), some(92.0)]) }],
|
|
274
|
+
["H3-P21", { ...TARGET_EMPTY, hall: some("Hall 3"), kind: variant("press", { series: "presses", rate: some(6.0), sub: none, status: none, detail: none }),
|
|
275
|
+
jobs: new Map([["j4903", { phase: "RUN", ticket: some("J-4903"), start: new Date("2026-07-27T00:00:00Z"), end: new Date("2026-08-24T00:00:00Z"), sheets: some(48.0), state: variant("confirmed", null), alert: none }]]) }],
|
|
276
|
+
["H3-P22", { ...TARGET_EMPTY, hall: some("Hall 3"), kind: variant("press", { series: "presses", rate: some(6.0), sub: none, status: none, detail: none }),
|
|
277
|
+
jobs: new Map([["j4906", { phase: "RUN", ticket: some("J-4906"), start: new Date("2026-08-10T00:00:00Z"), end: new Date("2026-09-07T00:00:00Z"), sheets: some(54.0), state: variant("proposed", variant("recommended", null)), alert: none }]]) }],
|
|
278
|
+
["h3-load", { ...TARGET_EMPTY, hall: some("Hall 3"), kind: variant("load", { name: "H3 load", sub: "%/wk", detail: none }),
|
|
279
|
+
load: fromW27([some(38.0), some(42.0), some(47.0), some(51.0), some(55.0), some(61.0), some(66.0), some(70.0), some(75.0), some(80.0), some(83.0), some(78.0)]) }],
|
|
251
280
|
// ── The span grammar: proposal flavours (a forecast ghost, a proposed
|
|
252
281
|
// cut, a declined run) ──
|
|
253
|
-
["H1-P07", { hall: none, kind: variant("press", { series: "flavours", rate: some(8.0), sub: none, status: none, detail: none,
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
decisions: [], ports: [], presses: new Map() }) }],
|
|
282
|
+
["H1-P07", { ...TARGET_EMPTY, hall: none, kind: variant("press", { series: "flavours", rate: some(8.0), sub: none, status: none, detail: none }),
|
|
283
|
+
jobs: new Map([
|
|
284
|
+
["run", { phase: "RUN", ticket: some("J-4580"), start: new Date("2026-06-29T00:00:00Z"), end: new Date("2026-07-20T00:00:00Z"), sheets: some(64.0), state: variant("in-progress", null), alert: none }],
|
|
285
|
+
["gho", { phase: "FORECAST", ticket: none, start: new Date("2026-07-20T00:00:00Z"), end: new Date("2026-08-03T00:00:00Z"), sheets: none, state: variant("estimated", null), alert: none }],
|
|
286
|
+
["rem", { phase: "CUT", ticket: none, start: new Date("2026-08-10T00:00:00Z"), end: new Date("2026-08-24T00:00:00Z"), sheets: none, state: variant("proposed", variant("removed", null)), alert: none }],
|
|
287
|
+
["rej", { phase: "DECLINED", ticket: none, start: new Date("2026-08-31T00:00:00Z"), end: new Date("2026-09-14T00:00:00Z"), sheets: none, state: variant("rejected", null), alert: none }],
|
|
288
|
+
]) }],
|
|
261
289
|
// …sheets, an applied decision and a port on a two-line gutter, its
|
|
262
290
|
// expand declaration dimming the axis behind the render…
|
|
263
|
-
["H1-P09", { hall: none, kind: variant("press", { series: "detail", rate: none, sub: some("12k sheets/h"), status: none,
|
|
264
|
-
detail: some({ height: some("152px"), axis: variant("dim", null) }),
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
presses: new Map() }) }],
|
|
291
|
+
["H1-P09", { ...TARGET_EMPTY, hall: none, kind: variant("press", { series: "detail", rate: none, sub: some("12k sheets/h"), status: none,
|
|
292
|
+
detail: some({ height: some("152px"), axis: variant("dim", null) }) }),
|
|
293
|
+
jobs: new Map([
|
|
294
|
+
["a", { phase: "RUN", ticket: some("J-4624"), start: new Date("2026-06-29T00:00:00Z"), end: new Date("2026-07-27T00:00:00Z"), sheets: some(112.0), state: variant("actual", null), alert: none }],
|
|
295
|
+
["b", { phase: "RUN", ticket: some("J-4693"), start: new Date("2026-07-27T00:00:00Z"), end: new Date("2026-08-17T00:00:00Z"), sheets: some(104.0), state: variant("proposed", variant("recommended", null)), alert: none }],
|
|
296
|
+
]),
|
|
297
|
+
decisions: new Map([["d1", { at: new Date("2026-07-27T00:00:00Z"), applied: true }]]),
|
|
298
|
+
ports: atInstants([[new Date("2026-07-27T00:00:00Z"), { label: "−24 k sheets" }]]) }],
|
|
272
299
|
// …a contract and the presses it books — its row rolls their runs up
|
|
273
300
|
// into union bands (renderer-derived)…
|
|
274
|
-
["Contract A", { hall: none, kind: variant("press", { series: "rollup", rate: none, sub: none, status: none, detail: none,
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
jobs: [{ key: "j4723", phase: "RUN", ticket: some("J-4723"), start: new Date("2026-07-13T00:00:00Z"), end: new Date("2026-08-10T00:00:00Z"), sheets: some(92.0), state: variant("confirmed", null), alert: none }],
|
|
285
|
-
decisions: [], ports: [], presses: new Map() }) }],
|
|
286
|
-
]) }) }],
|
|
301
|
+
["Contract A", { ...TARGET_EMPTY, hall: none, kind: variant("press", { series: "rollup", rate: none, sub: none, status: none, detail: none }),
|
|
302
|
+
presses: new Map([
|
|
303
|
+
["H1-P03", { ...TARGET_EMPTY, hall: none, kind: variant("press", { series: "rollup", rate: none, sub: none, status: none, detail: none }),
|
|
304
|
+
jobs: new Map([
|
|
305
|
+
["j4642", { phase: "RUN", ticket: some("J-4642"), start: new Date("2026-07-06T00:00:00Z"), end: new Date("2026-07-27T00:00:00Z"), sheets: some(96.0), state: variant("actual", null), alert: none }],
|
|
306
|
+
["j4663", { phase: "RUN", ticket: some("J-4663"), start: new Date("2026-08-03T00:00:00Z"), end: new Date("2026-08-24T00:00:00Z"), sheets: some(88.0), state: variant("proposed", variant("recommended", null)), alert: none }],
|
|
307
|
+
]) }],
|
|
308
|
+
["H2-P11", { ...TARGET_EMPTY, hall: none, kind: variant("press", { series: "rollup", rate: none, sub: none, status: none, detail: none }),
|
|
309
|
+
jobs: new Map([["j4723", { phase: "RUN", ticket: some("J-4723"), start: new Date("2026-07-13T00:00:00Z"), end: new Date("2026-08-10T00:00:00Z"), sheets: some(92.0), state: variant("confirmed", null), alert: none }]]) }],
|
|
310
|
+
]) }],
|
|
287
311
|
// …a linked delivery whose run starts BEYOND the window — in links focus
|
|
288
312
|
// its landing is a slot on the plot's edge…
|
|
289
|
-
["dlv", { hall: none, kind: variant("press", { series: "delivery", rate: none, sub: none, status: none, detail: none,
|
|
290
|
-
|
|
291
|
-
decisions: [], ports: [], presses: new Map() }) }],
|
|
313
|
+
["dlv", { ...TARGET_EMPTY, hall: none, kind: variant("press", { series: "delivery", rate: none, sub: none, status: none, detail: none }),
|
|
314
|
+
jobs: new Map([["d1", { phase: "DELIVER", ticket: none, start: new Date("2026-09-21T00:00:00Z"), end: new Date("2026-10-12T00:00:00Z"), sheets: some(91.0), state: variant("proposed", variant("recommended", null)), alert: none }]]) }],
|
|
292
315
|
// …and a contract rolled up by status.
|
|
293
|
-
["Contract B", { hall: none, kind: variant("press", { series: "contracts", rate: none, sub: none, status: none, detail: none,
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
decisions: [], ports: [], presses: new Map() }) }],
|
|
302
|
-
]) }) }],
|
|
316
|
+
["Contract B", { ...TARGET_EMPTY, hall: none, kind: variant("press", { series: "contracts", rate: none, sub: none, status: none, detail: none }),
|
|
317
|
+
presses: new Map([
|
|
318
|
+
["H2-P12", { ...TARGET_EMPTY, hall: none, kind: variant("press", { series: "contracts", rate: none, sub: none, status: none, detail: none }),
|
|
319
|
+
jobs: new Map([
|
|
320
|
+
["r1", { phase: "RUN", ticket: some("J-4594"), start: new Date("2026-07-06T00:00:00Z"), end: new Date("2026-08-03T00:00:00Z"), sheets: some(64.0), state: variant("actual", null), alert: none }],
|
|
321
|
+
["r2", { phase: "RUN", ticket: some("J-4606"), start: new Date("2026-07-20T00:00:00Z"), end: new Date("2026-08-17T00:00:00Z"), sheets: some(40.0), state: variant("proposed", variant("recommended", null)), alert: none }],
|
|
322
|
+
]) }],
|
|
323
|
+
]) }],
|
|
303
324
|
// ── A second heat series of the same kind, its own entry ──
|
|
304
|
-
["quality", { hall: none, kind: variant("load", { name: "Quality index", sub: "%",
|
|
305
|
-
|
|
306
|
-
detail: none }) }],
|
|
325
|
+
["quality", { ...TARGET_EMPTY, hall: none, kind: variant("load", { name: "Quality index", sub: "%", detail: none }),
|
|
326
|
+
load: fromW27([some(94.0), some(95.0), some(96.0), some(95.0), some(97.0), some(96.0), some(93.0), some(92.0), some(94.0), some(95.0), some(96.0), some(97.0)]) }],
|
|
307
327
|
// ── Crews — Crew A's chips derive from raw shifts; the relief pool's are
|
|
308
|
-
//
|
|
309
|
-
["crewA", { hall: none, kind: variant("crew", { name: "Crew A", pool: false, sub: some("152h → 168h"), value: none,
|
|
310
|
-
detail: some({ height: some("120px"), axis: variant("keep", null) }),
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
] }) }],
|
|
328
|
+
// chips stored as they draw. ──
|
|
329
|
+
["crewA", { ...TARGET_EMPTY, hall: none, kind: variant("crew", { name: "Crew A", pool: false, sub: some("152h → 168h"), value: none,
|
|
330
|
+
detail: some({ height: some("120px"), axis: variant("keep", null) }) }),
|
|
331
|
+
shifts: new Map([
|
|
332
|
+
["s1", { from: new Date("2026-06-29T00:00:00Z"), to: new Date("2026-07-13T00:00:00Z"), hours: 80.0, state: variant("confirmed", null) }],
|
|
333
|
+
["s2", { from: new Date("2026-07-13T00:00:00Z"), to: new Date("2026-07-27T00:00:00Z"), hours: 56.0, state: variant("proposed", variant("removed", null)) }],
|
|
334
|
+
["s3", { from: new Date("2026-07-27T00:00:00Z"), to: new Date("2026-08-10T00:00:00Z"), hours: 64.0, state: variant("proposed", variant("recommended", null)) }],
|
|
335
|
+
["s4", { from: new Date("2026-08-17T00:00:00Z"), to: new Date("2026-08-24T00:00:00Z"), hours: 48.0, state: variant("estimated", null) }],
|
|
336
|
+
["s5", { from: new Date("2026-08-31T00:00:00Z"), to: new Date("2026-09-14T00:00:00Z"), hours: 56.0, state: variant("proposed", variant("recommended", null)) }],
|
|
337
|
+
]) }],
|
|
338
|
+
["crewB", { ...TARGET_EMPTY, hall: none, kind: variant("crew", { name: "Crew B", pool: true, sub: none, value: some("128h"), detail: none }),
|
|
339
|
+
chips: new Map([
|
|
340
|
+
["b1", { from: new Date("2026-07-06T00:00:00Z"), to: new Date("2026-07-27T00:00:00Z"), label: "96h", state: variant("confirmed", null),
|
|
341
|
+
icon: some({ prefix: "fas", name: "user-group", label: none, style: none }) }],
|
|
342
|
+
["b2", { from: new Date("2026-08-10T00:00:00Z"), to: new Date("2026-08-31T00:00:00Z"), label: "+32h", state: variant("proposed", variant("recommended", null)), icon: none }],
|
|
343
|
+
]) }],
|
|
325
344
|
// ── Instant marks — every kind: milestones, decisions applied and
|
|
326
345
|
// pending, an exception, a custom icon; the releases on a cadence. ──
|
|
327
|
-
["milestones", { hall: none, kind: variant("stream", { name: "Milestones", cadence: none,
|
|
328
|
-
detail: some({ height: some("112px"), axis: variant("dim", null) }),
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
icon: some({ prefix: "fas", name: "rocket", label: none, style: none }), label: some("REL 2.4") },
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
["releases", { hall: none, kind: variant("stream", { name: "Releases", cadence: some("6-wk cadence"), detail: none,
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
346
|
+
["milestones", { ...TARGET_EMPTY, hall: none, kind: variant("stream", { name: "Milestones", cadence: none,
|
|
347
|
+
detail: some({ height: some("112px"), axis: variant("dim", null) }) }),
|
|
348
|
+
marks: new Map([
|
|
349
|
+
["kick", { at: new Date("2026-07-06T00:00:00Z"), kind: variant("milestone", null), icon: none, label: some("KICKOFF") }],
|
|
350
|
+
["d1", { at: new Date("2026-07-27T00:00:00Z"), kind: variant("decision", { applied: true }), icon: none, label: none }],
|
|
351
|
+
["rel", { at: new Date("2026-08-10T00:00:00Z"), kind: variant("milestone", null),
|
|
352
|
+
icon: some({ prefix: "fas", name: "rocket", label: none, style: none }), label: some("REL 2.4") }],
|
|
353
|
+
["audit", { at: new Date("2026-08-24T00:00:00Z"), kind: variant("exception", null), icon: none, label: some("AUDIT") }],
|
|
354
|
+
["d2", { at: new Date("2026-09-07T00:00:00Z"), kind: variant("decision", { applied: false }), icon: none, label: some("×3") }],
|
|
355
|
+
]) }],
|
|
356
|
+
["releases", { ...TARGET_EMPTY, hall: none, kind: variant("stream", { name: "Releases", cadence: some("6-wk cadence"), detail: none }),
|
|
357
|
+
marks: new Map([
|
|
358
|
+
["r1", { at: new Date("2026-07-13T00:00:00Z"), kind: variant("milestone", null), icon: none, label: some("2.3") }],
|
|
359
|
+
["r2", { at: new Date("2026-08-31T00:00:00Z"), kind: variant("milestone", null), icon: none, label: some("2.4") }],
|
|
360
|
+
]) }],
|
|
342
361
|
// ── Delivered sheets, a week apiece — negatives, and a week with none;
|
|
343
362
|
// its render draws its own canvas, so the shared lines leave its row. ──
|
|
344
|
-
["delivered", { hall: none, kind: variant("tally", { name: "Delivered · k",
|
|
345
|
-
detail: some({ height: some("120px"), axis: variant("off", null) }),
|
|
346
|
-
|
|
347
|
-
{ at: variant("time", new Date("2026-06-29T00:00:00Z")), value: some(128.0), text: none, tone: none },
|
|
348
|
-
{ at: variant("time", new Date("2026-07-06T00:00:00Z")), value: some(134.0), text: none, tone: none },
|
|
349
|
-
{ at: variant("time", new Date("2026-07-13T00:00:00Z")), value: some(119.0), text: none, tone: none },
|
|
350
|
-
{ at: variant("time", new Date("2026-07-20T00:00:00Z")), value: some(-96.0), text: none, tone: none },
|
|
351
|
-
{ at: variant("time", new Date("2026-07-27T00:00:00Z")), value: some(-88.0), text: none, tone: none },
|
|
352
|
-
{ at: variant("time", new Date("2026-08-03T00:00:00Z")), value: none, text: none, tone: none },
|
|
353
|
-
{ at: variant("time", new Date("2026-08-10T00:00:00Z")), value: some(151.0), text: none, tone: none },
|
|
354
|
-
{ at: variant("time", new Date("2026-08-17T00:00:00Z")), value: some(162.0), text: none, tone: none },
|
|
355
|
-
{ at: variant("time", new Date("2026-08-24T00:00:00Z")), value: some(144.0), text: none, tone: none },
|
|
356
|
-
] }) }],
|
|
363
|
+
["delivered", { ...TARGET_EMPTY, hall: none, kind: variant("tally", { name: "Delivered · k",
|
|
364
|
+
detail: some({ height: some("120px"), axis: variant("off", null) }) }),
|
|
365
|
+
delivered: fromW27([some(128.0), some(134.0), some(119.0), some(-96.0), some(-88.0), none, some(151.0), some(162.0), some(144.0)]) }],
|
|
357
366
|
// ── The finishing line's machines — each one shown three ways ──
|
|
358
|
-
["FN-1", { hall: none, kind: variant("finisher", { name: "Folder FN-1",
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
jobs: [{ key: "f2", phase: "CUT", ticket: some("J-4663"), start: new Date("2026-08-24T00:00:00Z"), end: new Date("2026-09-07T00:00:00Z"), sheets: some(88.0), state: variant("proposed", variant("recommended", null)), alert: none }],
|
|
367
|
-
util: [30.0, 34.0, 38.0, 42.0, 46.0, 50.0, 54.0, 58.0, 62.0, 66.0, 70.0, 74.0],
|
|
368
|
-
sheets: [
|
|
369
|
-
{ at: variant("time", new Date("2026-08-24T00:00:00Z")), value: some(44.0), text: none, tone: none },
|
|
370
|
-
{ at: variant("time", new Date("2026-08-31T00:00:00Z")), value: some(44.0), text: none, tone: none },
|
|
371
|
-
] }) }],
|
|
367
|
+
["FN-1", { ...TARGET_EMPTY, hall: none, kind: variant("finisher", { name: "Folder FN-1" }),
|
|
368
|
+
jobs: new Map([["f1", { phase: "FOLD", ticket: some("J-4642"), start: new Date("2026-07-27T00:00:00Z"), end: new Date("2026-08-10T00:00:00Z"), sheets: some(96.0), state: variant("confirmed", null), alert: none }]]),
|
|
369
|
+
util: fromW27([46.0, 52.0, 58.0, 66.0, 72.0, 78.0, 84.0, 80.0, 74.0, 68.0, 62.0, 58.0]),
|
|
370
|
+
sheets: atInstants([[new Date("2026-07-27T00:00:00Z"), 48.0], [new Date("2026-08-03T00:00:00Z"), -8.0]]) }],
|
|
371
|
+
["FN-2", { ...TARGET_EMPTY, hall: none, kind: variant("finisher", { name: "Cutter FN-2" }),
|
|
372
|
+
jobs: new Map([["f2", { phase: "CUT", ticket: some("J-4663"), start: new Date("2026-08-24T00:00:00Z"), end: new Date("2026-09-07T00:00:00Z"), sheets: some(88.0), state: variant("proposed", variant("recommended", null)), alert: none }]]),
|
|
373
|
+
util: fromW27([30.0, 34.0, 38.0, 42.0, 46.0, 50.0, 54.0, 58.0, 62.0, 66.0, 70.0, 74.0]),
|
|
374
|
+
sheets: atInstants([[new Date("2026-08-24T00:00:00Z"), 44.0], [new Date("2026-08-31T00:00:00Z"), 44.0]]) }],
|
|
372
375
|
])));
|
|
373
376
|
/**
|
|
374
377
|
* The data flagship: every row kind on one axis, from ONE raw ops source bound
|
|
@@ -423,7 +426,7 @@ export const planTargetState = example({
|
|
|
423
426
|
"span", "run", "group", "groups", "strip", "section", "heat", "buckets", "cards", "chip", "events", "mark", "chart",
|
|
424
427
|
"table", "views", "rows", "layers", "rollup", "union", "byStatus", "bands", "footer", "milestone",
|
|
425
428
|
"decision", "exception", "pinned", "port", "hovercard", "popover", "hover",
|
|
426
|
-
"slice", "Slice.bindRecords", "source", "Data.bindPaged", "
|
|
429
|
+
"slice", "Slice.bindRecords", "source", "Data.bindPaged", "indexes", "presets", "brush", "horizon", "toolbar", "affordances", "summary", "#1344",
|
|
427
430
|
"expand", "expandRender", "expandGutter", "keep", "dim", "off", "collapse", "context strip",
|
|
428
431
|
"resolver", "data-driven", "accessor", "raw", "target state", "flagship",
|
|
429
432
|
"layout", "row id", "Plan.ref", "Plan.sectionRef", "links", "link", "focus", "quantity", "Plan.quantity", "unit",
|
|
@@ -464,66 +467,57 @@ export const planTargetState = example({
|
|
|
464
467
|
}));
|
|
465
468
|
// ── The data step ──────────────────────────────────────────
|
|
466
469
|
// Hierarchy is the DATA's (#822): one `groupToDicts` makes each
|
|
467
|
-
// hall an entry holding the rows that stand in it
|
|
468
|
-
// source keys them
|
|
470
|
+
// hall an entry holding the rows that stand in it — its
|
|
471
|
+
// `members`, keyed as the source keys them — and the halls join
|
|
472
|
+
// the rest of the source.
|
|
469
473
|
const raw = $.let(ops.read());
|
|
470
474
|
const halls = $.let(raw.filter((_$2, r) => r.unwrap().hall.hasTag("some"))
|
|
471
475
|
.groupToDicts((_$2, r) => r.unwrap().hall.unwrap("some"), (_$2, _r, k) => k));
|
|
472
476
|
const rows = $.let(raw.filter((_$2, r) => r.unwrap().hall.hasTag("none"))
|
|
473
|
-
.union(halls.map((_$2, members) => East.value({ hall: none, kind: variant("hall",
|
|
474
|
-
// ── What the
|
|
475
|
-
|
|
476
|
-
//
|
|
477
|
-
//
|
|
478
|
-
//
|
|
479
|
-
const weeklyPoints = $.const(East.function([DateTimeType, ArrayType(FloatType)], ArrayType(MeasureRow), (_$2, from, weekly) => East.Array.generate(weekly.size(), MeasureRow, (_$3, i) => ({ week: from.addWeeks(i), pct: weekly.get(i) }))));
|
|
480
|
-
const weeklyCells = $.const(East.function([DateTimeType, ArrayType(OptionType(FloatType))], ArrayType(Plan.Types.HeatCell), (_$2, from, weekly) => East.Array.generate(weekly.size(), Plan.Types.HeatCell, ($3, i) => {
|
|
481
|
-
const value = $3.let(weekly.get(i), OptionType(FloatType));
|
|
482
|
-
const label = $3.let(none, OptionType(StringType));
|
|
483
|
-
$3.match(value, { some: ($4, v) => { $4.assign(label, some(East.Float.printFixed(v, 0n))); } });
|
|
484
|
-
return { at: Plan.at.time(from.addWeeks(i)), value, label };
|
|
485
|
-
})));
|
|
477
|
+
.union(halls.map((_$2, members) => East.value({ ...TARGET_EMPTY, hall: none, kind: variant("hall", null), members }, TargetOpsRow))));
|
|
478
|
+
// ── What the reads share ───────────────────────────────────
|
|
479
|
+
// Each series reads its row's collections BY NAME (#1436): the
|
|
480
|
+
// jobs as runs (`Plan.items`, an id and the times named), a week's
|
|
481
|
+
// values as cells and points (`Plan.values`, each at its week).
|
|
482
|
+
// `draw` builds only how an element looks.
|
|
486
483
|
// A press's rate, a thousand sheets an hour, as its gutter value.
|
|
487
484
|
const rateText = $.const(East.function([OptionType(FloatType)], OptionType(StringType), (_$2, rate) => rate.match({
|
|
488
485
|
some: (_$3, k) => East.value(some(East.str `${East.Float.printFixed(k, 0n)}k/h`), OptionType(StringType)),
|
|
489
486
|
none: (_$3) => East.value(none, OptionType(StringType)),
|
|
490
487
|
})));
|
|
491
|
-
//
|
|
492
|
-
//
|
|
493
|
-
//
|
|
494
|
-
//
|
|
495
|
-
// disagree. The run is written as a RECORD, so its instants are
|
|
496
|
-
// spelled with `Plan.at.time` — the `Plan.run` builder would wrap a
|
|
497
|
-
// DateTime field by itself.
|
|
488
|
+
// A job's look: the bar label and the quantity derive CLIENT-SIDE
|
|
489
|
+
// from its raw phase, ticket and sheets. A quantity is ONE value —
|
|
490
|
+
// the number, its unit and how it prints (#824) — so the bar's
|
|
491
|
+
// caption and a rollup's sum can never disagree.
|
|
498
492
|
const sheetsFormat = $.const(Format.Number({ maximumFractionDigits: 0n }));
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
const run = $3.let({
|
|
510
|
-
key: j.key, start: Plan.at.time(j.start), end: Plan.at.time(j.end), label,
|
|
511
|
-
quantity, state: j.state, status: j.alert,
|
|
512
|
-
moved: none, icon: none,
|
|
513
|
-
}, Plan.Types.Run);
|
|
514
|
-
return run;
|
|
515
|
-
})));
|
|
516
|
-
// Raw shifts → chips: hours print as the chip label, an ADDED
|
|
493
|
+
const jobLook = (job) => ({
|
|
494
|
+
label: job.ticket.match({ some: (_$2, t) => East.str `${job.phase} · ${t}`, none: () => job.phase }),
|
|
495
|
+
state: job.state,
|
|
496
|
+
quantity: job.sheets.match({
|
|
497
|
+
some: (_$2, t) => East.value(some(Plan.quantity(t, { unit: "k sheets", format: sheetsFormat })), OptionType(Plan.Types.Quantity)),
|
|
498
|
+
none: () => East.value(none, OptionType(Plan.Types.Quantity)),
|
|
499
|
+
}),
|
|
500
|
+
status: job.alert,
|
|
501
|
+
});
|
|
502
|
+
// A shift's look: its hours print as the chip label, an ADDED
|
|
517
503
|
// proposal wearing the `+` prefix — display derives from the
|
|
518
504
|
// lifecycle, down to the proposal's flavour (a `removed` shift is
|
|
519
505
|
// a proposal too, and `+` would read as its opposite).
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
506
|
+
const shiftLook = (shift) => ({
|
|
507
|
+
label: shift.state.match({
|
|
508
|
+
proposed: (_$2, p) => p.hasTag("removed").ifElse(() => East.str `${East.Float.printFixed(shift.hours, 0n)}h`, () => East.str `+${East.Float.printFixed(shift.hours, 0n)}h`),
|
|
509
|
+
}, () => East.str `${East.Float.printFixed(shift.hours, 0n)}h`),
|
|
510
|
+
state: shift.state,
|
|
511
|
+
});
|
|
512
|
+
// A week's load: its value, printed as the cell's label — a week
|
|
513
|
+
// reading none is the no-data cell.
|
|
514
|
+
const loadLook = (pct) => ({
|
|
515
|
+
value: pct,
|
|
516
|
+
label: pct.match({
|
|
517
|
+
some: (_$2, v) => East.value(some(East.Float.printFixed(v, 0n)), OptionType(StringType)),
|
|
518
|
+
none: () => East.value(none, OptionType(StringType)),
|
|
519
|
+
}),
|
|
520
|
+
});
|
|
527
521
|
const whole = $.const(Format.Number({ maximumFractionDigits: 0n }));
|
|
528
522
|
// ── The series — the list IS the layout (#822): one block per
|
|
529
523
|
// series, top to bottom, a section titling the blocks beneath
|
|
@@ -543,24 +537,24 @@ export const planTargetState = example({
|
|
|
543
537
|
expand: r => r.kind.unwrap("kpi").detail,
|
|
544
538
|
height: "spark", expandable: true,
|
|
545
539
|
left: { domain: [80, 110], tickValues: [80, 100] },
|
|
546
|
-
layers:
|
|
547
|
-
Plan.
|
|
540
|
+
layers: [
|
|
541
|
+
Plan.line(Plan.values("ontime", (pct) => ({ y: pct })), { breach: { below: 92 } }),
|
|
548
542
|
Chart.refLine({ y: 100, label: "TARGET 100" }),
|
|
549
543
|
],
|
|
550
544
|
}),
|
|
551
545
|
Plan.series.section(TargetOpsRow, { key: "vans-local", title: "Deliveries · Local", meta: "1 row", value: "9 drops", status: "warning" }, [
|
|
552
|
-
// Raw drops
|
|
546
|
+
// Raw drops, each a resting tile at its week.
|
|
553
547
|
Plan.series.buckets(TargetOpsRow, {
|
|
554
548
|
key: "vans", title: "Vans",
|
|
555
549
|
match: r => r.kind.hasTag("van").and(() => r.kind.unwrap("van").region.equal("local")),
|
|
556
550
|
label: r => r.kind.unwrap("van").name,
|
|
557
551
|
sub: r => r.kind.unwrap("van").sub,
|
|
558
|
-
events:
|
|
559
|
-
markers:
|
|
552
|
+
events: Plan.items("drops", { at: "at" }, (drop) => ({ state: drop.state })),
|
|
553
|
+
markers: Plan.values("alerts", (alert) => ({ message: alert.message, status: alert.status })),
|
|
560
554
|
}),
|
|
561
555
|
]),
|
|
562
556
|
Plan.series.section(TargetOpsRow, { key: "vans-regional", title: "Deliveries · Regional", meta: "1 row" }, [
|
|
563
|
-
//
|
|
557
|
+
// Tiles stored as they draw pass straight through, in their lanes.
|
|
564
558
|
Plan.series.buckets(TargetOpsRow, {
|
|
565
559
|
key: "regional", title: "Regional",
|
|
566
560
|
match: r => r.kind.hasTag("van").and(() => r.kind.unwrap("van").region.equal("regional")),
|
|
@@ -568,13 +562,16 @@ export const planTargetState = example({
|
|
|
568
562
|
sub: r => r.kind.unwrap("van").sub,
|
|
569
563
|
expand: r => r.kind.unwrap("van").detail,
|
|
570
564
|
lanes: r => r.kind.unwrap("van").lanes,
|
|
571
|
-
events:
|
|
572
|
-
|
|
565
|
+
events: Plan.items("tiles", { at: "at" }, (tile) => ({
|
|
566
|
+
state: tile.state, lane: tile.lane, label: tile.label, icon: tile.icon, tone: tile.tone, color: tile.color,
|
|
567
|
+
colorPalette: tile.colorPalette, stretch: tile.stretch, content: tile.content, animation: tile.animation,
|
|
568
|
+
})),
|
|
569
|
+
markers: Plan.values("alerts", (alert) => ({ message: alert.message, status: alert.status })),
|
|
573
570
|
}),
|
|
574
571
|
]),
|
|
575
572
|
// One strip PER HALL — the data step's entries — its members
|
|
576
|
-
// stepped down into (`Plan.children`) and laid out like
|
|
577
|
-
// top-level list: the hall's presses, then its load. Hall 2
|
|
573
|
+
// stepped down into by field (`Plan.children`) and laid out like
|
|
574
|
+
// a top-level list: the hall's presses, then its load. Hall 2
|
|
578
575
|
// rests folded, as its DECLARED mean strip with its member
|
|
579
576
|
// count; the host's state folds Hall 3.
|
|
580
577
|
Plan.series.group(TargetOpsRow, {
|
|
@@ -583,7 +580,7 @@ export const planTargetState = example({
|
|
|
583
580
|
label: (_r, hall) => hall,
|
|
584
581
|
collapsed: (_r, hall) => hall.equal("Hall 2"),
|
|
585
582
|
summaryAggregate: "mean",
|
|
586
|
-
children: Plan.children(
|
|
583
|
+
children: Plan.children("members", [
|
|
587
584
|
Plan.series.span(TargetOpsRow, {
|
|
588
585
|
key: "presses", title: "Presses",
|
|
589
586
|
match: r => r.kind.hasTag("press"),
|
|
@@ -591,9 +588,9 @@ export const planTargetState = example({
|
|
|
591
588
|
value: r => rateText(r.kind.unwrap("press").rate),
|
|
592
589
|
status: r => r.kind.unwrap("press").status,
|
|
593
590
|
expand: r => r.kind.unwrap("press").detail,
|
|
594
|
-
runs:
|
|
595
|
-
decisions:
|
|
596
|
-
ports:
|
|
591
|
+
runs: Plan.items("jobs", { from: "start", to: "end" }, jobLook),
|
|
592
|
+
decisions: Plan.items("decisions", { at: "at" }, (decision) => ({ applied: decision.applied })),
|
|
593
|
+
ports: Plan.values("ports", (port) => ({ label: some(port.label) })),
|
|
597
594
|
}),
|
|
598
595
|
Plan.series.heat(TargetOpsRow, {
|
|
599
596
|
key: "hall-load", title: "Hall load",
|
|
@@ -601,7 +598,7 @@ export const planTargetState = example({
|
|
|
601
598
|
label: r => r.kind.unwrap("load").name,
|
|
602
599
|
sub: r => some(r.kind.unwrap("load").sub),
|
|
603
600
|
expand: r => r.kind.unwrap("load").detail,
|
|
604
|
-
cells:
|
|
601
|
+
cells: Plan.heatCells(Plan.values("load", loadLook), { min: 0, max: 100, warnAt: 95 }),
|
|
605
602
|
}),
|
|
606
603
|
]),
|
|
607
604
|
}),
|
|
@@ -612,7 +609,7 @@ export const planTargetState = example({
|
|
|
612
609
|
match: r => r.kind.hasTag("press").and(() => r.kind.unwrap("press").series.equal("flavours")),
|
|
613
610
|
label: (_r, k) => k, id: true,
|
|
614
611
|
value: r => rateText(r.kind.unwrap("press").rate),
|
|
615
|
-
runs:
|
|
612
|
+
runs: Plan.items("jobs", { from: "start", to: "end" }, jobLook),
|
|
616
613
|
}),
|
|
617
614
|
// …sheets, an applied decision and a port on a stacked two-line gutter…
|
|
618
615
|
Plan.series.span(TargetOpsRow, {
|
|
@@ -621,25 +618,26 @@ export const planTargetState = example({
|
|
|
621
618
|
label: (_r, k) => k, id: true, stacked: true,
|
|
622
619
|
sub: r => r.kind.unwrap("press").sub,
|
|
623
620
|
expand: r => r.kind.unwrap("press").detail,
|
|
624
|
-
runs:
|
|
625
|
-
decisions:
|
|
626
|
-
ports:
|
|
621
|
+
runs: Plan.items("jobs", { from: "start", to: "end" }, jobLook),
|
|
622
|
+
decisions: Plan.items("decisions", { at: "at" }, (decision) => ({ applied: decision.applied })),
|
|
623
|
+
ports: Plan.values("ports", (port) => ({ label: some(port.label) })),
|
|
627
624
|
}),
|
|
628
|
-
// …a contract's presses nest under it (`children` —
|
|
629
|
-
// this series, to any depth), and its row rolls their
|
|
625
|
+
// …a contract's presses nest under it (`children: "presses"` —
|
|
626
|
+
// more of this series, to any depth), and its row rolls their
|
|
627
|
+
// runs up…
|
|
630
628
|
Plan.series.span(TargetOpsRow, {
|
|
631
629
|
key: "rollup", title: "Rollup",
|
|
632
630
|
match: r => r.kind.hasTag("press").and(() => r.kind.unwrap("press").series.equal("rollup")),
|
|
633
631
|
label: (_r, k) => k, id: true,
|
|
634
|
-
runs:
|
|
635
|
-
children:
|
|
632
|
+
runs: Plan.items("jobs", { from: "start", to: "end" }, jobLook),
|
|
633
|
+
children: "presses", rollup: "union",
|
|
636
634
|
}),
|
|
637
635
|
// …a delivery whose run lands past the window…
|
|
638
636
|
Plan.series.span(TargetOpsRow, {
|
|
639
637
|
key: "delivery", title: "Deliveries",
|
|
640
638
|
match: r => r.kind.hasTag("press").and(() => r.kind.unwrap("press").series.equal("delivery")),
|
|
641
639
|
label: (_r, k) => k, id: true,
|
|
642
|
-
runs:
|
|
640
|
+
runs: Plan.items("jobs", { from: "start", to: "end" }, jobLook),
|
|
643
641
|
}),
|
|
644
642
|
// …and a contract rolled up by status, under a section.
|
|
645
643
|
Plan.series.section(TargetOpsRow, { key: "contract-block", title: "Contracts", meta: "1 row" }, [
|
|
@@ -647,8 +645,8 @@ export const planTargetState = example({
|
|
|
647
645
|
key: "contracts", title: "Contracts",
|
|
648
646
|
match: r => r.kind.hasTag("press").and(() => r.kind.unwrap("press").series.equal("contracts")),
|
|
649
647
|
label: (_r, k) => k, id: true,
|
|
650
|
-
runs:
|
|
651
|
-
children:
|
|
648
|
+
runs: Plan.items("jobs", { from: "start", to: "end" }, jobLook),
|
|
649
|
+
children: "presses", rollup: "byStatus",
|
|
652
650
|
}),
|
|
653
651
|
]),
|
|
654
652
|
// A section folded to start, resting as its strip — its
|
|
@@ -660,7 +658,7 @@ export const planTargetState = example({
|
|
|
660
658
|
match: r => r.kind.hasTag("load"),
|
|
661
659
|
label: r => r.kind.unwrap("load").name,
|
|
662
660
|
sub: r => some(r.kind.unwrap("load").sub),
|
|
663
|
-
cells:
|
|
661
|
+
cells: Plan.heatCells(Plan.values("load", loadLook), { min: 0, max: 100 }),
|
|
664
662
|
}),
|
|
665
663
|
]),
|
|
666
664
|
// Chips derived from raw shifts on a two-line gutter…
|
|
@@ -670,25 +668,25 @@ export const planTargetState = example({
|
|
|
670
668
|
label: r => r.kind.unwrap("crew").name, stacked: true,
|
|
671
669
|
sub: r => r.kind.unwrap("crew").sub,
|
|
672
670
|
expand: r => r.kind.unwrap("crew").detail,
|
|
673
|
-
chips:
|
|
671
|
+
chips: Plan.items("shifts", { from: "from", to: "to" }, shiftLook),
|
|
674
672
|
}),
|
|
675
|
-
// …and
|
|
673
|
+
// …and chips stored as they draw, one with an icon.
|
|
676
674
|
Plan.series.section(TargetOpsRow, { key: "relief", title: "Relief pool", meta: "1 row" }, [
|
|
677
675
|
Plan.series.cards(TargetOpsRow, {
|
|
678
676
|
key: "pool", title: "Pool",
|
|
679
677
|
match: r => r.kind.hasTag("crew").and(() => r.kind.unwrap("crew").pool),
|
|
680
678
|
label: r => r.kind.unwrap("crew").name,
|
|
681
679
|
value: r => r.kind.unwrap("crew").value,
|
|
682
|
-
chips:
|
|
680
|
+
chips: Plan.items("chips", { from: "from", to: "to" }, (chip) => ({ label: chip.label, state: chip.state, icon: chip.icon })),
|
|
683
681
|
}),
|
|
684
682
|
]),
|
|
685
683
|
Plan.series.events(TargetOpsRow, {
|
|
686
684
|
key: "milestones", title: "Milestones",
|
|
687
685
|
match: r => r.kind.hasTag("stream").and(() => r.kind.unwrap("stream").cadence.hasTag("none")),
|
|
688
686
|
label: r => r.kind.unwrap("stream").name, id: true,
|
|
689
|
-
value: r => some(East.print(r.
|
|
687
|
+
value: r => some(East.print(r.marks.size())),
|
|
690
688
|
expand: r => r.kind.unwrap("stream").detail,
|
|
691
|
-
marks:
|
|
689
|
+
marks: Plan.items("marks", { at: "at" }, (mark) => ({ kind: mark.kind, icon: mark.icon, label: mark.label })),
|
|
692
690
|
}),
|
|
693
691
|
Plan.series.section(TargetOpsRow, { key: "programs", title: "Programs", meta: "1 row" }, [
|
|
694
692
|
Plan.series.events(TargetOpsRow, {
|
|
@@ -696,7 +694,7 @@ export const planTargetState = example({
|
|
|
696
694
|
match: r => r.kind.hasTag("stream").and(() => r.kind.unwrap("stream").cadence.hasTag("some")),
|
|
697
695
|
label: r => r.kind.unwrap("stream").name, id: true, stacked: true,
|
|
698
696
|
sub: r => r.kind.unwrap("stream").cadence,
|
|
699
|
-
marks:
|
|
697
|
+
marks: Plan.items("marks", { at: "at" }, (mark) => ({ kind: mark.kind, icon: mark.icon, label: mark.label })),
|
|
700
698
|
}),
|
|
701
699
|
]),
|
|
702
700
|
Plan.series.table(TargetOpsRow, {
|
|
@@ -704,7 +702,7 @@ export const planTargetState = example({
|
|
|
704
702
|
match: r => r.kind.hasTag("tally"),
|
|
705
703
|
label: r => r.kind.unwrap("tally").name,
|
|
706
704
|
expand: r => r.kind.unwrap("tally").detail,
|
|
707
|
-
cells:
|
|
705
|
+
cells: Plan.tableCells(Plan.values("delivered", (sheets) => ({ value: sheets }))),
|
|
708
706
|
format: whole,
|
|
709
707
|
}),
|
|
710
708
|
// ONE machine, THREE views — a `views` series gives each
|
|
@@ -718,17 +716,17 @@ export const planTargetState = example({
|
|
|
718
716
|
Plan.series.span(TargetOpsRow, {
|
|
719
717
|
key: "finisher-jobs", title: "Finisher jobs",
|
|
720
718
|
label: r => r.kind.unwrap("finisher").name, id: true,
|
|
721
|
-
runs:
|
|
719
|
+
runs: Plan.items("jobs", { from: "start", to: "end" }, jobLook),
|
|
722
720
|
}),
|
|
723
721
|
Plan.series.chart(TargetOpsRow, {
|
|
724
722
|
key: "finisher-util", title: "Finisher · utilisation",
|
|
725
723
|
label: r => r.kind.unwrap("finisher").name, stacked: true, sub: _r => some("utilisation %"), height: "spark",
|
|
726
|
-
layers:
|
|
724
|
+
layers: [Plan.line(Plan.values("util", (pct) => ({ y: pct })))],
|
|
727
725
|
}),
|
|
728
726
|
Plan.series.table(TargetOpsRow, {
|
|
729
727
|
key: "finisher-sheets", title: "Finisher · sheets",
|
|
730
728
|
label: r => r.kind.unwrap("finisher").name, stacked: true, sub: _r => some("sheets · plan Δ"),
|
|
731
|
-
cells:
|
|
729
|
+
cells: Plan.tableCells(Plan.values("sheets", (sheets) => ({ value: some(sheets) }))),
|
|
732
730
|
format: whole,
|
|
733
731
|
}),
|
|
734
732
|
]),
|
|
@@ -764,7 +762,7 @@ export const planTargetState = example({
|
|
|
764
762
|
// presets narrow the deliveries and keep the window.
|
|
765
763
|
const slice = $.let(Slice.bindRecords("ex.plan.target", {
|
|
766
764
|
deliveries: {
|
|
767
|
-
source: Data.bindPaged(planTargetDeliveries, { indexes: [
|
|
765
|
+
source: Data.bindPaged(planTargetDeliveries, { indexes: ["plan_target_deliveries_by_at", "plan_target_deliveries_by_risk_at", "plan_target_deliveries_by_hall_at", "plan_target_deliveries_by_hall_risk_at"] }),
|
|
768
766
|
label: "Deliveries", icon: { prefix: "fas", name: "truck" },
|
|
769
767
|
fields: {
|
|
770
768
|
hall: { label: "Hall", hints: ["Hall 1", "Hall 2"] },
|
|
@@ -938,37 +936,43 @@ export const planTargetState = example({
|
|
|
938
936
|
// ============================================================================
|
|
939
937
|
// planVariants — THE Plan configurator (#571): axis presets × style sweeps
|
|
940
938
|
// ============================================================================
|
|
941
|
-
/** A press job, its label already composed. */
|
|
939
|
+
/** A press job, under its id — its label already composed. */
|
|
942
940
|
export const VariantsJob = StructType({
|
|
943
|
-
|
|
941
|
+
label: StringType,
|
|
944
942
|
start: DateTimeType, end: DateTimeType, state: EventStateType,
|
|
945
943
|
});
|
|
946
|
-
/**
|
|
947
|
-
export const
|
|
948
|
-
/** A
|
|
949
|
-
export const VariantsAlloc = StructType({ key: StringType, at: DateTimeType, state: EventStateType });
|
|
950
|
-
/** A crew shift, its label already composed. */
|
|
944
|
+
/** A van drop, under its id. */
|
|
945
|
+
export const VariantsAlloc = StructType({ at: DateTimeType, state: EventStateType });
|
|
946
|
+
/** A crew shift, under its id — its label already composed. */
|
|
951
947
|
export const VariantsShift = StructType({
|
|
952
|
-
|
|
948
|
+
from: DateTimeType, to: DateTimeType, label: StringType, state: EventStateType,
|
|
953
949
|
});
|
|
954
|
-
/**
|
|
955
|
-
export const
|
|
950
|
+
/** A milestone or an exception on a stream, under its id. */
|
|
951
|
+
export const VariantsMark = StructType({ at: DateTimeType, kind: Plan.Types.EventMarkKind, label: OptionType(StringType) });
|
|
956
952
|
/**
|
|
957
953
|
* ONE flat source: `series` names the series that claims the row, and every
|
|
958
|
-
*
|
|
954
|
+
* collection a row's kind does not read stays empty. The collections are Dicts
|
|
955
|
+
* each series reads by name (#1436): the things people drag by their ids, and
|
|
956
|
+
* a reading a fortnight keyed by its week.
|
|
959
957
|
*/
|
|
960
958
|
export const VariantsOpsRow = StructType({
|
|
961
959
|
series: StringType, label: StringType,
|
|
962
960
|
sub: OptionType(StringType),
|
|
963
|
-
jobs:
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
allocs:
|
|
967
|
-
act:
|
|
968
|
-
plan:
|
|
969
|
-
shifts:
|
|
970
|
-
marks:
|
|
961
|
+
jobs: DictType(StringType, VariantsJob),
|
|
962
|
+
util: DictType(DateTimeType, FloatType),
|
|
963
|
+
load: DictType(DateTimeType, FloatType),
|
|
964
|
+
allocs: DictType(StringType, VariantsAlloc),
|
|
965
|
+
act: DictType(DateTimeType, FloatType),
|
|
966
|
+
plan: DictType(DateTimeType, FloatType),
|
|
967
|
+
shifts: DictType(StringType, VariantsShift),
|
|
968
|
+
marks: DictType(StringType, VariantsMark),
|
|
971
969
|
});
|
|
970
|
+
/** Every collection a row holds, empty: a row names the few it fills. */
|
|
971
|
+
const VARIANTS_EMPTY = {
|
|
972
|
+
jobs: new Map(), util: new Map(), load: new Map(), allocs: new Map(), act: new Map(), plan: new Map(), shifts: new Map(), marks: new Map(),
|
|
973
|
+
};
|
|
974
|
+
/** A reading a fortnight from W27 (Monday 29 June 2026), each keyed by its week's instant. */
|
|
975
|
+
const fortnightsFromW27 = (readings) => atInstants(readings.map((v, i) => [new Date(Date.UTC(2026, 5, 29 + 14 * i)), v]));
|
|
972
976
|
/**
|
|
973
977
|
* Every row kind on purpose: the density sweep re-rhythms them all at once —
|
|
974
978
|
* compact tightens the shared row and the bars in it, while a two-line gutter
|
|
@@ -976,71 +980,42 @@ export const VariantsOpsRow = StructType({
|
|
|
976
980
|
* sweep re-widths every label. The readings are fortnightly from W27.
|
|
977
981
|
*/
|
|
978
982
|
export const planVariantsOps = e3.input("plan_variants_ops", DictType(StringType, VariantsOpsRow), variant("value", new Map([
|
|
979
|
-
["util", { series: "util", label: "Util %", sub: none,
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
{
|
|
983
|
-
{
|
|
984
|
-
],
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
],
|
|
996
|
-
points: [], cells: [], allocs: [], act: [], plan: [], shifts: [], marks: [] }],
|
|
997
|
-
["load", { series: "load", label: "H2 load", sub: some("%/wk"), jobs: [], points: [],
|
|
998
|
-
cells: [
|
|
999
|
-
{ at: variant("time", new Date("2026-06-29T00:00:00Z")), value: some(46.0), label: some("46") },
|
|
1000
|
-
{ at: variant("time", new Date("2026-07-13T00:00:00Z")), value: some(58.0), label: some("58") },
|
|
1001
|
-
{ at: variant("time", new Date("2026-07-27T00:00:00Z")), value: some(66.0), label: some("66") },
|
|
1002
|
-
{ at: variant("time", new Date("2026-08-10T00:00:00Z")), value: some(72.0), label: some("72") },
|
|
1003
|
-
{ at: variant("time", new Date("2026-08-24T00:00:00Z")), value: some(84.0), label: some("84") },
|
|
1004
|
-
{ at: variant("time", new Date("2026-09-07T00:00:00Z")), value: some(96.0), label: some("96") },
|
|
1005
|
-
],
|
|
1006
|
-
allocs: [], act: [], plan: [], shifts: [], marks: [] }],
|
|
1007
|
-
["van1", { series: "van", label: "Van 1", sub: none, jobs: [], points: [], cells: [],
|
|
1008
|
-
allocs: [
|
|
1009
|
-
{ key: "a1", at: new Date("2026-07-06T00:00:00Z"), state: variant("confirmed", null) },
|
|
1010
|
-
{ key: "a2", at: new Date("2026-07-27T00:00:00Z"), state: variant("proposed", variant("recommended", null)) },
|
|
1011
|
-
{ key: "a3", at: new Date("2026-08-10T00:00:00Z"), state: variant("confirmed", null) },
|
|
1012
|
-
],
|
|
1013
|
-
act: [], plan: [], shifts: [], marks: [] }],
|
|
983
|
+
["util", { ...VARIANTS_EMPTY, series: "util", label: "Util %", sub: none, util: fortnightsFromW27([46.0, 58.0, 66.0, 72.0, 84.0, 96.0]) }],
|
|
984
|
+
["p03", { ...VARIANTS_EMPTY, series: "press", label: "H1-P03", sub: some("12k sheets/h"),
|
|
985
|
+
jobs: new Map([
|
|
986
|
+
["j4642", { label: "RUN · J-4642", start: new Date("2026-07-06T00:00:00Z"), end: new Date("2026-07-27T00:00:00Z"), state: variant("in-progress", null) }],
|
|
987
|
+
["j4663", { label: "RUN · J-4663", start: new Date("2026-08-03T00:00:00Z"), end: new Date("2026-08-24T00:00:00Z"), state: variant("proposed", variant("recommended", null)) }],
|
|
988
|
+
]) }],
|
|
989
|
+
["p04", { ...VARIANTS_EMPTY, series: "press", label: "H1-P04", sub: none,
|
|
990
|
+
jobs: new Map([
|
|
991
|
+
["j4624", { label: "RUN · J-4624", start: new Date("2026-06-29T00:00:00Z"), end: new Date("2026-07-20T00:00:00Z"), state: variant("actual", null) }],
|
|
992
|
+
]) }],
|
|
993
|
+
["load", { ...VARIANTS_EMPTY, series: "load", label: "H2 load", sub: some("%/wk"), load: fortnightsFromW27([46.0, 58.0, 66.0, 72.0, 84.0, 96.0]) }],
|
|
994
|
+
["van1", { ...VARIANTS_EMPTY, series: "van", label: "Van 1", sub: none,
|
|
995
|
+
allocs: new Map([
|
|
996
|
+
["a1", { at: new Date("2026-07-06T00:00:00Z"), state: variant("confirmed", null) }],
|
|
997
|
+
["a2", { at: new Date("2026-07-27T00:00:00Z"), state: variant("proposed", variant("recommended", null)) }],
|
|
998
|
+
["a3", { at: new Date("2026-08-10T00:00:00Z"), state: variant("confirmed", null) }],
|
|
999
|
+
]) }],
|
|
1014
1000
|
// Delivered sheets and their signed Δ against plan — the table row's
|
|
1015
1001
|
// two positions.
|
|
1016
|
-
["desp", { series: "desp", label: "Delivered · k", sub: none,
|
|
1017
|
-
act: [
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
{
|
|
1032
|
-
],
|
|
1033
|
-
marks: [] }],
|
|
1034
|
-
["ms", { series: "ms", label: "Milestones", sub: none, jobs: [], points: [], cells: [], allocs: [], act: [], plan: [], shifts: [],
|
|
1035
|
-
marks: [
|
|
1036
|
-
{ key: "k", at: variant("time", new Date("2026-07-13T00:00:00Z")), kind: variant("milestone", null), icon: none, label: some("KICKOFF") },
|
|
1037
|
-
{ key: "a", at: variant("time", new Date("2026-08-17T00:00:00Z")), kind: variant("exception", null), icon: none, label: some("AUDIT") },
|
|
1038
|
-
] }],
|
|
1039
|
-
["p11", { series: "gpress", label: "H3-P11", sub: none,
|
|
1040
|
-
jobs: [
|
|
1041
|
-
{ key: "j4903", label: "RUN · J-4903", start: new Date("2026-07-13T00:00:00Z"), end: new Date("2026-08-10T00:00:00Z"), state: variant("confirmed", null) },
|
|
1042
|
-
],
|
|
1043
|
-
points: [], cells: [], allocs: [], act: [], plan: [], shifts: [], marks: [] }],
|
|
1002
|
+
["desp", { ...VARIANTS_EMPTY, series: "desp", label: "Delivered · k", sub: none,
|
|
1003
|
+
act: fortnightsFromW27([69.0, 87.0, 99.0, 108.0, 126.0, 144.0]),
|
|
1004
|
+
plan: fortnightsFromW27([-24.0, -12.0, -4.0, 2.0, 14.0, 26.0]) }],
|
|
1005
|
+
["crewA", { ...VARIANTS_EMPTY, series: "crew", label: "Crew A", sub: none,
|
|
1006
|
+
shifts: new Map([
|
|
1007
|
+
["s1", { from: new Date("2026-06-29T00:00:00Z"), to: new Date("2026-07-13T00:00:00Z"), label: "80h", state: variant("confirmed", null) }],
|
|
1008
|
+
["s2", { from: new Date("2026-07-27T00:00:00Z"), to: new Date("2026-08-10T00:00:00Z"), label: "+64h", state: variant("proposed", variant("recommended", null)) }],
|
|
1009
|
+
]) }],
|
|
1010
|
+
["ms", { ...VARIANTS_EMPTY, series: "ms", label: "Milestones", sub: none,
|
|
1011
|
+
marks: new Map([
|
|
1012
|
+
["k", { at: new Date("2026-07-13T00:00:00Z"), kind: variant("milestone", null), label: some("KICKOFF") }],
|
|
1013
|
+
["a", { at: new Date("2026-08-17T00:00:00Z"), kind: variant("exception", null), label: some("AUDIT") }],
|
|
1014
|
+
]) }],
|
|
1015
|
+
["p11", { ...VARIANTS_EMPTY, series: "gpress", label: "H3-P11", sub: none,
|
|
1016
|
+
jobs: new Map([
|
|
1017
|
+
["j4903", { label: "RUN · J-4903", start: new Date("2026-07-13T00:00:00Z"), end: new Date("2026-08-10T00:00:00Z"), state: variant("confirmed", null) }],
|
|
1018
|
+
]) }],
|
|
1044
1019
|
])));
|
|
1045
1020
|
/**
|
|
1046
1021
|
* THE Plan configurator — the slot-2 variant-space surface the retired Gantt /
|
|
@@ -1085,39 +1060,37 @@ export const planVariants = example({
|
|
|
1085
1060
|
key: "util", title: "Utilisation",
|
|
1086
1061
|
match: r => r.series.equal("util"),
|
|
1087
1062
|
label: r => r.label, id: true, height: "spark", expandable: true,
|
|
1088
|
-
layers:
|
|
1063
|
+
layers: [Plan.line(Plan.values("util", (pct) => ({ y: pct })))],
|
|
1089
1064
|
}),
|
|
1090
1065
|
Plan.series.span(VariantsOpsRow, {
|
|
1091
1066
|
key: "press", title: "Press jobs",
|
|
1092
1067
|
match: r => r.series.equal("press"),
|
|
1093
1068
|
label: r => r.label, id: true, sub: r => r.sub,
|
|
1094
|
-
runs:
|
|
1095
|
-
key: j.key, start: j.start, end: j.end, label: j.label, state: j.state,
|
|
1096
|
-
})),
|
|
1069
|
+
runs: Plan.items("jobs", { from: "start", to: "end" }, (job) => ({ label: job.label, state: job.state })),
|
|
1097
1070
|
}),
|
|
1098
1071
|
Plan.series.heat(VariantsOpsRow, {
|
|
1099
1072
|
key: "load", title: "Hall load",
|
|
1100
1073
|
match: r => r.series.equal("load"),
|
|
1101
1074
|
label: r => r.label, sub: r => r.sub,
|
|
1102
|
-
cells:
|
|
1075
|
+
cells: Plan.heatCells(Plan.values("load", (pct) => ({ value: pct, label: some(East.Float.printFixed(pct, 0n)) })), { min: 0, max: 100 }),
|
|
1103
1076
|
}),
|
|
1104
1077
|
Plan.series.buckets(VariantsOpsRow, {
|
|
1105
1078
|
key: "van", title: "Van drops",
|
|
1106
1079
|
match: r => r.series.equal("van"),
|
|
1107
1080
|
label: r => r.label,
|
|
1108
|
-
events:
|
|
1081
|
+
events: Plan.items("allocs", { at: "at" }, (alloc) => ({ state: alloc.state })),
|
|
1109
1082
|
}),
|
|
1110
1083
|
// The positions STACKED — the row grows a line per position.
|
|
1111
1084
|
Plan.series.table(VariantsOpsRow, {
|
|
1112
1085
|
key: "desp", title: "Deliveries",
|
|
1113
1086
|
match: r => r.series.equal("desp"),
|
|
1114
1087
|
label: r => r.label, split: "vertical",
|
|
1115
|
-
series:
|
|
1116
|
-
Plan.tableSeries({ strong: true, cells: Plan.tableCells(
|
|
1088
|
+
series: [
|
|
1089
|
+
Plan.tableSeries({ strong: true, cells: Plan.tableCells(Plan.values("act", (n) => ({ value: n }))) }),
|
|
1117
1090
|
Plan.tableSeries({
|
|
1118
1091
|
tone: "muted",
|
|
1119
1092
|
format: Format.Number({ maximumFractionDigits: 0n, signDisplay: "always" }),
|
|
1120
|
-
cells: Plan.tableCells(
|
|
1093
|
+
cells: Plan.tableCells(Plan.values("plan", (n) => ({ value: n }))),
|
|
1121
1094
|
}),
|
|
1122
1095
|
],
|
|
1123
1096
|
format: Format.Number({ maximumFractionDigits: 0n }),
|
|
@@ -1126,13 +1099,13 @@ export const planVariants = example({
|
|
|
1126
1099
|
key: "crew", title: "Crew shifts",
|
|
1127
1100
|
match: r => r.series.equal("crew"),
|
|
1128
1101
|
label: r => r.label,
|
|
1129
|
-
chips:
|
|
1102
|
+
chips: Plan.items("shifts", { from: "from", to: "to" }, (shift) => ({ label: shift.label, state: shift.state })),
|
|
1130
1103
|
}),
|
|
1131
1104
|
Plan.series.events(VariantsOpsRow, {
|
|
1132
1105
|
key: "ms", title: "Milestones",
|
|
1133
1106
|
match: r => r.series.equal("ms"),
|
|
1134
1107
|
label: r => r.label, id: true,
|
|
1135
|
-
marks:
|
|
1108
|
+
marks: Plan.items("marks", { at: "at" }, (mark) => ({ kind: mark.kind, label: mark.label })),
|
|
1136
1109
|
}),
|
|
1137
1110
|
// A section header, so the group-toggle callback has something to fire on.
|
|
1138
1111
|
Plan.series.section(VariantsOpsRow, { key: "hall3", title: "Hall 3", meta: "1 row" }, [
|
|
@@ -1140,9 +1113,7 @@ export const planVariants = example({
|
|
|
1140
1113
|
key: "gpress", title: "Grouped jobs",
|
|
1141
1114
|
match: r => r.series.equal("gpress"),
|
|
1142
1115
|
label: r => r.label, id: true,
|
|
1143
|
-
runs:
|
|
1144
|
-
key: j.key, start: j.start, end: j.end, label: j.label, state: j.state,
|
|
1145
|
-
})),
|
|
1116
|
+
runs: Plan.items("jobs", { from: "start", to: "end" }, (job) => ({ label: job.label, state: job.state })),
|
|
1146
1117
|
}),
|
|
1147
1118
|
]),
|
|
1148
1119
|
], ArrayType(Plan.Types.Series(VariantsOpsRow)));
|
|
@@ -1217,36 +1188,51 @@ export const planVariants = example({
|
|
|
1217
1188
|
// planMeasures — measure rows over weekly readings, folded to the resolution
|
|
1218
1189
|
// the slice states (#824)
|
|
1219
1190
|
// ============================================================================
|
|
1191
|
+
/** A band's bounds at a week: its low and high edges. */
|
|
1192
|
+
export const MeasureBand = StructType({ lo: FloatType, hi: FloatType });
|
|
1220
1193
|
/**
|
|
1221
|
-
* A measure — its
|
|
1222
|
-
*
|
|
1223
|
-
*
|
|
1224
|
-
*
|
|
1225
|
-
*
|
|
1226
|
-
*
|
|
1227
|
-
*
|
|
1228
|
-
* (
|
|
1194
|
+
* A measure — its values a week apiece, each keyed by its week's instant (#1436),
|
|
1195
|
+
* each collection read by name: `readings`, the cells a heat or table row
|
|
1196
|
+
* draws, a week reading none its no-data cell; `second`, the position a table
|
|
1197
|
+
* row pairs with them (a Δ against plan, what left); `points`, the readings a
|
|
1198
|
+
* chart row draws, and `paired`, the series it pairs with them (the stacked
|
|
1199
|
+
* columns' second hall, the dual chart's on-time line); `band`, a band's
|
|
1200
|
+
* bounds; and `segments`, a finishing line's capacity compositions. A measure
|
|
1201
|
+
* nests (`children`): a hall holds its presses, a top its contracts, a contract
|
|
1202
|
+
* its orders — the hierarchy is the data's own (#822), to whatever depth it has.
|
|
1229
1203
|
*/
|
|
1230
1204
|
export const Measure = RecursiveType((self) => StructType({
|
|
1231
1205
|
series: StringType, label: StringType,
|
|
1232
1206
|
sub: OptionType(StringType), value: OptionType(StringType),
|
|
1233
|
-
readings:
|
|
1234
|
-
second:
|
|
1235
|
-
|
|
1236
|
-
|
|
1207
|
+
readings: DictType(DateTimeType, OptionType(FloatType)),
|
|
1208
|
+
second: DictType(DateTimeType, OptionType(FloatType)),
|
|
1209
|
+
points: DictType(DateTimeType, FloatType),
|
|
1210
|
+
paired: DictType(DateTimeType, FloatType),
|
|
1211
|
+
band: DictType(DateTimeType, MeasureBand),
|
|
1212
|
+
segments: DictType(DateTimeType, ArrayType(Plan.Types.Segment)),
|
|
1237
1213
|
children: DictType(StringType, self),
|
|
1238
1214
|
}));
|
|
1215
|
+
/** Every collection a measure holds, empty: a measure names the few it fills. */
|
|
1216
|
+
const MEASURE_EMPTY = {
|
|
1217
|
+
readings: new Map(), second: new Map(), points: new Map(), paired: new Map(), band: new Map(), segments: new Map(), children: new Map(),
|
|
1218
|
+
};
|
|
1219
|
+
/** A value a week from W23 (Monday 1 June 2026), each keyed by its week's instant. */
|
|
1220
|
+
const fromW23 = (weekly) => atInstants(weekly.map((v, i) => [new Date(Date.UTC(2026, 5, 1 + 7 * i)), v]));
|
|
1239
1221
|
/** On-time %, a week apiece from W23: July's weeks run under the 92 breach. */
|
|
1240
|
-
const MEASURE_ON_TIME = [
|
|
1222
|
+
const MEASURE_ON_TIME = [96.1, 96.4, 96.8, 97.0, 96.2, 91.0, 89.4, 88.9, 90.6, 93.8, 94.0, 94.2];
|
|
1241
1223
|
/** Sheets printed, thousands a week — Hall 1's, then Hall 2's. */
|
|
1242
|
-
const MEASURE_PRINTED_H1 = [
|
|
1243
|
-
const MEASURE_PRINTED_H2 = [
|
|
1224
|
+
const MEASURE_PRINTED_H1 = [28.0, 34.0, 40.0, 29.0, 35.0, 41.0, 30.0, 36.0, 42.0, 31.0, 37.0, 43.0];
|
|
1225
|
+
const MEASURE_PRINTED_H2 = [14.0, 19.0, 24.0, 16.0, 21.0, 26.0, 18.0, 23.0, 15.0, 20.0, 25.0, 17.0];
|
|
1244
1226
|
/** An order's delivered sheets, thousands a week — the week of 3 August none. */
|
|
1245
1227
|
const MEASURE_ACT = [some(40.0), some(47.0), some(54.0), some(61.0), some(68.0), some(75.0), some(82.0), some(89.0), some(96.0), none, some(110.0), some(117.0)];
|
|
1246
1228
|
/** The Δ against plan beside them — some weeks none. */
|
|
1247
1229
|
const MEASURE_DELTA = [some(-8.0), some(-6.5), some(-5.0), none, some(-2.0), some(-0.5), some(1.0), none, some(4.0), some(5.5), some(7.0), none];
|
|
1248
1230
|
/** What left as the sheets came in. */
|
|
1249
1231
|
const MEASURE_OUT = [some(-12.0), some(-15.0), some(-18.0), some(-21.0), some(-24.0), some(-27.0), some(-30.0), some(-33.0), some(-36.0), some(-39.0), some(-42.0), some(-45.0)];
|
|
1232
|
+
/** An order: its delivered sheets, and the position beside them. */
|
|
1233
|
+
const order = (series, label, second = []) => ({ ...MEASURE_EMPTY, series, label, sub: none, value: none, readings: fromW23(MEASURE_ACT), second: fromW23(second) });
|
|
1234
|
+
/** A parent measure: no readings of its own, its children's subtotal. */
|
|
1235
|
+
const parent = (series, label, children) => ({ ...MEASURE_EMPTY, series, label, sub: none, value: none, children: new Map(children) });
|
|
1250
1236
|
/**
|
|
1251
1237
|
* Twelve weeks of every measure, W23–W34 — from the first Monday of June, so
|
|
1252
1238
|
* the first month's column starts with the first week; the weeks after now
|
|
@@ -1254,128 +1240,96 @@ const MEASURE_OUT = [some(-12.0), some(-15.0), some(-18.0), some(-21.0), some(-2
|
|
|
1254
1240
|
*/
|
|
1255
1241
|
export const planMeasureReadings = e3.input("plan_measure_readings", DictType(StringType, Measure), variant("value", new Map([
|
|
1256
1242
|
// ── Output ──
|
|
1257
|
-
["spark", { series: "spark", label: "On-time", sub: none, value: some("94.2%"),
|
|
1258
|
-
readings: MEASURE_ON_TIME, second: [], lo: [], hi: [], segments: [], children: new Map() }],
|
|
1243
|
+
["spark", { ...MEASURE_EMPTY, series: "spark", label: "On-time", sub: none, value: some("94.2%"), points: fromW23(MEASURE_ON_TIME) }],
|
|
1259
1244
|
// A running total: each week the sheets printed so far.
|
|
1260
|
-
["cum", { series: "cum", label: "Cumulative · k", sub: none, value: some("194 k sheets"),
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
second: [], lo: [], hi: [], segments: [], children: new Map() }],
|
|
1268
|
-
["refs", { series: "refs", label: "On-time + refs", sub: none, value: none,
|
|
1269
|
-
readings: MEASURE_ON_TIME, second: [], lo: [], hi: [], segments: [], children: new Map() }],
|
|
1245
|
+
["cum", { ...MEASURE_EMPTY, series: "cum", label: "Cumulative · k", sub: none, value: some("194 k sheets"),
|
|
1246
|
+
points: fromW23([40.0, 54.0, 68.0, 82.0, 96.0, 110.0, 124.0, 138.0, 152.0, 166.0, 180.0, 194.0]) }],
|
|
1247
|
+
["stacked", { ...MEASURE_EMPTY, series: "stacked", label: "Printed · k", sub: some("k/wk"), value: none,
|
|
1248
|
+
points: fromW23(MEASURE_PRINTED_H1), paired: fromW23(MEASURE_PRINTED_H2) }],
|
|
1249
|
+
["ppm", { ...MEASURE_EMPTY, series: "ppm", label: "Defects · ppm", sub: none, value: some("161"),
|
|
1250
|
+
points: fromW23([120.0, 157.0, 134.0, 171.0, 148.0, 125.0, 162.0, 139.0, 176.0, 153.0, 130.0, 167.0]) }],
|
|
1251
|
+
["refs", { ...MEASURE_EMPTY, series: "refs", label: "On-time + refs", sub: none, value: none, points: fromW23(MEASURE_ON_TIME) }],
|
|
1270
1252
|
// Output columns on the left axis; the on-time line and its ±3 band on the right.
|
|
1271
|
-
["dual", { series: "dual", label: "Out + on-time", sub: none, value: none,
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1253
|
+
["dual", { ...MEASURE_EMPTY, series: "dual", label: "Out + on-time", sub: none, value: none,
|
|
1254
|
+
points: fromW23(MEASURE_PRINTED_H1), paired: fromW23(MEASURE_ON_TIME),
|
|
1255
|
+
band: fromW23([
|
|
1256
|
+
{ lo: 93.1, hi: 99.1 }, { lo: 93.4, hi: 99.4 }, { lo: 93.8, hi: 99.8 }, { lo: 94.0, hi: 100.0 }, { lo: 93.2, hi: 99.2 }, { lo: 88.0, hi: 94.0 },
|
|
1257
|
+
{ lo: 86.4, hi: 92.4 }, { lo: 85.9, hi: 91.9 }, { lo: 87.6, hi: 93.6 }, { lo: 90.8, hi: 96.8 }, { lo: 91.0, hi: 97.0 }, { lo: 91.2, hi: 97.2 },
|
|
1258
|
+
]) }],
|
|
1276
1259
|
// ── Load ──
|
|
1277
1260
|
// A hall with no readings of its own: its row is its presses' per-bucket
|
|
1278
1261
|
// mean. H1-P03 read nothing in the week of 29 June; H1-P04 nothing in
|
|
1279
1262
|
// August, which folds to a month with no data.
|
|
1280
|
-
["hall1",
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
readings: [some(45.0), some(62.0), some(79.0), some(46.0), some(63.0), some(80.0), some(47.0), some(64.0), some(81.0), some(48.0), some(65.0), some(82.0)],
|
|
1289
|
-
second: [], lo: [], hi: [], segments: [], children: new Map() }],
|
|
1263
|
+
["hall1", parent("depth", "Hall 1", [
|
|
1264
|
+
["p03h", { ...MEASURE_EMPTY, series: "depth", label: "H1-P03", sub: none, value: none,
|
|
1265
|
+
readings: fromW23([some(46.0), some(52.0), some(58.0), some(61.0), none, some(72.0), some(78.0), some(84.0), some(90.0), some(96.0), some(98.0), some(92.0)]) }],
|
|
1266
|
+
["p04h", { ...MEASURE_EMPTY, series: "depth", label: "H1-P04", sub: none, value: none,
|
|
1267
|
+
readings: fromW23([some(44.0), some(50.0), some(55.0), some(60.0), some(63.0), some(70.0), some(74.0), some(80.0), some(86.0), none, none, none]) }],
|
|
1268
|
+
])],
|
|
1269
|
+
["peak", { ...MEASURE_EMPTY, series: "peak", label: "Hall 2 peak %", sub: none, value: none,
|
|
1270
|
+
readings: fromW23([some(45.0), some(62.0), some(79.0), some(46.0), some(63.0), some(80.0), some(47.0), some(64.0), some(81.0), some(48.0), some(65.0), some(82.0)]) }],
|
|
1290
1271
|
// Crew A's booked fraction of its hours, a week apiece.
|
|
1291
|
-
["booked", { series: "booked", label: "Crew A", sub: some("booked h"), value: none,
|
|
1292
|
-
|
|
1293
|
-
second: [], lo: [], hi: [], segments: [], children: new Map() }],
|
|
1272
|
+
["booked", { ...MEASURE_EMPTY, series: "booked", label: "Crew A", sub: some("booked h"), value: none,
|
|
1273
|
+
points: fromW23([0.9, 0.84, 0.79, 0.74, 0.68, 0.62, 0.57, 0.52, 0.46, 0.41, 0.35, 0.3]) }],
|
|
1294
1274
|
// ── Finishing ──
|
|
1295
|
-
// Capacity compositions — plain `{ fill, weight, label }` records
|
|
1296
|
-
// and August's are their month's only one, so
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
segments: [
|
|
1300
|
-
|
|
1275
|
+
// Capacity compositions — plain `{ fill, weight, label }` records, a
|
|
1276
|
+
// week's at its week. June's and August's are their month's only one, so
|
|
1277
|
+
// they keep their labels.
|
|
1278
|
+
["finishing", { ...MEASURE_EMPTY, series: "segments", label: "Finishing line", sub: some("capacity"), value: none,
|
|
1279
|
+
segments: atInstants([
|
|
1280
|
+
[new Date("2026-06-29T00:00:00Z"), [
|
|
1301
1281
|
{ fill: variant("success", null), weight: 60.0, label: some("60%") },
|
|
1302
1282
|
{ fill: variant("warning", null), weight: 25.0, label: some("25%") },
|
|
1303
1283
|
{ fill: variant("slack", null), weight: 15.0, label: none },
|
|
1304
|
-
]
|
|
1305
|
-
|
|
1284
|
+
]],
|
|
1285
|
+
[new Date("2026-07-06T00:00:00Z"), [
|
|
1306
1286
|
{ fill: variant("success", null), weight: 70.0, label: some("70%") },
|
|
1307
1287
|
{ fill: variant("slack", null), weight: 30.0, label: none },
|
|
1308
|
-
]
|
|
1309
|
-
|
|
1288
|
+
]],
|
|
1289
|
+
[new Date("2026-07-13T00:00:00Z"), [
|
|
1310
1290
|
{ fill: variant("danger", null), weight: 40.0, label: some("40%") },
|
|
1311
1291
|
{ fill: variant("free", null), weight: 60.0, label: none },
|
|
1312
|
-
]
|
|
1313
|
-
|
|
1292
|
+
]],
|
|
1293
|
+
[new Date("2026-08-10T00:00:00Z"), [
|
|
1314
1294
|
{ fill: variant("success", null), weight: 55.0, label: some("55%") },
|
|
1315
1295
|
{ fill: variant("warning", null), weight: 30.0, label: some("30%") },
|
|
1316
1296
|
{ fill: variant("slack", null), weight: 15.0, label: none },
|
|
1317
|
-
]
|
|
1318
|
-
] }],
|
|
1297
|
+
]],
|
|
1298
|
+
]) }],
|
|
1319
1299
|
// ── Deliveries ──
|
|
1320
1300
|
// Two levels of nesting — a top holds its contracts, a contract its
|
|
1321
1301
|
// orders; every level with no readings of its own is a subtotal.
|
|
1322
|
-
["deliveries",
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
["contract-b", { series: "orders", label: "Contract B", sub: none, value: none, readings: [], second: [], lo: [], hi: [], segments: [],
|
|
1330
|
-
children: new Map([
|
|
1331
|
-
["j6219", { series: "orders", label: "J-6219", sub: none, value: none, readings: MEASURE_ACT, second: [], lo: [], hi: [], segments: [], children: new Map() }],
|
|
1332
|
-
]) }],
|
|
1333
|
-
]) }],
|
|
1334
|
-
["reprints", { series: "orders", label: "Reprints", sub: none, value: none, readings: [], second: [], lo: [], hi: [], segments: [],
|
|
1335
|
-
children: new Map([
|
|
1336
|
-
["contract-b", { series: "orders", label: "Contract B", sub: none, value: none, readings: [], second: [], lo: [], hi: [], segments: [],
|
|
1337
|
-
children: new Map([
|
|
1338
|
-
["rp-0031", { series: "orders", label: "RP-0031", sub: none, value: none, readings: MEASURE_ACT, second: [], lo: [], hi: [], segments: [], children: new Map() }],
|
|
1339
|
-
]) }],
|
|
1340
|
-
]) }],
|
|
1302
|
+
["deliveries", parent("orders", "Deliveries", [
|
|
1303
|
+
["contract-a", parent("orders", "Contract A", [["j6188", order("orders", "J-6188")], ["j6204", order("orders", "J-6204")]])],
|
|
1304
|
+
["contract-b", parent("orders", "Contract B", [["j6219", order("orders", "J-6219")]])],
|
|
1305
|
+
])],
|
|
1306
|
+
["reprints", parent("orders", "Reprints", [
|
|
1307
|
+
["contract-b", parent("orders", "Contract B", [["rp-0031", order("orders", "RP-0031")]])],
|
|
1308
|
+
])],
|
|
1341
1309
|
// Footer emphasis, a negative and the em-dash: July read nothing at all.
|
|
1342
|
-
["net", { series: "net", label: "Net flow", sub: none, value: none,
|
|
1343
|
-
readings: [some(22.0), some(-26.0), some(8.0), some(-4.0), some(12.0), none, none, none, none, some(-30.0), some(12.0), some(6.0)],
|
|
1344
|
-
second: [], lo: [], hi: [], segments: [], children: new Map() }],
|
|
1310
|
+
["net", { ...MEASURE_EMPTY, series: "net", label: "Net flow", sub: none, value: none,
|
|
1311
|
+
readings: fromW23([some(22.0), some(-26.0), some(8.0), some(-4.0), some(12.0), none, none, none, none, some(-30.0), some(12.0), some(6.0)]) }],
|
|
1345
1312
|
// Multi-value rows — the actuals and the Δ per row. The SPLIT (how the
|
|
1346
1313
|
// positions sit against each other) and the GUTTER (one line or two) are
|
|
1347
1314
|
// independent choices, so all four combinations are here: the pair that
|
|
1348
1315
|
// reads well depends on the numbers, not on the split.
|
|
1349
|
-
["actplan", {
|
|
1350
|
-
|
|
1351
|
-
["inout", { series: "inout", label: "In / out", sub: none, value: none,
|
|
1352
|
-
readings: MEASURE_ACT, second: MEASURE_OUT, lo: [], hi: [], segments: [], children: new Map() }],
|
|
1316
|
+
["actplan", { ...order("actplan", "Act · Δ plan", MEASURE_DELTA), sub: some("k/wk") }],
|
|
1317
|
+
["inout", order("inout", "In / out", MEASURE_OUT)],
|
|
1353
1318
|
// Horizontal, on a ONE-line gutter — the pair reads as a single fact
|
|
1354
1319
|
// ("booked beside free"), so a sub label would only repeat it.
|
|
1355
|
-
["sidebyside",
|
|
1356
|
-
readings: MEASURE_ACT, second: MEASURE_OUT, lo: [], hi: [], segments: [], children: new Map() }],
|
|
1320
|
+
["sidebyside", order("sidebyside", "Booked · free", MEASURE_OUT)],
|
|
1357
1321
|
// Vertical, on a TWO-line gutter — the stack needs the unit spelled out,
|
|
1358
1322
|
// because the positions are the same measure at two times.
|
|
1359
|
-
["overunder", {
|
|
1360
|
-
readings: MEASURE_ACT, second: MEASURE_DELTA, lo: [], hi: [], segments: [], children: new Map() }],
|
|
1323
|
+
["overunder", { ...order("overunder", "Act / plan", MEASURE_DELTA), sub: some("k/wk") }],
|
|
1361
1324
|
// NESTED and multi-value: the subtotal parent mirrors its members, an act
|
|
1362
1325
|
// subtotal beside a Δ subtotal.
|
|
1363
|
-
["lots",
|
|
1364
|
-
children: new Map([
|
|
1365
|
-
["lt-1", { series: "lot", label: "LT-2201", sub: none, value: none, readings: MEASURE_ACT, second: MEASURE_DELTA, lo: [], hi: [], segments: [], children: new Map() }],
|
|
1366
|
-
["lt-2", { series: "lot", label: "LT-2202", sub: none, value: none, readings: MEASURE_ACT, second: MEASURE_DELTA, lo: [], hi: [], segments: [], children: new Map() }],
|
|
1367
|
-
]) }],
|
|
1326
|
+
["lots", parent("lot", "Lots", [["lt-1", order("lot", "LT-2201", MEASURE_DELTA)], ["lt-2", order("lot", "LT-2202", MEASURE_DELTA)]])],
|
|
1368
1327
|
// The same, STACKED: members and their subtotal both put the two positions
|
|
1369
1328
|
// on their own lines, so the parent has to grow too.
|
|
1370
|
-
["stacks",
|
|
1371
|
-
children: new Map([
|
|
1372
|
-
["st-1", { series: "stack", label: "ST-3301", sub: none, value: none, readings: MEASURE_ACT, second: MEASURE_OUT, lo: [], hi: [], segments: [], children: new Map() }],
|
|
1373
|
-
["st-2", { series: "stack", label: "ST-3302", sub: none, value: none, readings: MEASURE_ACT, second: MEASURE_OUT, lo: [], hi: [], segments: [], children: new Map() }],
|
|
1374
|
-
]) }],
|
|
1329
|
+
["stacks", parent("stack", "Stacks", [["st-1", order("stack", "ST-3301", MEASURE_OUT)], ["st-2", order("stack", "ST-3302", MEASURE_OUT)]])],
|
|
1375
1330
|
// A balance: the paper in stock, thousands of sheets, each week's close.
|
|
1376
|
-
["stock", { series: "stock", label: "Paper stock · k", sub: none, value: none,
|
|
1377
|
-
readings: [some(420.0), some(398.0), some(376.0), some(354.0), some(332.0), some(310.0), some(288.0), some(266.0), some(244.0), some(222.0), some(200.0), some(178.0)],
|
|
1378
|
-
second: [], lo: [], hi: [], segments: [], children: new Map() }],
|
|
1331
|
+
["stock", { ...MEASURE_EMPTY, series: "stock", label: "Paper stock · k", sub: none, value: none,
|
|
1332
|
+
readings: fromW23([some(420.0), some(398.0), some(376.0), some(354.0), some(332.0), some(310.0), some(288.0), some(266.0), some(244.0), some(222.0), some(200.0), some(178.0)]) }],
|
|
1379
1333
|
])));
|
|
1380
1334
|
/** A week the readings cover: its first instant, and its name. */
|
|
1381
1335
|
export const MeasureWeek = StructType({ week: DateTimeType, label: StringType });
|
|
@@ -1384,22 +1338,26 @@ export const MeasureWeek = StructType({ week: DateTimeType, label: StringType })
|
|
|
1384
1338
|
* the library's Weeks tab lists, the rail's `weeks` source holding the
|
|
1385
1339
|
* canvas's window in its range (#1344).
|
|
1386
1340
|
*/
|
|
1387
|
-
export const planMeasureWeeks = e3.record("plan_measure_weeks", DictType(StringType, MeasureWeek),
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
]
|
|
1401
|
-
|
|
1402
|
-
|
|
1341
|
+
export const planMeasureWeeks = e3.record("plan_measure_weeks", DictType(StringType, MeasureWeek), {
|
|
1342
|
+
initial: new Map([
|
|
1343
|
+
["W23", { week: new Date("2026-06-01T00:00:00Z"), label: "W23 · 1 Jun" }],
|
|
1344
|
+
["W24", { week: new Date("2026-06-08T00:00:00Z"), label: "W24 · 8 Jun" }],
|
|
1345
|
+
["W25", { week: new Date("2026-06-15T00:00:00Z"), label: "W25 · 15 Jun" }],
|
|
1346
|
+
["W26", { week: new Date("2026-06-22T00:00:00Z"), label: "W26 · 22 Jun" }],
|
|
1347
|
+
["W27", { week: new Date("2026-06-29T00:00:00Z"), label: "W27 · 29 Jun" }],
|
|
1348
|
+
["W28", { week: new Date("2026-07-06T00:00:00Z"), label: "W28 · 6 Jul" }],
|
|
1349
|
+
["W29", { week: new Date("2026-07-13T00:00:00Z"), label: "W29 · 13 Jul" }],
|
|
1350
|
+
["W30", { week: new Date("2026-07-20T00:00:00Z"), label: "W30 · 20 Jul" }],
|
|
1351
|
+
["W31", { week: new Date("2026-07-27T00:00:00Z"), label: "W31 · 27 Jul" }],
|
|
1352
|
+
["W32", { week: new Date("2026-08-03T00:00:00Z"), label: "W32 · 3 Aug" }],
|
|
1353
|
+
["W33", { week: new Date("2026-08-10T00:00:00Z"), label: "W33 · 10 Aug" }],
|
|
1354
|
+
["W34", { week: new Date("2026-08-17T00:00:00Z"), label: "W34 · 17 Aug" }],
|
|
1355
|
+
]),
|
|
1356
|
+
indexes: (index) => ({
|
|
1357
|
+
// The weeks by their first instant: what the window narrows them through.
|
|
1358
|
+
plan_measure_weeks_by_week: index.on(["week"]),
|
|
1359
|
+
}),
|
|
1360
|
+
});
|
|
1403
1361
|
/**
|
|
1404
1362
|
* Measure rows — the chart, heat and table grammars over weekly readings, and
|
|
1405
1363
|
* the temporal fold (#824) that shows them at whatever resolution the slice
|
|
@@ -1442,7 +1400,7 @@ export const planMeasures = example({
|
|
|
1442
1400
|
"em-dash", "neg", "tableSeries", "split", "horizontal", "vertical", "multi-value", "multi-cell", "two-line",
|
|
1443
1401
|
"strong", "muted", "rollup", "mirror", "position", "fold", "temporal fold", "resolution", "week", "month",
|
|
1444
1402
|
"rebucket", "bucket", "max", "last", "count", "default", "override", "layer", "Plan.layer", "column",
|
|
1445
|
-
"line", "Slice", "Slice.bindRecords", "source", "Data.bindPaged", "
|
|
1403
|
+
"line", "Slice", "Slice.bindRecords", "source", "Data.bindPaged", "indexes", "toolbar", "range", "brush", "section", "match", "gutter",
|
|
1446
1404
|
"raw", "readings", "Reactive", "Data.bind", "bound", "e3.input", "e3.record", "library", "Plan.library.tab", "#824", "#1258", "#1344",
|
|
1447
1405
|
],
|
|
1448
1406
|
description: "Measure rows over weekly readings bound from e3, at MONTH to start — chart rows (a line with its breach, an area, stacked columns, a scatter, every annotation, and a fixed dual-axis composition with its band and swatches), heat rows (colour depth under a hall averaging its presses on its scale with the no-data hatch, weight bars with a planned tail, status segments) and table rows (subtotals at every depth, footer emphasis with a negative and the em-dash, multi-value positions in every split × gutter combination); each row folds a month's weeks by what its values mean, or by its override (peak load by max, a running total and the paper stock by last, the dual chart's columns by mean), the toolbar's resolution segment switches MONTH and WEEK, and one Slice.bindRecords rail's weeks source, which the Weeks tab lists, holds the window the horizon brush moves",
|
|
@@ -1456,31 +1414,18 @@ export const planMeasures = example({
|
|
|
1456
1414
|
const w1 = $.const(new Date("2025-12-29T00:00:00Z"), DateTimeType);
|
|
1457
1415
|
return w1.addWeeks(n.subtract(1n));
|
|
1458
1416
|
}));
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
//
|
|
1463
|
-
//
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
none: (_$3) => East.value(none, OptionType(Point)),
|
|
1470
|
-
}))));
|
|
1471
|
-
const band = $.const(East.function([ArrayType(FloatType), ArrayType(FloatType)], ArrayType(BandPoint), ($, lo, hi) => East.Array.generate(lo.size(), BandPoint, (_$2, i) => ({ week: week(i.add(23n)), lo: lo.get(i), hi: hi.get(i) }))));
|
|
1472
|
-
const heat = $.const(East.function([ArrayType(OptionType(FloatType))], ArrayType(Plan.Types.HeatCell), ($, readings) => East.Array.generate(readings.size(), Plan.Types.HeatCell, ($2, i) => {
|
|
1473
|
-
const value = $2.let(readings.get(i), OptionType(FloatType));
|
|
1474
|
-
const label = $2.let(none, OptionType(StringType));
|
|
1475
|
-
$2.match(value, { some: ($3, v) => { $3.assign(label, some(East.Float.printFixed(v, 0n))); } });
|
|
1476
|
-
return { at: Plan.at.time(week(i.add(23n))), value, label };
|
|
1477
|
-
})));
|
|
1478
|
-
const booked = $.const(East.function([ArrayType(OptionType(FloatType))], ArrayType(Plan.Types.WeightCell), ($, readings) => readings.filterMap((_$2, r, i) => r.match({
|
|
1479
|
-
some: (_$3, f) => East.value(some({ at: Plan.at.time(week(i.add(23n))), fraction: f, planned: i.add(23n).greater(27n) }), OptionType(Plan.Types.WeightCell)),
|
|
1480
|
-
none: (_$3) => East.value(none, OptionType(Plan.Types.WeightCell)),
|
|
1481
|
-
}))));
|
|
1482
|
-
const weekly = $.const(East.function([ArrayType(OptionType(FloatType))], ArrayType(Weekly), ($, readings) => East.Array.generate(readings.size(), Weekly, (_$2, i) => ({ at: week(i.add(23n)), value: readings.get(i) }))));
|
|
1417
|
+
// Each row reads its measure's values BY NAME, each at its week
|
|
1418
|
+
// (#1436): a chart's points, heat cells printing their values (a
|
|
1419
|
+
// week with none the no-data hatch), booked fractions (the weeks
|
|
1420
|
+
// after now the planned, pale tail), and table cells (a week with
|
|
1421
|
+
// none the em-dash).
|
|
1422
|
+
const printed = (v) => v.match({
|
|
1423
|
+
some: (_$2, x) => East.value(some(East.Float.printFixed(x, 0n)), OptionType(StringType)),
|
|
1424
|
+
none: () => East.value(none, OptionType(StringType)),
|
|
1425
|
+
});
|
|
1426
|
+
const now = $.const(week(27n));
|
|
1483
1427
|
const whole = $.const(Format.Number({ maximumFractionDigits: 0n }));
|
|
1428
|
+
const signed = $.const(Format.Number({ maximumFractionDigits: 0n, signDisplay: "always" }));
|
|
1484
1429
|
const series = $.const([
|
|
1485
1430
|
Plan.series.section(Measure, { key: "output", title: "Output", meta: "5 rows" }, [
|
|
1486
1431
|
// A level: the on-time line folds a month's weeks by their
|
|
@@ -1493,14 +1438,14 @@ export const planMeasures = example({
|
|
|
1493
1438
|
label: r => r.label, id: true,
|
|
1494
1439
|
value: r => r.value, status: _r => some(variant("warning", null)),
|
|
1495
1440
|
height: "spark", expandable: true, expandedHeight: "120px",
|
|
1496
|
-
layers:
|
|
1441
|
+
layers: [Plan.line(Plan.values("points", (v) => ({ y: v })), { breach: { below: 92 } })],
|
|
1497
1442
|
}),
|
|
1498
1443
|
// A running total — a month shows where it CLOSED.
|
|
1499
1444
|
Plan.series.chart(Measure, {
|
|
1500
1445
|
key: "cum", title: "Cumulative",
|
|
1501
1446
|
match: r => r.series.equal("cum"),
|
|
1502
1447
|
label: r => r.label, id: true, value: r => r.value,
|
|
1503
|
-
layers:
|
|
1448
|
+
layers: [Plan.area(Plan.values("points", (v) => ({ y: v })), { fold: "last" })],
|
|
1504
1449
|
}),
|
|
1505
1450
|
// Amounts — the two halls' columns stacked by one series id,
|
|
1506
1451
|
// each month their weeks' SUM, on a two-line gutter (label
|
|
@@ -1509,9 +1454,9 @@ export const planMeasures = example({
|
|
|
1509
1454
|
key: "stacked", title: "Stacked",
|
|
1510
1455
|
match: r => r.series.equal("stacked"),
|
|
1511
1456
|
label: r => r.label, id: true, stacked: true, sub: r => r.sub,
|
|
1512
|
-
layers:
|
|
1513
|
-
Plan.
|
|
1514
|
-
Plan.
|
|
1457
|
+
layers: [
|
|
1458
|
+
Plan.column(Plan.values("points", (v) => ({ y: v })), { series: "H1" }),
|
|
1459
|
+
Plan.column(Plan.values("paired", (v) => ({ y: v })), { series: "H2" }),
|
|
1515
1460
|
],
|
|
1516
1461
|
}),
|
|
1517
1462
|
// A scatter draws every reading, folded or not.
|
|
@@ -1519,7 +1464,7 @@ export const planMeasures = example({
|
|
|
1519
1464
|
key: "ppm", title: "Ppm",
|
|
1520
1465
|
match: r => r.series.equal("ppm"),
|
|
1521
1466
|
label: r => r.label, id: true, value: r => r.value,
|
|
1522
|
-
layers:
|
|
1467
|
+
layers: [Plan.scatter(Plan.values("points", (v) => ({ y: v })))],
|
|
1523
1468
|
}),
|
|
1524
1469
|
// A line and every annotation kind, at expanded density.
|
|
1525
1470
|
Plan.series.chart(Measure, {
|
|
@@ -1527,8 +1472,8 @@ export const planMeasures = example({
|
|
|
1527
1472
|
match: r => r.series.equal("refs"),
|
|
1528
1473
|
label: r => r.label, id: true,
|
|
1529
1474
|
height: "expanded",
|
|
1530
|
-
layers:
|
|
1531
|
-
Plan.
|
|
1475
|
+
layers: [
|
|
1476
|
+
Plan.line(Plan.values("points", (v) => ({ y: v })), { breach: { below: 92 } }),
|
|
1532
1477
|
Chart.refLine({ y: 100, label: "TARGET 100" }),
|
|
1533
1478
|
Chart.refBand({ x: [week(29n), week(31n)], label: "CRUNCH" }),
|
|
1534
1479
|
Chart.refDot({ x: week(30n), y: 88.9, label: "LOW" }),
|
|
@@ -1549,10 +1494,10 @@ export const planMeasures = example({
|
|
|
1549
1494
|
left: { domain: [0, 60], tickValues: [0, 25, 50] },
|
|
1550
1495
|
right: { domain: [80, 105], tickValues: [85, 95, 105] },
|
|
1551
1496
|
swatches: [{ color: "ink.3", label: "out" }, { color: "brand.d", label: "on-time · rh" }],
|
|
1552
|
-
layers:
|
|
1553
|
-
Plan.
|
|
1554
|
-
Plan.
|
|
1555
|
-
Plan.
|
|
1497
|
+
layers: [
|
|
1498
|
+
Plan.column(Plan.values("points", (v) => ({ y: v })), { fold: "mean" }),
|
|
1499
|
+
Plan.line(Plan.values("paired", (v) => ({ y: v })), { axis: "right" }),
|
|
1500
|
+
Plan.band(Plan.values("band", (b) => ({ lo: b.lo, hi: b.hi })), { axis: "right" }),
|
|
1556
1501
|
],
|
|
1557
1502
|
}),
|
|
1558
1503
|
]),
|
|
@@ -1564,8 +1509,8 @@ export const planMeasures = example({
|
|
|
1564
1509
|
key: "depth", title: "Depth",
|
|
1565
1510
|
match: r => r.series.equal("depth"),
|
|
1566
1511
|
label: r => r.label, id: true,
|
|
1567
|
-
cells:
|
|
1568
|
-
children:
|
|
1512
|
+
cells: Plan.heatCells(Plan.values("readings", (v) => ({ value: v, label: printed(v) })), { min: 0, max: 100, warnAt: 95 }),
|
|
1513
|
+
children: "children", aggregate: "mean",
|
|
1569
1514
|
scale: { min: 0, max: 100, warnAt: 95 },
|
|
1570
1515
|
}),
|
|
1571
1516
|
// The same kind of reading, folded by its MAX — the peak a
|
|
@@ -1574,7 +1519,7 @@ export const planMeasures = example({
|
|
|
1574
1519
|
key: "peak", title: "Peak load",
|
|
1575
1520
|
match: r => r.series.equal("peak"),
|
|
1576
1521
|
label: r => r.label,
|
|
1577
|
-
cells:
|
|
1522
|
+
cells: Plan.heatCells(Plan.values("readings", (v) => ({ value: v, label: printed(v) })), { min: 0, max: 100, fold: "max", format: whole }),
|
|
1578
1523
|
}),
|
|
1579
1524
|
// A fraction of each week booked — a month's is its weeks'
|
|
1580
1525
|
// mean, pale only when every one of them is planned.
|
|
@@ -1582,7 +1527,7 @@ export const planMeasures = example({
|
|
|
1582
1527
|
key: "booked", title: "Booked",
|
|
1583
1528
|
match: r => r.series.equal("booked"),
|
|
1584
1529
|
label: r => r.label, sub: r => r.sub,
|
|
1585
|
-
cells:
|
|
1530
|
+
cells: Plan.weightCells(Plan.values("points", (fraction, at) => ({ fraction, planned: at.greater(now) }))),
|
|
1586
1531
|
}),
|
|
1587
1532
|
]),
|
|
1588
1533
|
Plan.series.section(Measure, { key: "finishing", title: "Finishing", meta: "1 row" }, [
|
|
@@ -1592,7 +1537,7 @@ export const planMeasures = example({
|
|
|
1592
1537
|
key: "segments", title: "Segments",
|
|
1593
1538
|
match: r => r.series.equal("segments"),
|
|
1594
1539
|
label: r => r.label, sub: r => r.sub,
|
|
1595
|
-
cells:
|
|
1540
|
+
cells: Plan.segmentCells(Plan.values("segments", (segments) => ({ segments }))),
|
|
1596
1541
|
}),
|
|
1597
1542
|
]),
|
|
1598
1543
|
Plan.series.section(Measure, { key: "deliveries", title: "Deliveries", meta: "21 rows" }, [
|
|
@@ -1603,15 +1548,15 @@ export const planMeasures = example({
|
|
|
1603
1548
|
key: "orders", title: "Orders",
|
|
1604
1549
|
match: r => r.series.equal("orders"),
|
|
1605
1550
|
label: r => r.label,
|
|
1606
|
-
cells:
|
|
1607
|
-
children:
|
|
1551
|
+
cells: Plan.tableCells(Plan.values("readings", (v) => ({ value: v }))),
|
|
1552
|
+
children: "children", aggregate: "sum",
|
|
1608
1553
|
format: whole,
|
|
1609
1554
|
}),
|
|
1610
1555
|
Plan.series.table(Measure, {
|
|
1611
1556
|
key: "net", title: "Net",
|
|
1612
1557
|
match: r => r.series.equal("net"),
|
|
1613
1558
|
label: r => r.label, emphasis: "footer",
|
|
1614
|
-
cells:
|
|
1559
|
+
cells: Plan.tableCells(Plan.values("readings", (v) => ({ value: v }))),
|
|
1615
1560
|
format: whole,
|
|
1616
1561
|
}),
|
|
1617
1562
|
// Per-POSITION style declared ONCE, in the CONFIG — a strong
|
|
@@ -1620,13 +1565,9 @@ export const planMeasures = example({
|
|
|
1620
1565
|
key: "actplan", title: "Actual vs plan",
|
|
1621
1566
|
match: r => r.series.equal("actplan"),
|
|
1622
1567
|
label: r => r.label, stacked: true, sub: r => r.sub,
|
|
1623
|
-
series:
|
|
1624
|
-
Plan.tableSeries({ strong: true, rollup: true, cells: Plan.tableCells(
|
|
1625
|
-
Plan.tableSeries({
|
|
1626
|
-
tone: "muted",
|
|
1627
|
-
format: Format.Number({ maximumFractionDigits: 0n, signDisplay: "always" }),
|
|
1628
|
-
cells: Plan.tableCells(weekly(r.second)),
|
|
1629
|
-
}),
|
|
1568
|
+
series: [
|
|
1569
|
+
Plan.tableSeries({ strong: true, rollup: true, cells: Plan.tableCells(Plan.values("readings", (v) => ({ value: v }))) }),
|
|
1570
|
+
Plan.tableSeries({ tone: "muted", format: signed, cells: Plan.tableCells(Plan.values("second", (v) => ({ value: v }))) }),
|
|
1630
1571
|
],
|
|
1631
1572
|
format: whole,
|
|
1632
1573
|
}),
|
|
@@ -1635,9 +1576,9 @@ export const planMeasures = example({
|
|
|
1635
1576
|
key: "inout", title: "Inout",
|
|
1636
1577
|
match: r => r.series.equal("inout"),
|
|
1637
1578
|
label: r => r.label, split: "vertical",
|
|
1638
|
-
series:
|
|
1639
|
-
Plan.tableSeries({ cells: Plan.tableCells(
|
|
1640
|
-
Plan.tableSeries({ tone: "muted", cells: Plan.tableCells(
|
|
1579
|
+
series: [
|
|
1580
|
+
Plan.tableSeries({ cells: Plan.tableCells(Plan.values("readings", (v) => ({ value: v }))) }),
|
|
1581
|
+
Plan.tableSeries({ tone: "muted", cells: Plan.tableCells(Plan.values("second", (v) => ({ value: v }))) }),
|
|
1641
1582
|
],
|
|
1642
1583
|
format: whole,
|
|
1643
1584
|
}),
|
|
@@ -1648,9 +1589,9 @@ export const planMeasures = example({
|
|
|
1648
1589
|
key: "sidebyside", title: "Side by side",
|
|
1649
1590
|
match: r => r.series.equal("sidebyside"),
|
|
1650
1591
|
label: r => r.label, split: "horizontal",
|
|
1651
|
-
series:
|
|
1652
|
-
Plan.tableSeries({ strong: true, cells: Plan.tableCells(
|
|
1653
|
-
Plan.tableSeries({ tone: "muted", cells: Plan.tableCells(
|
|
1592
|
+
series: [
|
|
1593
|
+
Plan.tableSeries({ strong: true, cells: Plan.tableCells(Plan.values("readings", (v) => ({ value: v }))) }),
|
|
1594
|
+
Plan.tableSeries({ tone: "muted", cells: Plan.tableCells(Plan.values("second", (v) => ({ value: v }))) }),
|
|
1654
1595
|
],
|
|
1655
1596
|
format: whole,
|
|
1656
1597
|
}),
|
|
@@ -1663,15 +1604,11 @@ export const planMeasures = example({
|
|
|
1663
1604
|
key: "lot", title: "Lot",
|
|
1664
1605
|
match: r => r.series.equal("lot"),
|
|
1665
1606
|
label: r => r.label,
|
|
1666
|
-
series:
|
|
1667
|
-
Plan.tableSeries({ strong: true, cells: Plan.tableCells(
|
|
1668
|
-
Plan.tableSeries({
|
|
1669
|
-
tone: "muted",
|
|
1670
|
-
format: Format.Number({ maximumFractionDigits: 0n, signDisplay: "always" }),
|
|
1671
|
-
cells: Plan.tableCells(weekly(r.second)),
|
|
1672
|
-
}),
|
|
1607
|
+
series: [
|
|
1608
|
+
Plan.tableSeries({ strong: true, cells: Plan.tableCells(Plan.values("readings", (v) => ({ value: v }))) }),
|
|
1609
|
+
Plan.tableSeries({ tone: "muted", format: signed, cells: Plan.tableCells(Plan.values("second", (v) => ({ value: v }))) }),
|
|
1673
1610
|
],
|
|
1674
|
-
children:
|
|
1611
|
+
children: "children", aggregate: "sum",
|
|
1675
1612
|
format: whole,
|
|
1676
1613
|
}),
|
|
1677
1614
|
// NESTED and VERTICAL — the subtotal stacks its positions the
|
|
@@ -1683,11 +1620,11 @@ export const planMeasures = example({
|
|
|
1683
1620
|
key: "stack", title: "Stack",
|
|
1684
1621
|
match: r => r.series.equal("stack"),
|
|
1685
1622
|
label: r => r.label, split: "vertical",
|
|
1686
|
-
series:
|
|
1687
|
-
Plan.tableSeries({ strong: true, cells: Plan.tableCells(
|
|
1688
|
-
Plan.tableSeries({ tone: "muted", cells: Plan.tableCells(
|
|
1623
|
+
series: [
|
|
1624
|
+
Plan.tableSeries({ strong: true, cells: Plan.tableCells(Plan.values("readings", (v) => ({ value: v }))) }),
|
|
1625
|
+
Plan.tableSeries({ tone: "muted", cells: Plan.tableCells(Plan.values("second", (v) => ({ value: v }))) }),
|
|
1689
1626
|
],
|
|
1690
|
-
children:
|
|
1627
|
+
children: "children", aggregate: "sum",
|
|
1691
1628
|
format: whole,
|
|
1692
1629
|
}),
|
|
1693
1630
|
// VERTICAL on a TWO-line gutter — the remaining combination,
|
|
@@ -1696,13 +1633,9 @@ export const planMeasures = example({
|
|
|
1696
1633
|
key: "overunder", title: "Over / under",
|
|
1697
1634
|
match: r => r.series.equal("overunder"),
|
|
1698
1635
|
label: r => r.label, split: "vertical", stacked: true, sub: r => r.sub,
|
|
1699
|
-
series:
|
|
1700
|
-
Plan.tableSeries({ strong: true, rollup: true, cells: Plan.tableCells(
|
|
1701
|
-
Plan.tableSeries({
|
|
1702
|
-
tone: "muted",
|
|
1703
|
-
format: Format.Number({ maximumFractionDigits: 0n, signDisplay: "always" }),
|
|
1704
|
-
cells: Plan.tableCells(weekly(r.second)),
|
|
1705
|
-
}),
|
|
1636
|
+
series: [
|
|
1637
|
+
Plan.tableSeries({ strong: true, rollup: true, cells: Plan.tableCells(Plan.values("readings", (v) => ({ value: v }))) }),
|
|
1638
|
+
Plan.tableSeries({ tone: "muted", format: signed, cells: Plan.tableCells(Plan.values("second", (v) => ({ value: v }))) }),
|
|
1706
1639
|
],
|
|
1707
1640
|
format: whole,
|
|
1708
1641
|
}),
|
|
@@ -1711,7 +1644,7 @@ export const planMeasures = example({
|
|
|
1711
1644
|
key: "stock", title: "Paper stock",
|
|
1712
1645
|
match: r => r.series.equal("stock"),
|
|
1713
1646
|
label: r => r.label,
|
|
1714
|
-
cells:
|
|
1647
|
+
cells: Plan.tableCells(Plan.values("readings", (v) => ({ value: v }))), fold: "last",
|
|
1715
1648
|
format: whole,
|
|
1716
1649
|
}),
|
|
1717
1650
|
]),
|
|
@@ -1721,7 +1654,7 @@ export const planMeasures = example({
|
|
|
1721
1654
|
// resolution, MONTH to start; the Weeks tab lists the weeks in it.
|
|
1722
1655
|
const slice = $.let(Slice.bindRecords("ex.plan.measures", {
|
|
1723
1656
|
weeks: {
|
|
1724
|
-
source: Data.bindPaged(planMeasureWeeks, { indexes: [
|
|
1657
|
+
source: Data.bindPaged(planMeasureWeeks, { indexes: ["plan_measure_weeks_by_week"] }),
|
|
1725
1658
|
label: "Weeks", fields: { week: { label: "Week", format: { date: "MMM D" } } }, range: "week",
|
|
1726
1659
|
},
|
|
1727
1660
|
}));
|
|
@@ -1738,75 +1671,83 @@ export const planMeasures = example({
|
|
|
1738
1671
|
// planRowDrop — `data`'s rows edited in place: cards dropped from the Plan's
|
|
1739
1672
|
// library panel, runs moved and resized, one checked Save (#880, #825, #1259)
|
|
1740
1673
|
// ============================================================================
|
|
1741
|
-
/** A job, its label already composed. */
|
|
1674
|
+
/** A job, under its id — its label already composed. */
|
|
1742
1675
|
export const DropJob = StructType({
|
|
1743
|
-
|
|
1676
|
+
label: StringType,
|
|
1744
1677
|
start: DateTimeType, end: DateTimeType, state: EventStateType,
|
|
1745
1678
|
});
|
|
1746
|
-
/** A van drop. */
|
|
1747
|
-
export const DropAlloc = StructType({
|
|
1748
|
-
/** A crew shift, its label already composed. */
|
|
1679
|
+
/** A van drop, under its id. */
|
|
1680
|
+
export const DropAlloc = StructType({ at: DateTimeType, state: EventStateType });
|
|
1681
|
+
/** A crew shift, under its id — its label already composed. */
|
|
1749
1682
|
export const DropShift = StructType({
|
|
1750
|
-
|
|
1683
|
+
from: DateTimeType, to: DateTimeType, label: StringType, state: EventStateType,
|
|
1751
1684
|
});
|
|
1685
|
+
/** A milestone on a stream, under its id. */
|
|
1686
|
+
export const DropMark = StructType({ at: DateTimeType, kind: Plan.Types.EventMarkKind, label: OptionType(StringType) });
|
|
1752
1687
|
/** A press a hall holds — its jobs; its name is its key. */
|
|
1753
|
-
export const DropPress = StructType({ jobs:
|
|
1754
|
-
/** One row of the ops record — `series` names the series that claims it;
|
|
1755
|
-
*
|
|
1756
|
-
*
|
|
1688
|
+
export const DropPress = StructType({ jobs: DictType(StringType, DropJob) });
|
|
1689
|
+
/** One row of the ops record — `series` names the series that claims it; its
|
|
1690
|
+
* collections are Dicts a series reads by name (#1436): the things people
|
|
1691
|
+
* drag by their ids, `readings` and `nums` a value a fortnight keyed by its
|
|
1692
|
+
* week; a hall holds its presses (`presses`), the hierarchy the data's own. */
|
|
1757
1693
|
export const DropOpsRow = StructType({
|
|
1758
1694
|
series: StringType, label: StringType,
|
|
1759
|
-
jobs:
|
|
1760
|
-
readings:
|
|
1761
|
-
allocs:
|
|
1762
|
-
nums:
|
|
1763
|
-
shifts:
|
|
1764
|
-
marks:
|
|
1695
|
+
jobs: DictType(StringType, DropJob),
|
|
1696
|
+
readings: DictType(DateTimeType, FloatType),
|
|
1697
|
+
allocs: DictType(StringType, DropAlloc),
|
|
1698
|
+
nums: DictType(DateTimeType, FloatType),
|
|
1699
|
+
shifts: DictType(StringType, DropShift),
|
|
1700
|
+
marks: DictType(StringType, DropMark),
|
|
1765
1701
|
presses: DictType(StringType, DropPress),
|
|
1766
1702
|
});
|
|
1703
|
+
/** Every collection an ops row holds, empty: a row names the few it fills. */
|
|
1704
|
+
const DROP_EMPTY = {
|
|
1705
|
+
jobs: new Map(), readings: new Map(), allocs: new Map(), nums: new Map(), shifts: new Map(), marks: new Map(), presses: new Map(),
|
|
1706
|
+
};
|
|
1707
|
+
/** A value a fortnight from W27 (Monday 29 June 2026), each keyed by its week's instant. */
|
|
1708
|
+
const fortnightly = (readings) => atInstants(readings.map((v, i) => [new Date(Date.UTC(2026, 5, 29 + 14 * i)), v]));
|
|
1767
1709
|
/**
|
|
1768
1710
|
* The ops RECORD every gesture drafts and Save commits to — its initial state
|
|
1769
1711
|
* the genesis commit. The droppable and inert kinds interleave, and Hall 3 is
|
|
1770
1712
|
* an entry holding its two presses, H3-P11 already running four jobs, so one
|
|
1771
1713
|
* more is refused.
|
|
1772
1714
|
*/
|
|
1773
|
-
export const planDropOps = e3.record("plan_drop_ops", DictType(StringType, DropOpsRow),
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
export const planDropOpsPatch = e3.mutation.patch(planDropOps);
|
|
1715
|
+
export const planDropOps = e3.record("plan_drop_ops", DictType(StringType, DropOpsRow), {
|
|
1716
|
+
initial: new Map([
|
|
1717
|
+
["util", { ...DROP_EMPTY, series: "util", label: "Util %", readings: fortnightly([46.0, 58.0, 66.0, 72.0, 84.0, 96.0]) }],
|
|
1718
|
+
["p03", { ...DROP_EMPTY, series: "press", label: "H1-P03",
|
|
1719
|
+
jobs: new Map([["j4642", { label: "RUN · J-4642", start: new Date("2026-07-06T00:00:00Z"), end: new Date("2026-07-27T00:00:00Z"), state: variant("in-progress", null) }]]) }],
|
|
1720
|
+
["p04", { ...DROP_EMPTY, series: "press", label: "H1-P04",
|
|
1721
|
+
jobs: new Map([["j4624", { label: "RUN · J-4624", start: new Date("2026-06-29T00:00:00Z"), end: new Date("2026-07-20T00:00:00Z"), state: variant("confirmed", null) }]]) }],
|
|
1722
|
+
["load", { ...DROP_EMPTY, series: "load", label: "H2 load", readings: fortnightly([46.0, 58.0, 66.0, 72.0, 84.0, 96.0]) }],
|
|
1723
|
+
["van1", { ...DROP_EMPTY, series: "van", label: "Van 1",
|
|
1724
|
+
allocs: new Map([["a1", { at: new Date("2026-07-13T00:00:00Z"), state: variant("confirmed", null) }]]) }],
|
|
1725
|
+
["desp", { ...DROP_EMPTY, series: "table", label: "Delivered · k",
|
|
1726
|
+
nums: atInstants([[new Date("2026-07-06T00:00:00Z"), 128.0], [new Date("2026-07-27T00:00:00Z"), -96.0]]) }],
|
|
1727
|
+
["crewA", { ...DROP_EMPTY, series: "crew", label: "Crew A",
|
|
1728
|
+
shifts: new Map([["s1", { from: new Date("2026-06-29T00:00:00Z"), to: new Date("2026-07-13T00:00:00Z"), label: "80h", state: variant("confirmed", null) }]]) }],
|
|
1729
|
+
["ms", { ...DROP_EMPTY, series: "strm", label: "Milestones",
|
|
1730
|
+
marks: new Map([["k", { at: new Date("2026-07-13T00:00:00Z"), kind: variant("milestone", null), label: some("KICKOFF") }]]) }],
|
|
1731
|
+
// A HALL, holding its presses: a gesture on one of them drafts the hall,
|
|
1732
|
+
// the entry they ride in. H3-P11 already runs four jobs.
|
|
1733
|
+
["hall3", { ...DROP_EMPTY, series: "hall", label: "Hall 3",
|
|
1734
|
+
presses: new Map([
|
|
1735
|
+
["H3-P11", { jobs: new Map([
|
|
1736
|
+
["j4897", { label: "RUN · J-4897", start: new Date("2026-06-29T00:00:00Z"), end: new Date("2026-07-13T00:00:00Z"), state: variant("confirmed", null) }],
|
|
1737
|
+
["j4903", { label: "RUN · J-4903", start: new Date("2026-07-20T00:00:00Z"), end: new Date("2026-08-17T00:00:00Z"), state: variant("confirmed", null) }],
|
|
1738
|
+
["j4911", { label: "RUN · J-4911", start: new Date("2026-08-17T00:00:00Z"), end: new Date("2026-08-31T00:00:00Z"), state: variant("proposed", variant("recommended", null)) }],
|
|
1739
|
+
["j4918", { label: "RUN · J-4918", start: new Date("2026-08-31T00:00:00Z"), end: new Date("2026-09-14T00:00:00Z"), state: variant("proposed", variant("recommended", null)) }],
|
|
1740
|
+
]) }],
|
|
1741
|
+
["H3-P12", { jobs: new Map([
|
|
1742
|
+
["j4925", { label: "RUN · J-4925", start: new Date("2026-07-06T00:00:00Z"), end: new Date("2026-07-27T00:00:00Z"), state: variant("confirmed", null) }],
|
|
1743
|
+
]) }],
|
|
1744
|
+
]) }],
|
|
1745
|
+
]),
|
|
1746
|
+
mutations: (mutation) => ({
|
|
1747
|
+
// The ops record's patch door — every Save commits through it.
|
|
1748
|
+
patch: mutation.patch(),
|
|
1749
|
+
}),
|
|
1750
|
+
});
|
|
1810
1751
|
/** A card in the palette — a thing a row of its family takes. */
|
|
1811
1752
|
export const DropCard = StructType({ name: StringType, family: StringType, note: StringType });
|
|
1812
1753
|
/** The palette's cards, by key — a card's key is what a drop names it by. */
|
|
@@ -1837,13 +1778,14 @@ export const planDropCards = e3.input("plan_drop_cards", DictType(StringType, Dr
|
|
|
1837
1778
|
* so the question is answered at three levels:
|
|
1838
1779
|
*
|
|
1839
1780
|
* 1. **Structurally, by series.** A card lands only on a row whose series
|
|
1840
|
-
* declares `edit` —
|
|
1841
|
-
* elements
|
|
1842
|
-
*
|
|
1843
|
-
*
|
|
1844
|
-
*
|
|
1845
|
-
*
|
|
1846
|
-
* they register no cell and never
|
|
1781
|
+
* declares `edit: { create }` — it lands in the collection the row's
|
|
1782
|
+
* elements read (`Plan.items`, #1436), under an id the Plan mints, and
|
|
1783
|
+
* `create` says what it becomes, from the drop: the card, the row and the
|
|
1784
|
+
* bucket's instant. Only the kinds holding discrete scheduled objects can
|
|
1785
|
+
* — `span` (runs), `buckets` (tiles), `events` (marks), `cards` (chips). A
|
|
1786
|
+
* `chart` / `heat` / `table` row renders DERIVED values, and section
|
|
1787
|
+
* headers and group strips are wayfinding: they register no cell and never
|
|
1788
|
+
* light up during a drag.
|
|
1847
1789
|
* 2. **By policy, with `canDrop`.** Of the rows that can receive, this canvas
|
|
1848
1790
|
* admits only the matching FAMILY: a job goes on a press, a delivery on
|
|
1849
1791
|
* a van, a shift on a crew, a milestone on a stream. The `PALLET` card
|
|
@@ -1857,13 +1799,14 @@ export const planDropCards = e3.input("plan_drop_cards", DictType(StringType, Dr
|
|
|
1857
1799
|
* door (`Record.onApply`).
|
|
1858
1800
|
*
|
|
1859
1801
|
* A press's run moves along its press or onto another press and resizes by
|
|
1860
|
-
* either end: its
|
|
1861
|
-
*
|
|
1802
|
+
* either end: its runs' read names the jobs and the times a move writes
|
|
1803
|
+
* (`Plan.items("jobs", { from: "start", to: "end" }, …)`), and the job keeps
|
|
1804
|
+
* its id wherever it goes. It moves in whole weeks, or in days with
|
|
1862
1805
|
* Shift held; from the keyboard, Space on a focused run picks it up: ←/→ move
|
|
1863
1806
|
* it a week, Shift+←/→ its end, Alt+←/→ its start, ↑/↓ carry it to another
|
|
1864
1807
|
* press, and Space drops it.
|
|
1865
1808
|
*
|
|
1866
|
-
* Hall 3 is an entry of its own, holding its presses (`Plan.children`): its
|
|
1809
|
+
* Hall 3 is an entry of its own, holding its presses (`Plan.children("presses", …)`): its
|
|
1867
1810
|
* row rolls their runs up and takes no drop, and every gesture on one of its
|
|
1868
1811
|
* presses lands on the press row, one level down, yet drafts the HALL — the
|
|
1869
1812
|
* source's top-level entry, which the whole subtree rides in. A job moved
|
|
@@ -1882,7 +1825,7 @@ export const planDropCards = e3.input("plan_drop_cards", DictType(StringType, Dr
|
|
|
1882
1825
|
export const planRowDrop = example({
|
|
1883
1826
|
keywords: [
|
|
1884
1827
|
"Plan", "library", "panel", "Plan.library.tab", "palette", "DnD", "drag", "drop", "canDrop",
|
|
1885
|
-
"edit", "items", "create", "add", "target", "surface", "cell", "slot", "row kind", "selective", "veto",
|
|
1828
|
+
"edit", "items", "Plan.items", "Plan.values", "create", "minted id", "add", "target", "surface", "cell", "slot", "row kind", "selective", "veto",
|
|
1886
1829
|
"invalid", "span", "buckets", "events", "cards", "chart", "heat", "table", "section", "row id", "row text",
|
|
1887
1830
|
"droppable", "inert", "bucket instant", "editing", "session", "onApply", "onPatch", "ready",
|
|
1888
1831
|
"Readiness", "Editing.Types.Readiness", "draft", "drafts", "transaction", "Save", "undo", "redo",
|
|
@@ -1897,7 +1840,7 @@ export const planRowDrop = example({
|
|
|
1897
1840
|
// The source is a RECORD: the canvas pages it, and each Save is
|
|
1898
1841
|
// one commit through its patch door.
|
|
1899
1842
|
const ops = $.let(Data.bindPaged(planDropOps));
|
|
1900
|
-
const record = $.let(Record.bind(planDropOps
|
|
1843
|
+
const record = $.let(Record.bind(planDropOps));
|
|
1901
1844
|
// The palette the cards come from, by key.
|
|
1902
1845
|
const cards = $.let(Data.bind(planDropCards));
|
|
1903
1846
|
const palette = $.let(cards.read());
|
|
@@ -1906,15 +1849,6 @@ export const planRowDrop = example({
|
|
|
1906
1849
|
const w1 = $.const(new Date("2025-12-29T00:00:00Z"), DateTimeType);
|
|
1907
1850
|
return w1.addWeeks(n.subtract(1n));
|
|
1908
1851
|
}));
|
|
1909
|
-
const MeasureRow = StructType({ week: DateTimeType, pct: FloatType });
|
|
1910
|
-
// A row's fortnightly readings from W27, as a chart's points and as
|
|
1911
|
-
// heat cells printing their values.
|
|
1912
|
-
const points = $.const(East.function([ArrayType(FloatType)], ArrayType(MeasureRow), ($, readings) => East.Array.generate(readings.size(), MeasureRow, (_$, i) => ({ week: week(i.multiply(2n).add(27n)), pct: readings.get(i) }))));
|
|
1913
|
-
const cells = $.const(East.function([ArrayType(FloatType)], ArrayType(Plan.Types.HeatCell), ($, readings) => East.Array.generate(readings.size(), Plan.Types.HeatCell, (_$, i) => ({
|
|
1914
|
-
at: Plan.at.time(week(i.multiply(2n).add(27n))),
|
|
1915
|
-
value: some(readings.get(i)),
|
|
1916
|
-
label: some(East.Float.printFixed(readings.get(i), 0n)),
|
|
1917
|
-
}))));
|
|
1918
1852
|
// Everything a drop creates is a PROPOSAL — it is a suggestion the
|
|
1919
1853
|
// host has not committed, and the lifecycle is how the canvas says so.
|
|
1920
1854
|
const ADDED = variant("proposed", variant("added", null));
|
|
@@ -1998,28 +1932,23 @@ export const planRowDrop = example({
|
|
|
1998
1932
|
key: "util", title: "Utilisation",
|
|
1999
1933
|
match: r => r.series.equal("util"),
|
|
2000
1934
|
label: r => r.label, id: true, height: "spark",
|
|
2001
|
-
layers:
|
|
1935
|
+
layers: [Plan.line(Plan.values("readings", (pct) => ({ y: pct })))],
|
|
2002
1936
|
}),
|
|
2003
1937
|
// RECEIVES and MOVES — runs are discrete scheduled
|
|
2004
|
-
// objects. A dropped job joins
|
|
2005
|
-
//
|
|
2006
|
-
//
|
|
2007
|
-
//
|
|
2008
|
-
//
|
|
2009
|
-
//
|
|
2010
|
-
//
|
|
1938
|
+
// objects, read by name (#1436). A dropped job joins
|
|
1939
|
+
// the press's `jobs` under the id the Plan mints, a
|
|
1940
|
+
// fortnight long from the bucket the pointer named; a
|
|
1941
|
+
// run moves and resizes — the job found by its id, the
|
|
1942
|
+
// run's own key, and a move writing the `start` and
|
|
1943
|
+
// `end` the read names. A job keeps its id wherever it
|
|
1944
|
+
// goes: a move onto a press already holding it is refused.
|
|
2011
1945
|
Plan.series.span(DropOpsRow, {
|
|
2012
1946
|
key: "press", title: "Press jobs",
|
|
2013
1947
|
match: r => r.series.equal("press"),
|
|
2014
1948
|
label: r => r.label, id: true,
|
|
2015
|
-
runs:
|
|
2016
|
-
key: j.key, start: j.start, end: j.end, label: j.label, state: j.state,
|
|
2017
|
-
})),
|
|
1949
|
+
runs: Plan.items("jobs", { from: "start", to: "end" }, (job) => ({ label: job.label, state: job.state })),
|
|
2018
1950
|
edit: {
|
|
2019
|
-
|
|
2020
|
-
key: "key", start: "start", end: "end",
|
|
2021
|
-
create: (drop, r) => ({
|
|
2022
|
-
key: East.str `drop-${drop.from.key}-${East.print(r.jobs.size())}`,
|
|
1951
|
+
create: (drop) => ({
|
|
2023
1952
|
label: palette.get(drop.from.key).name,
|
|
2024
1953
|
start: drop.at.unwrap("time"), end: drop.at.unwrap("time").addWeeks(2n),
|
|
2025
1954
|
state: ADDED,
|
|
@@ -2031,7 +1960,7 @@ export const planRowDrop = example({
|
|
|
2031
1960
|
key: "load", title: "Hall load",
|
|
2032
1961
|
match: r => r.series.equal("load"),
|
|
2033
1962
|
label: r => r.label,
|
|
2034
|
-
cells:
|
|
1963
|
+
cells: Plan.heatCells(Plan.values("readings", (pct) => ({ value: some(pct), label: some(East.Float.printFixed(pct, 0n)) })), { min: 0, max: 100 }),
|
|
2035
1964
|
}),
|
|
2036
1965
|
// RECEIVES — a dropped delivery becomes a tile in
|
|
2037
1966
|
// the bucket under the pointer.
|
|
@@ -2039,21 +1968,15 @@ export const planRowDrop = example({
|
|
|
2039
1968
|
key: "van", title: "Van drops",
|
|
2040
1969
|
match: r => r.series.equal("van"),
|
|
2041
1970
|
label: r => r.label,
|
|
2042
|
-
events:
|
|
2043
|
-
edit: {
|
|
2044
|
-
items: "allocs",
|
|
2045
|
-
create: (drop, r) => ({
|
|
2046
|
-
key: East.str `drop-${drop.from.key}-${East.print(r.allocs.size())}`,
|
|
2047
|
-
at: drop.at.unwrap("time"), state: ADDED,
|
|
2048
|
-
}),
|
|
2049
|
-
},
|
|
1971
|
+
events: Plan.items("allocs", { at: "at" }, (alloc) => ({ state: alloc.state })),
|
|
1972
|
+
edit: { create: (drop) => ({ at: drop.at.unwrap("time"), state: ADDED }) },
|
|
2050
1973
|
}),
|
|
2051
1974
|
// INERT — the cells are computed numbers.
|
|
2052
1975
|
Plan.series.table(DropOpsRow, {
|
|
2053
1976
|
key: "table", title: "Delivered sheets",
|
|
2054
1977
|
match: r => r.series.equal("table"),
|
|
2055
1978
|
label: r => r.label,
|
|
2056
|
-
cells:
|
|
1979
|
+
cells: Plan.tableCells(Plan.values("nums", (n) => ({ value: some(n) }))),
|
|
2057
1980
|
format: Format.Number({ maximumFractionDigits: 0n }),
|
|
2058
1981
|
}),
|
|
2059
1982
|
// RECEIVES — a dropped shift becomes a chip.
|
|
@@ -2061,13 +1984,9 @@ export const planRowDrop = example({
|
|
|
2061
1984
|
key: "crew", title: "Crew shifts",
|
|
2062
1985
|
match: r => r.series.equal("crew"),
|
|
2063
1986
|
label: r => r.label,
|
|
2064
|
-
chips:
|
|
2065
|
-
key: s.key, from: s.from, to: s.to, label: s.label, state: s.state,
|
|
2066
|
-
})),
|
|
1987
|
+
chips: Plan.items("shifts", { from: "from", to: "to" }, (shift) => ({ label: shift.label, state: shift.state })),
|
|
2067
1988
|
edit: {
|
|
2068
|
-
|
|
2069
|
-
create: (drop, r) => ({
|
|
2070
|
-
key: East.str `drop-${drop.from.key}-${East.print(r.shifts.size())}`,
|
|
1989
|
+
create: (drop) => ({
|
|
2071
1990
|
from: drop.at.unwrap("time"), to: drop.at.unwrap("time").addWeeks(2n),
|
|
2072
1991
|
label: palette.get(drop.from.key).name, state: ADDED,
|
|
2073
1992
|
}),
|
|
@@ -2079,20 +1998,18 @@ export const planRowDrop = example({
|
|
|
2079
1998
|
key: "strm", title: "Milestones",
|
|
2080
1999
|
match: r => r.series.equal("strm"),
|
|
2081
2000
|
label: r => r.label, id: true,
|
|
2082
|
-
marks:
|
|
2001
|
+
marks: Plan.items("marks", { at: "at" }, (mark) => ({ kind: mark.kind, label: mark.label })),
|
|
2083
2002
|
edit: {
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
key: East.str `drop-${drop.from.key}-${East.print(r.marks.size())}`,
|
|
2087
|
-
at: drop.at, kind: variant("milestone", null), icon: none,
|
|
2003
|
+
create: (drop) => ({
|
|
2004
|
+
at: drop.at.unwrap("time"), kind: variant("milestone", null),
|
|
2088
2005
|
label: some(palette.get(drop.from.key).name),
|
|
2089
2006
|
}),
|
|
2090
2007
|
},
|
|
2091
2008
|
}),
|
|
2092
2009
|
// A SECTION's header is inert. Under it, a HALL: an
|
|
2093
|
-
// entry holding its presses, stepped down into
|
|
2094
|
-
//
|
|
2095
|
-
//
|
|
2010
|
+
// entry holding its presses, stepped down into by
|
|
2011
|
+
// field — which is what lets a gesture on a press
|
|
2012
|
+
// write back into its hall. The
|
|
2096
2013
|
// hall's row rolls their runs up and takes no drop; a
|
|
2097
2014
|
// press of it receives, and its runs move — onto the
|
|
2098
2015
|
// Hall 1 presses too.
|
|
@@ -2102,18 +2019,13 @@ export const planRowDrop = example({
|
|
|
2102
2019
|
match: r => r.series.equal("hall"),
|
|
2103
2020
|
label: r => r.label,
|
|
2104
2021
|
runs: _r => [], rollup: "union",
|
|
2105
|
-
children: Plan.children(
|
|
2022
|
+
children: Plan.children("presses", [
|
|
2106
2023
|
Plan.series.span(DropPress, {
|
|
2107
2024
|
key: "gpress", title: "Hall presses",
|
|
2108
2025
|
label: (_p, k) => k, id: true,
|
|
2109
|
-
runs:
|
|
2110
|
-
key: j.key, start: j.start, end: j.end, label: j.label, state: j.state,
|
|
2111
|
-
})),
|
|
2026
|
+
runs: Plan.items("jobs", { from: "start", to: "end" }, (job) => ({ label: job.label, state: job.state })),
|
|
2112
2027
|
edit: {
|
|
2113
|
-
|
|
2114
|
-
key: "key", start: "start", end: "end",
|
|
2115
|
-
create: (drop, p) => ({
|
|
2116
|
-
key: East.str `drop-${drop.from.key}-${East.print(p.jobs.size())}`,
|
|
2028
|
+
create: (drop) => ({
|
|
2117
2029
|
label: palette.get(drop.from.key).name,
|
|
2118
2030
|
start: drop.at.unwrap("time"), end: drop.at.unwrap("time").addWeeks(2n),
|
|
2119
2031
|
state: ADDED,
|
|
@@ -2147,114 +2059,94 @@ export const generateNumberHorizon = East.function([IntegerType], ArrayType(Numb
|
|
|
2147
2059
|
* number axis's Orders tab lists, the rail's `orders` source narrowing them by
|
|
2148
2060
|
* hall, its integer range the canvas's window (#1344).
|
|
2149
2061
|
*/
|
|
2150
|
-
export const planNumberOrders = e3.record("plan_number_orders", DictType(StringType, NumberHorizonRow),
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
/** A
|
|
2162
|
-
export const
|
|
2163
|
-
/** A
|
|
2164
|
-
export const
|
|
2165
|
-
/** A
|
|
2166
|
-
export const
|
|
2167
|
-
/** A
|
|
2168
|
-
export const
|
|
2169
|
-
/** RAW rows — every instant is a plain number (a day index)
|
|
2062
|
+
export const planNumberOrders = e3.record("plan_number_orders", DictType(StringType, NumberHorizonRow), {
|
|
2063
|
+
initial: () => East.compile(East.function([IntegerType], DictType(StringType, NumberHorizonRow), ($, count) => {
|
|
2064
|
+
const generate = $.const(generateNumberHorizon);
|
|
2065
|
+
return generate(count).toDict((_$2, order) => order.key, (_$2, order) => order);
|
|
2066
|
+
}), [])(24n),
|
|
2067
|
+
indexes: (index) => ({
|
|
2068
|
+
// The orders by their day, alone and after their hall: a hall's narrowing holds the window too.
|
|
2069
|
+
plan_number_orders_by_day: index.on(["day"]),
|
|
2070
|
+
plan_number_orders_by_hall_day: index.on(["hall", "day"]),
|
|
2071
|
+
}),
|
|
2072
|
+
});
|
|
2073
|
+
/** A van drop, under its id — its day (a plain number), lane and lifecycle state. */
|
|
2074
|
+
export const NumberAlloc = StructType({ day: FloatType, lane: StringType, state: EventStateType });
|
|
2075
|
+
/** A job, under its id — its days. */
|
|
2076
|
+
export const NumberJob = StructType({ label: StringType, start: FloatType, end: FloatType, state: EventStateType });
|
|
2077
|
+
/** A crew shift, under its id — its days and hours. */
|
|
2078
|
+
export const NumberShift = StructType({ from: FloatType, to: FloatType, hours: FloatType, state: EventStateType });
|
|
2079
|
+
/** A milestone, under its id — its day. */
|
|
2080
|
+
export const NumberMark = StructType({ day: FloatType, label: StringType });
|
|
2081
|
+
/** RAW rows — every instant is a plain number (a day index): the things people drag by their ids, their days fields
|
|
2082
|
+
* of them, and a value a day keyed by its day, each a Dict a series reads by name (#1436). */
|
|
2170
2083
|
export const NumberOpsRow = StructType({
|
|
2171
2084
|
series: StringType, label: StringType, value: OptionType(StringType), sub: OptionType(StringType),
|
|
2172
|
-
allocations:
|
|
2173
|
-
marks:
|
|
2174
|
-
|
|
2085
|
+
allocations: DictType(StringType, NumberAlloc), jobs: DictType(StringType, NumberJob), shifts: DictType(StringType, NumberShift),
|
|
2086
|
+
marks: DictType(StringType, NumberMark), output: DictType(FloatType, FloatType), delivered: DictType(FloatType, FloatType),
|
|
2087
|
+
load: DictType(FloatType, FloatType),
|
|
2175
2088
|
});
|
|
2089
|
+
/** Every collection a row holds, empty: a row names the few it fills. */
|
|
2090
|
+
const NUMBER_EMPTY = {
|
|
2091
|
+
allocations: new Map(), jobs: new Map(), shifts: new Map(), marks: new Map(), output: new Map(), delivered: new Map(), load: new Map(),
|
|
2092
|
+
};
|
|
2093
|
+
/** A value a day from day 1, each keyed by its day. */
|
|
2094
|
+
const daily = (values) => new Map(values.map((v, i) => [i + 1, v]));
|
|
2176
2095
|
/** The rows — the keys are the entries' identities; the series list is the layout. */
|
|
2177
2096
|
export const planNumberOps = e3.input("plan_number_ops", DictType(StringType, NumberOpsRow), variant("value", new Map([
|
|
2178
|
-
["van1", { series: "van", label: "Van 1", value: none, sub: some("drops/day"),
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
["
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
{
|
|
2216
|
-
{
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
] }],
|
|
2224
|
-
["out", { series: "out", label: "Printed · k", value: some("612 k sheets"), sub: none,
|
|
2225
|
-
allocations: [], jobs: [], shifts: [], marks: [], sheets: [], cells: [],
|
|
2226
|
-
points: [
|
|
2227
|
-
{ day: 1.0, sheets: 60.0 }, { day: 2.0, sheets: 77.0 }, { day: 3.0, sheets: 94.0 }, { day: 4.0, sheets: 71.0 },
|
|
2228
|
-
{ day: 5.0, sheets: 88.0 }, { day: 6.0, sheets: 65.0 }, { day: 7.0, sheets: 82.0 }, { day: 8.0, sheets: 99.0 },
|
|
2229
|
-
] }],
|
|
2230
|
-
["desp", { series: "table", label: "Delivered · k", value: none, sub: none,
|
|
2231
|
-
allocations: [], jobs: [], shifts: [], marks: [], points: [], cells: [],
|
|
2232
|
-
sheets: [
|
|
2233
|
-
{ at: 1.0, value: some(80.0) }, { at: 2.0, value: some(103.0) }, { at: 3.0, value: some(126.0) }, { at: 4.0, value: some(149.0) },
|
|
2234
|
-
{ at: 5.0, value: some(102.0) }, { at: 6.0, value: some(125.0) }, { at: 7.0, value: some(148.0) }, { at: 8.0, value: some(101.0) },
|
|
2235
|
-
] }],
|
|
2236
|
-
["crewA", { series: "crew", label: "Crew A", value: none, sub: none,
|
|
2237
|
-
allocations: [], jobs: [], marks: [], points: [], sheets: [], cells: [],
|
|
2238
|
-
shifts: [
|
|
2239
|
-
{ key: "s1", from: 1.0, to: 3.0, hours: 24.0, state: variant("confirmed", null) },
|
|
2240
|
-
{ key: "s2", from: 3.0, to: 6.0, hours: 36.0, state: variant("confirmed", null) },
|
|
2241
|
-
{ key: "s3", from: 6.0, to: 8.0, hours: 24.0, state: variant("proposed", variant("recommended", null)) },
|
|
2242
|
-
] }],
|
|
2243
|
-
["ms", { series: "ms", label: "Milestones", value: none, sub: none,
|
|
2244
|
-
allocations: [], jobs: [], shifts: [], points: [], sheets: [], cells: [],
|
|
2245
|
-
marks: [
|
|
2246
|
-
{ key: "kick", day: 2.0, label: "KICKOFF" },
|
|
2247
|
-
{ key: "rel", day: 6.0, label: "REL" },
|
|
2248
|
-
] }],
|
|
2097
|
+
["van1", { ...NUMBER_EMPTY, series: "van", label: "Van 1", value: none, sub: some("drops/day"),
|
|
2098
|
+
allocations: new Map([
|
|
2099
|
+
["a1", { day: 1.0, lane: "am", state: variant("confirmed", null) }],
|
|
2100
|
+
["a2", { day: 1.0, lane: "pm", state: variant("confirmed", null) }],
|
|
2101
|
+
["a3", { day: 2.0, lane: "am", state: variant("confirmed", null) }],
|
|
2102
|
+
["a4", { day: 3.0, lane: "pm", state: variant("proposed", variant("recommended", null)) }],
|
|
2103
|
+
["a5", { day: 5.0, lane: "am", state: variant("proposed", variant("recommended", null)) }],
|
|
2104
|
+
["a6", { day: 6.0, lane: "pm", state: variant("confirmed", null) }],
|
|
2105
|
+
["a7", { day: 8.0, lane: "am", state: variant("proposed", variant("recommended", null)) }],
|
|
2106
|
+
]) }],
|
|
2107
|
+
["van2", { ...NUMBER_EMPTY, series: "van", label: "Van 2", value: none, sub: some("drops/day"),
|
|
2108
|
+
allocations: new Map([
|
|
2109
|
+
["b1", { day: 2.0, lane: "pm", state: variant("confirmed", null) }],
|
|
2110
|
+
["b2", { day: 4.0, lane: "am", state: variant("confirmed", null) }],
|
|
2111
|
+
["b3", { day: 4.0, lane: "pm", state: variant("proposed", variant("recommended", null)) }],
|
|
2112
|
+
["b4", { day: 7.0, lane: "am", state: variant("proposed", variant("recommended", null)) }],
|
|
2113
|
+
]) }],
|
|
2114
|
+
["p03", { ...NUMBER_EMPTY, series: "press", label: "H1-P03", value: some("12k/h"), sub: none,
|
|
2115
|
+
jobs: new Map([
|
|
2116
|
+
["mr", { label: "MAKEREADY", start: 1.0, end: 2.0, state: variant("actual", null) }],
|
|
2117
|
+
["j4642", { label: "RUN · J-4642", start: 2.0, end: 5.0, state: variant("in-progress", null) }],
|
|
2118
|
+
["j4663", { label: "RUN · J-4663", start: 6.0, end: 8.0, state: variant("proposed", variant("recommended", null)) }],
|
|
2119
|
+
]) }],
|
|
2120
|
+
["p04", { ...NUMBER_EMPTY, series: "press", label: "H1-P04", value: some("8k/h"), sub: none,
|
|
2121
|
+
jobs: new Map([
|
|
2122
|
+
["j4624", { label: "RUN · J-4624", start: 1.0, end: 4.0, state: variant("actual", null) }],
|
|
2123
|
+
["proof", { label: "PROOF", start: 4.0, end: 5.0, state: variant("confirmed", null) }],
|
|
2124
|
+
["j4693", { label: "RUN · J-4693", start: 5.0, end: 9.0, state: variant("proposed", variant("recommended", null)) }],
|
|
2125
|
+
]) }],
|
|
2126
|
+
["load", { ...NUMBER_EMPTY, series: "load", label: "Hall load", value: none, sub: none, load: daily([40.0, 69.0, 43.0, 72.0, 46.0, 75.0, 49.0, 78.0]) }],
|
|
2127
|
+
["out", { ...NUMBER_EMPTY, series: "out", label: "Printed · k", value: some("612 k sheets"), sub: none,
|
|
2128
|
+
output: daily([60.0, 77.0, 94.0, 71.0, 88.0, 65.0, 82.0, 99.0]) }],
|
|
2129
|
+
["desp", { ...NUMBER_EMPTY, series: "table", label: "Delivered · k", value: none, sub: none,
|
|
2130
|
+
delivered: daily([80.0, 103.0, 126.0, 149.0, 102.0, 125.0, 148.0, 101.0]) }],
|
|
2131
|
+
["crewA", { ...NUMBER_EMPTY, series: "crew", label: "Crew A", value: none, sub: none,
|
|
2132
|
+
shifts: new Map([
|
|
2133
|
+
["s1", { from: 1.0, to: 3.0, hours: 24.0, state: variant("confirmed", null) }],
|
|
2134
|
+
["s2", { from: 3.0, to: 6.0, hours: 36.0, state: variant("confirmed", null) }],
|
|
2135
|
+
["s3", { from: 6.0, to: 8.0, hours: 24.0, state: variant("proposed", variant("recommended", null)) }],
|
|
2136
|
+
]) }],
|
|
2137
|
+
["ms", { ...NUMBER_EMPTY, series: "ms", label: "Milestones", value: none, sub: none,
|
|
2138
|
+
marks: new Map([
|
|
2139
|
+
["kick", { day: 2.0, label: "KICKOFF" }],
|
|
2140
|
+
["rel", { day: 6.0, label: "REL" }],
|
|
2141
|
+
]) }],
|
|
2249
2142
|
])));
|
|
2250
2143
|
/**
|
|
2251
2144
|
* The `number` axis — the retired Planner's `plannerPoint` canvas on the
|
|
2252
2145
|
* Plan: eight days at step 1, AM/PM lanes in the bucket rows, `now` at day
|
|
2253
2146
|
* 5. Every kind positions on the one numeric scale, and every instant in the
|
|
2254
|
-
* raw data is a plain number: a
|
|
2255
|
-
*
|
|
2256
|
-
*
|
|
2257
|
-
* reads a numeric `at`, and the heat cells stored as RECORDS spell it out with
|
|
2147
|
+
* raw data is a plain number, read by name (#1436): a Float time field of the
|
|
2148
|
+
* things people drag (`Plan.items("jobs", { from: "start", to: "end" }, …)`),
|
|
2149
|
+
* and the Float key of a value a day (`Plan.values("load", …)`), each lands on
|
|
2258
2150
|
* the `number` arm. One rail (`Slice.bindRecords`, #1344): its `orders`
|
|
2259
2151
|
* source — twelve days of orders behind the eight-day window, a record of
|
|
2260
2152
|
* their own — narrows the library's Orders tab by hall, and its range field is
|
|
@@ -2265,10 +2157,10 @@ export const planNumberOps = e3.input("plan_number_ops", DictType(StringType, Nu
|
|
|
2265
2157
|
*/
|
|
2266
2158
|
export const planNumberAxis = example({
|
|
2267
2159
|
keywords: [
|
|
2268
|
-
"Plan", "axis", "number", "numeric", "step", "Plan.axis.number", "Plan.
|
|
2160
|
+
"Plan", "axis", "number", "numeric", "step", "Plan.axis.number", "Plan.items", "Plan.values", "instant",
|
|
2269
2161
|
"day", "AM", "PM", "lanes", "buckets", "Planner", "plannerPoint", "brush", "integer", "range", "half-open",
|
|
2270
2162
|
"format", "now", "typed axis", "#631", "raw", "Data.bind", "bound", "e3.input", "e3.record",
|
|
2271
|
-
"Slice.bindRecords", "source", "Data.bindPaged", "
|
|
2163
|
+
"Slice.bindRecords", "source", "Data.bindPaged", "indexes", "library", "Plan.library.tab", "#1344",
|
|
2272
2164
|
],
|
|
2273
2165
|
description: "The number axis over rows bound from e3 — day 1..8 at step 1 with AM/PM lanes (the retired Planner's plannerPoint), every row kind on the numeric scale; one Slice.bindRecords rail whose orders source narrows the Orders tab by hall and holds the window, the horizon brush over its integer range",
|
|
2274
2166
|
fn: East.function([], UIComponentType, (_$) => {
|
|
@@ -2285,51 +2177,49 @@ export const planNumberAxis = example({
|
|
|
2285
2177
|
label: r => r.label, sub: r => r.sub,
|
|
2286
2178
|
// The Planner's AM/PM lanes — sub-slots of each day column.
|
|
2287
2179
|
lanes: _r => [Plan.lane({ key: "am", label: "AM" }), Plan.lane({ key: "pm", label: "PM" })],
|
|
2288
|
-
// `
|
|
2289
|
-
events:
|
|
2180
|
+
// `day` is a Float field of each drop — the read places it on the number arm.
|
|
2181
|
+
events: Plan.items("allocations", { at: "day" }, (alloc) => ({ lane: alloc.lane, state: alloc.state })),
|
|
2290
2182
|
}),
|
|
2291
2183
|
]),
|
|
2292
2184
|
Plan.series.span(NumberOpsRow, {
|
|
2293
2185
|
key: "press", title: "Presses",
|
|
2294
2186
|
match: r => r.series.equal("press"),
|
|
2295
2187
|
label: r => r.label, id: true, value: r => r.value,
|
|
2296
|
-
runs:
|
|
2188
|
+
runs: Plan.items("jobs", { from: "start", to: "end" }, (job) => ({ label: job.label, state: job.state })),
|
|
2297
2189
|
}),
|
|
2298
2190
|
Plan.series.heat(NumberOpsRow, {
|
|
2299
2191
|
key: "load", title: "Hall load",
|
|
2300
2192
|
match: r => r.series.equal("load"),
|
|
2301
2193
|
label: r => r.label,
|
|
2302
|
-
|
|
2194
|
+
// A value a day, keyed by its Float day — each cell on the number arm.
|
|
2195
|
+
cells: Plan.heatCells(Plan.values("load", (pct) => ({ value: pct, label: some(East.Float.printFixed(pct, 0n)) })), { min: 0, max: 100, warnAt: 90 }),
|
|
2303
2196
|
}),
|
|
2304
2197
|
Plan.series.chart(NumberOpsRow, {
|
|
2305
2198
|
key: "out", title: "Output",
|
|
2306
2199
|
match: r => r.series.equal("out"),
|
|
2307
2200
|
label: r => r.label, id: true, value: r => r.value, height: "expanded",
|
|
2308
|
-
|
|
2309
|
-
layers: r => [Chart.Column(r.points, { x: p => p.day, y: p => p.sheets })],
|
|
2201
|
+
layers: [Plan.column(Plan.values("output", (sheets) => ({ y: sheets })))],
|
|
2310
2202
|
}),
|
|
2311
2203
|
Plan.series.table(NumberOpsRow, {
|
|
2312
2204
|
key: "table", title: "Deliveries",
|
|
2313
2205
|
match: r => r.series.equal("table"),
|
|
2314
2206
|
label: r => r.label,
|
|
2315
|
-
|
|
2316
|
-
cells: r => Plan.tableCells(r.sheets),
|
|
2207
|
+
cells: Plan.tableCells(Plan.values("delivered", (sheets) => ({ value: sheets }))),
|
|
2317
2208
|
format: Format.Number({ maximumFractionDigits: 0n }),
|
|
2318
2209
|
}),
|
|
2319
2210
|
Plan.series.cards(NumberOpsRow, {
|
|
2320
2211
|
key: "crew", title: "Crews",
|
|
2321
2212
|
match: r => r.series.equal("crew"),
|
|
2322
2213
|
label: r => r.label,
|
|
2323
|
-
chips:
|
|
2324
|
-
|
|
2325
|
-
label: East.str `${East.Float.printFixed(s.hours, 0n)}h`, state: s.state,
|
|
2214
|
+
chips: Plan.items("shifts", { from: "from", to: "to" }, (shift) => ({
|
|
2215
|
+
label: East.str `${East.Float.printFixed(shift.hours, 0n)}h`, state: shift.state,
|
|
2326
2216
|
})),
|
|
2327
2217
|
}),
|
|
2328
2218
|
Plan.series.events(NumberOpsRow, {
|
|
2329
2219
|
key: "ms", title: "Milestones",
|
|
2330
2220
|
match: r => r.series.equal("ms"),
|
|
2331
2221
|
label: r => r.label, id: true,
|
|
2332
|
-
marks:
|
|
2222
|
+
marks: Plan.items("marks", { at: "day" }, (mark) => ({ kind: "milestone", label: mark.label })),
|
|
2333
2223
|
}),
|
|
2334
2224
|
], ArrayType(Plan.Types.Series(NumberOpsRow)));
|
|
2335
2225
|
// The orders' range field is an INTEGER day, so the brush, the
|
|
@@ -2337,7 +2227,7 @@ export const planNumberAxis = example({
|
|
|
2337
2227
|
// half-open range — the eight days `[1, 9)`.
|
|
2338
2228
|
const slice = $.let(Slice.bindRecords("ex.plan.number", {
|
|
2339
2229
|
orders: {
|
|
2340
|
-
source: Data.bindPaged(planNumberOrders, { indexes: [
|
|
2230
|
+
source: Data.bindPaged(planNumberOrders, { indexes: ["plan_number_orders_by_day", "plan_number_orders_by_hall_day"] }),
|
|
2341
2231
|
label: "Orders", icon: { prefix: "fas", name: "box" },
|
|
2342
2232
|
fields: { hall: { label: "Hall", hints: ["Hall 1", "Hall 2"] }, day: { label: "Day" } },
|
|
2343
2233
|
range: "day", search: [],
|
|
@@ -2363,100 +2253,78 @@ export const planNumberAxis = example({
|
|
|
2363
2253
|
// ============================================================================
|
|
2364
2254
|
// planOrdinalAxis — the `ordinal` axis (#631): workflow phases
|
|
2365
2255
|
// ============================================================================
|
|
2366
|
-
/** A job — the phases it spans, by name. */
|
|
2367
|
-
export const OrdinalJob = StructType({
|
|
2368
|
-
/** A bindery slot — the phase it sits in. */
|
|
2369
|
-
export const OrdinalAlloc = StructType({
|
|
2370
|
-
/** A crew shift — the phases it covers. */
|
|
2371
|
-
export const OrdinalShift = StructType({
|
|
2372
|
-
/** A gate — its phase, and whether it holds the work. */
|
|
2373
|
-
export const OrdinalMark = StructType({
|
|
2374
|
-
/**
|
|
2375
|
-
|
|
2376
|
-
/** A table reading — a phase and its value. */
|
|
2377
|
-
export const OrdinalReading = StructType({ at: StringType, value: OptionType(FloatType) });
|
|
2378
|
-
/** RAW rows — every instant is a phase NAME. */
|
|
2256
|
+
/** A job, under its id — the phases it spans, by name. */
|
|
2257
|
+
export const OrdinalJob = StructType({ label: StringType, start: StringType, end: StringType, state: EventStateType });
|
|
2258
|
+
/** A bindery slot, under its id — the phase it sits in. */
|
|
2259
|
+
export const OrdinalAlloc = StructType({ phase: StringType, state: EventStateType });
|
|
2260
|
+
/** A crew shift, under its id — the phases it covers. */
|
|
2261
|
+
export const OrdinalShift = StructType({ from: StringType, to: StringType, label: StringType, state: EventStateType });
|
|
2262
|
+
/** A gate, under its id — its phase, and whether it holds the work. */
|
|
2263
|
+
export const OrdinalMark = StructType({ phase: StringType, label: StringType, exception: BooleanType });
|
|
2264
|
+
/** RAW rows — every instant is a phase NAME: the things people drag by their ids, their phases fields of them, and a
|
|
2265
|
+
* value a phase keyed by the phase, each a Dict a series reads by name (#1436). */
|
|
2379
2266
|
export const OrdinalOrderRow = StructType({
|
|
2380
2267
|
series: StringType, label: StringType, value: OptionType(StringType), sub: OptionType(StringType),
|
|
2381
|
-
jobs:
|
|
2382
|
-
marks:
|
|
2383
|
-
|
|
2268
|
+
jobs: DictType(StringType, OrdinalJob), allocations: DictType(StringType, OrdinalAlloc), shifts: DictType(StringType, OrdinalShift),
|
|
2269
|
+
marks: DictType(StringType, OrdinalMark), wip: DictType(StringType, FloatType), counts: DictType(StringType, OptionType(FloatType)),
|
|
2270
|
+
load: DictType(StringType, FloatType),
|
|
2384
2271
|
});
|
|
2272
|
+
/** Every collection a row holds, empty: a row names the few it fills. */
|
|
2273
|
+
const ORDINAL_EMPTY = {
|
|
2274
|
+
jobs: new Map(), allocations: new Map(), shifts: new Map(), marks: new Map(), wip: new Map(), counts: new Map(), load: new Map(),
|
|
2275
|
+
};
|
|
2385
2276
|
/** The jobs, bindery slots, loads and gates of a six-phase workflow. */
|
|
2386
2277
|
export const planOrdinalOrders = e3.input("plan_ordinal_orders", DictType(StringType, OrdinalOrderRow), variant("value", new Map([
|
|
2387
|
-
["j6188", { series: "job", label: "J-6188", value: some("96 k sheets"), sub: none,
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
["
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
{
|
|
2412
|
-
{
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
points: [
|
|
2420
|
-
{ phase: "PREPRESS", n: 4.0 }, { phase: "PLATES", n: 11.0 }, { phase: "PRINT", n: 18.0 },
|
|
2421
|
-
{ phase: "FINISH", n: 5.0 }, { phase: "BIND", n: 12.0 }, { phase: "DELIVER", n: 19.0 },
|
|
2422
|
-
] }],
|
|
2423
|
-
["count", { series: "count", label: "Jobs in phase", value: none, sub: none,
|
|
2424
|
-
jobs: [], allocations: [], shifts: [], marks: [], points: [], cells: [],
|
|
2425
|
-
counts: [
|
|
2426
|
-
{ at: "PREPRESS", value: some(12.0) }, { at: "PLATES", value: some(23.0) }, { at: "PRINT", value: some(34.0) },
|
|
2427
|
-
{ at: "FINISH", value: some(15.0) }, { at: "BIND", value: none }, { at: "DELIVER", value: some(37.0) },
|
|
2428
|
-
] }],
|
|
2429
|
-
["crew", { series: "crew", label: "Crew B", value: none, sub: none,
|
|
2430
|
-
jobs: [], allocations: [], marks: [], points: [], counts: [], cells: [],
|
|
2431
|
-
shifts: [
|
|
2432
|
-
{ key: "s1", from: "PREPRESS", to: "PLATES", label: "prepress crew", state: variant("confirmed", null) },
|
|
2433
|
-
{ key: "s2", from: "PRINT", to: "DELIVER", label: "+ bindery crew", state: variant("proposed", variant("recommended", null)) },
|
|
2434
|
-
] }],
|
|
2435
|
-
["gates", { series: "gates", label: "Gates", value: none, sub: none,
|
|
2436
|
-
jobs: [], allocations: [], shifts: [], points: [], counts: [], cells: [],
|
|
2437
|
-
marks: [
|
|
2438
|
-
{ key: "g1", phase: "FINISH", label: "HOLD", exception: true },
|
|
2439
|
-
{ key: "g2", phase: "DELIVER", label: "RELEASE", exception: false },
|
|
2440
|
-
] }],
|
|
2278
|
+
["j6188", { ...ORDINAL_EMPTY, series: "job", label: "J-6188", value: some("96 k sheets"), sub: none,
|
|
2279
|
+
jobs: new Map([
|
|
2280
|
+
["plates", { label: "PLATES", start: "PREPRESS", end: "PLATES", state: variant("actual", null) }],
|
|
2281
|
+
["print", { label: "PRINT · J-4642", start: "PRINT", end: "FINISH", state: variant("in-progress", null) }],
|
|
2282
|
+
["deliver", { label: "BIND + DELIVER", start: "BIND", end: "DELIVER", state: variant("proposed", variant("recommended", null)) }],
|
|
2283
|
+
]) }],
|
|
2284
|
+
["j6204", { ...ORDINAL_EMPTY, series: "job", label: "J-6204", value: some("54 k sheets"), sub: none,
|
|
2285
|
+
jobs: new Map([
|
|
2286
|
+
["prepress", { label: "PREPRESS", start: "PREPRESS", end: "PREPRESS", state: variant("actual", null) }],
|
|
2287
|
+
["print", { label: "PRINT · J-4663", start: "PLATES", end: "BIND", state: variant("proposed", variant("recommended", null)) }],
|
|
2288
|
+
]) }],
|
|
2289
|
+
["bindery", { ...ORDINAL_EMPTY, series: "bindery", label: "Bindery 2", value: none, sub: some("slots"),
|
|
2290
|
+
allocations: new Map([
|
|
2291
|
+
["a1", { phase: "PLATES", state: variant("confirmed", null) }], ["a2", { phase: "PRINT", state: variant("confirmed", null) }],
|
|
2292
|
+
["a3", { phase: "PRINT", state: variant("proposed", variant("recommended", null)) }], ["a4", { phase: "BIND", state: variant("proposed", variant("recommended", null)) }],
|
|
2293
|
+
]) }],
|
|
2294
|
+
["load", { ...ORDINAL_EMPTY, series: "load", label: "Phase load", value: none, sub: none,
|
|
2295
|
+
load: new Map([["PREPRESS", 35.0], ["PLATES", 66.0], ["PRINT", 37.0], ["FINISH", 68.0], ["BIND", 39.0], ["DELIVER", 70.0]]) }],
|
|
2296
|
+
["wip", { ...ORDINAL_EMPTY, series: "wip", label: "WIP · jobs", value: some("31"), sub: none,
|
|
2297
|
+
wip: new Map([["PREPRESS", 4.0], ["PLATES", 11.0], ["PRINT", 18.0], ["FINISH", 5.0], ["BIND", 12.0], ["DELIVER", 19.0]]) }],
|
|
2298
|
+
["count", { ...ORDINAL_EMPTY, series: "count", label: "Jobs in phase", value: none, sub: none,
|
|
2299
|
+
counts: new Map([["PREPRESS", some(12.0)], ["PLATES", some(23.0)], ["PRINT", some(34.0)], ["FINISH", some(15.0)], ["BIND", none], ["DELIVER", some(37.0)]]) }],
|
|
2300
|
+
["crew", { ...ORDINAL_EMPTY, series: "crew", label: "Crew B", value: none, sub: none,
|
|
2301
|
+
shifts: new Map([
|
|
2302
|
+
["s1", { from: "PREPRESS", to: "PLATES", label: "prepress crew", state: variant("confirmed", null) }],
|
|
2303
|
+
["s2", { from: "PRINT", to: "DELIVER", label: "+ bindery crew", state: variant("proposed", variant("recommended", null)) }],
|
|
2304
|
+
]) }],
|
|
2305
|
+
["gates", { ...ORDINAL_EMPTY, series: "gates", label: "Gates", value: none, sub: none,
|
|
2306
|
+
marks: new Map([
|
|
2307
|
+
["g1", { phase: "FINISH", label: "HOLD", exception: true }],
|
|
2308
|
+
["g2", { phase: "DELIVER", label: "RELEASE", exception: false }],
|
|
2309
|
+
]) }],
|
|
2441
2310
|
])));
|
|
2442
2311
|
/**
|
|
2443
2312
|
* The `ordinal` axis — a workflow of six phases, each a bucket, in declared
|
|
2444
|
-
* order. Instants are the phase VALUES: a run's `start`
|
|
2445
|
-
* names (an interval covers `[start, end]` in phase order —
|
|
2446
|
-
* axis the end names the LAST bucket covered, since values are
|
|
2447
|
-
* edges), a tile
|
|
2448
|
-
*
|
|
2449
|
-
* record written as data spells its phase on the `ordinal` arm. There is no
|
|
2313
|
+
* order. Instants are the phase VALUES, read by name (#1436): a run's `start`
|
|
2314
|
+
* / `end` are phase names (an interval covers `[start, end]` in phase order —
|
|
2315
|
+
* on an ordinal axis the end names the LAST bucket covered, since values are
|
|
2316
|
+
* buckets, not edges), a tile and a gate sit in a phase, and a value a phase is
|
|
2317
|
+
* keyed by the phase's name — a load cell, a column, a count. There is no
|
|
2450
2318
|
* slice range for an ordinal axis — the list IS the window, so the horizon
|
|
2451
2319
|
* brush does not mount and the window keys idle; `now` names a phase.
|
|
2452
2320
|
*/
|
|
2453
2321
|
export const planOrdinalAxis = example({
|
|
2454
2322
|
keywords: [
|
|
2455
2323
|
"Plan", "axis", "ordinal", "phase", "phases", "workflow", "stage", "Plan.axis.ordinal",
|
|
2456
|
-
"Plan.
|
|
2324
|
+
"Plan.items", "Plan.values", "instant", "values", "list", "span", "buckets", "heat", "chart", "table",
|
|
2457
2325
|
"cards", "events", "Planner", "typed axis", "#631", "raw", "Data.bind", "bound", "e3.input",
|
|
2458
2326
|
],
|
|
2459
|
-
description: "The ordinal axis over rows bound from e3 — six workflow phases as the buckets, in declared order; jobs span phases, tiles and
|
|
2327
|
+
description: "The ordinal axis over rows bound from e3 — six workflow phases as the buckets, in declared order; jobs span phases, tiles and gates sit in them, and a value a phase, keyed by the phase, lands a heat cell, a column and a count in each",
|
|
2460
2328
|
fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
|
|
2461
2329
|
const orders = $.let(Data.bind(planOrdinalOrders));
|
|
2462
2330
|
const PHASES = $.const(["PREPRESS", "PLATES", "PRINT", "FINISH", "BIND", "DELIVER"], ArrayType(StringType));
|
|
@@ -2468,49 +2336,48 @@ export const planOrdinalAxis = example({
|
|
|
2468
2336
|
key: "job", title: "Jobs",
|
|
2469
2337
|
match: r => r.series.equal("job"),
|
|
2470
2338
|
label: r => r.label, id: true, value: r => r.value,
|
|
2471
|
-
// `
|
|
2472
|
-
runs:
|
|
2339
|
+
// `start` / `end` are String fields of each job — the read places them on the ordinal arm.
|
|
2340
|
+
runs: Plan.items("jobs", { from: "start", to: "end" }, (job) => ({ label: job.label, state: job.state })),
|
|
2473
2341
|
}),
|
|
2474
2342
|
]),
|
|
2475
2343
|
Plan.series.buckets(OrdinalOrderRow, {
|
|
2476
2344
|
key: "bindery", title: "Bindery",
|
|
2477
2345
|
match: r => r.series.equal("bindery"),
|
|
2478
2346
|
label: r => r.label, sub: r => r.sub,
|
|
2479
|
-
events:
|
|
2347
|
+
events: Plan.items("allocations", { at: "phase" }, (alloc) => ({ state: alloc.state })),
|
|
2480
2348
|
}),
|
|
2481
2349
|
Plan.series.heat(OrdinalOrderRow, {
|
|
2482
2350
|
key: "load", title: "Phase load",
|
|
2483
2351
|
match: r => r.series.equal("load"),
|
|
2484
2352
|
label: r => r.label,
|
|
2485
|
-
|
|
2353
|
+
// A value a phase, keyed by the phase's name — each cell in its phase.
|
|
2354
|
+
cells: Plan.heatCells(Plan.values("load", (pct) => ({ value: pct, label: some(East.Float.printFixed(pct, 0n)) })), { min: 0, max: 100, warnAt: 90 }),
|
|
2486
2355
|
}),
|
|
2487
2356
|
Plan.series.chart(OrdinalOrderRow, {
|
|
2488
2357
|
key: "wip", title: "WIP",
|
|
2489
2358
|
match: r => r.series.equal("wip"),
|
|
2490
2359
|
label: r => r.label, id: true, value: r => r.value, height: "expanded",
|
|
2491
|
-
|
|
2492
|
-
layers: r => [Chart.Column(r.points, { x: p => p.phase, y: p => p.n })],
|
|
2360
|
+
layers: [Plan.column(Plan.values("wip", (n) => ({ y: n })))],
|
|
2493
2361
|
}),
|
|
2494
2362
|
Plan.series.table(OrdinalOrderRow, {
|
|
2495
2363
|
key: "count", title: "Counts",
|
|
2496
2364
|
match: r => r.series.equal("count"),
|
|
2497
2365
|
label: r => r.label,
|
|
2498
|
-
cells:
|
|
2366
|
+
cells: Plan.tableCells(Plan.values("counts", (n) => ({ value: n }))),
|
|
2499
2367
|
format: Format.Number({ maximumFractionDigits: 0n }),
|
|
2500
2368
|
}),
|
|
2501
2369
|
Plan.series.cards(OrdinalOrderRow, {
|
|
2502
2370
|
key: "crew", title: "Crews",
|
|
2503
2371
|
match: r => r.series.equal("crew"),
|
|
2504
2372
|
label: r => r.label,
|
|
2505
|
-
chips:
|
|
2373
|
+
chips: Plan.items("shifts", { from: "from", to: "to" }, (shift) => ({ label: shift.label, state: shift.state })),
|
|
2506
2374
|
}),
|
|
2507
2375
|
Plan.series.events(OrdinalOrderRow, {
|
|
2508
2376
|
key: "gates", title: "Gates",
|
|
2509
2377
|
match: r => r.series.equal("gates"),
|
|
2510
2378
|
label: r => r.label, id: true,
|
|
2511
|
-
marks:
|
|
2512
|
-
|
|
2513
|
-
kind: m.exception.ifElse(() => EXCEPTION, () => MILESTONE),
|
|
2379
|
+
marks: Plan.items("marks", { at: "phase" }, (gate) => ({
|
|
2380
|
+
label: gate.label, kind: gate.exception.ifElse(() => EXCEPTION, () => MILESTONE),
|
|
2514
2381
|
})),
|
|
2515
2382
|
}),
|
|
2516
2383
|
], ArrayType(Plan.Types.Series(OrdinalOrderRow)));
|
|
@@ -2530,18 +2397,22 @@ export const planOrdinalAxis = example({
|
|
|
2530
2397
|
/** A task on the plan — its owner and its window. */
|
|
2531
2398
|
export const SliceJob = StructType({ task: StringType, owner: StringType, start: DateTimeType, end: DateTimeType });
|
|
2532
2399
|
/** The plan's tasks, by name — a record of their own, the rows the slice's `rows` source narrows (#1344). */
|
|
2533
|
-
export const planSliceJobs = e3.record("plan_slice_jobs", DictType(StringType, SliceJob),
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2400
|
+
export const planSliceJobs = e3.record("plan_slice_jobs", DictType(StringType, SliceJob), {
|
|
2401
|
+
initial: new Map([
|
|
2402
|
+
["Planning", { task: "Planning", owner: "Team A", start: new Date("2024-01-01T00:00:00Z"), end: new Date("2024-01-15T00:00:00Z") }],
|
|
2403
|
+
["Design", { task: "Design", owner: "Team B", start: new Date("2024-01-10T00:00:00Z"), end: new Date("2024-02-01T00:00:00Z") }],
|
|
2404
|
+
["Development", { task: "Development", owner: "Team C", start: new Date("2024-01-20T00:00:00Z"), end: new Date("2024-03-15T00:00:00Z") }],
|
|
2405
|
+
["Testing", { task: "Testing", owner: "Team A", start: new Date("2024-03-01T00:00:00Z"), end: new Date("2024-03-30T00:00:00Z") }],
|
|
2406
|
+
]),
|
|
2407
|
+
indexes: (index) => ({
|
|
2408
|
+
// The tasks over the interval each runs, alone and after its owner: a range of dates the rail chooses reads every task
|
|
2409
|
+
// overlapping it once, an owner's beside it too.
|
|
2410
|
+
plan_slice_jobs_by_time: index.interval({ from: "start", to: "end" }),
|
|
2411
|
+
plan_slice_jobs_by_owner_time: index.interval({ on: ["owner"], from: "start", to: "end" }),
|
|
2412
|
+
}),
|
|
2413
|
+
});
|
|
2543
2414
|
export const slicePlanChrome = example({
|
|
2544
|
-
keywords: ["Slice", "Slice.bindRecords", "rows", "Plan", "slice", "rail", "filter", "range", "brush", "timeline", "Data.bindPaged", "
|
|
2415
|
+
keywords: ["Slice", "Slice.bindRecords", "rows", "Plan", "slice", "rail", "filter", "range", "brush", "timeline", "Data.bindPaged", "indexes", "interval", "e3.record", "#1344"],
|
|
2545
2416
|
description: "Ops plan — a Plan narrowed through one Slice.bindRecords rail over tasks bound from e3: its `rows` source gives the canvas its rows, paged through the tasks' interval index and narrowed by owner and by their dates — fields of the rail the viewer sets — while the window is the canvas's own: the toolbar's dates and the horizon brush move it, and never decide which tasks are listed",
|
|
2546
2417
|
fn: East.function([], UIComponentType, (_$) => {
|
|
2547
2418
|
return (_jsx(Reactive, { children: $ => {
|
|
@@ -2549,7 +2420,7 @@ export const slicePlanChrome = example({
|
|
|
2549
2420
|
// the rail, the viewer's to set: the dates on screen never narrow the tasks.
|
|
2550
2421
|
const slice = $.let(Slice.bindRecords("ex.slice.plan.chrome", {
|
|
2551
2422
|
rows: {
|
|
2552
|
-
source: Data.bindPaged(planSliceJobs, { indexes: [
|
|
2423
|
+
source: Data.bindPaged(planSliceJobs, { indexes: ["plan_slice_jobs_by_time", "plan_slice_jobs_by_owner_time"] }),
|
|
2553
2424
|
label: "Tasks",
|
|
2554
2425
|
fields: { owner: { label: "Owner", hints: ["Team A", "Team B", "Team C"] }, start: { label: "Start" } },
|
|
2555
2426
|
search: [],
|