@elaraai/e3-ui 1.0.93 → 1.0.95

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.
Files changed (303) hide show
  1. package/dist/src/allocation/adapters.d.ts +159 -0
  2. package/dist/src/allocation/adapters.d.ts.map +1 -0
  3. package/dist/src/allocation/adapters.js +241 -0
  4. package/dist/src/allocation/adapters.js.map +1 -0
  5. package/dist/src/allocation/functions.d.ts +664 -0
  6. package/dist/src/allocation/functions.d.ts.map +1 -0
  7. package/dist/src/allocation/functions.js +782 -0
  8. package/dist/src/allocation/functions.js.map +1 -0
  9. package/dist/src/allocation/index.d.ts +2985 -0
  10. package/dist/src/allocation/index.d.ts.map +1 -0
  11. package/dist/src/allocation/index.js +499 -0
  12. package/dist/src/allocation/index.js.map +1 -0
  13. package/dist/src/allocation/types.d.ts +832 -0
  14. package/dist/src/allocation/types.d.ts.map +1 -0
  15. package/dist/src/allocation/types.js +360 -0
  16. package/dist/src/allocation/types.js.map +1 -0
  17. package/dist/src/bind/func.d.ts +14 -0
  18. package/dist/src/bind/func.d.ts.map +1 -1
  19. package/dist/src/bind/func.js +22 -1
  20. package/dist/src/bind/func.js.map +1 -1
  21. package/dist/src/calendar/index.d.ts +510 -1639
  22. package/dist/src/calendar/index.d.ts.map +1 -1
  23. package/dist/src/calendar/index.js +56 -29
  24. package/dist/src/calendar/index.js.map +1 -1
  25. package/dist/src/flowchart/canvas.d.ts +9 -21
  26. package/dist/src/flowchart/canvas.d.ts.map +1 -1
  27. package/dist/src/flowchart/canvas.js +10 -22
  28. package/dist/src/flowchart/canvas.js.map +1 -1
  29. package/dist/src/flowchart/flowchart.d.ts +19 -13
  30. package/dist/src/flowchart/flowchart.d.ts.map +1 -1
  31. package/dist/src/flowchart/flowchart.js.map +1 -1
  32. package/dist/src/flowchart/index.d.ts +44 -21
  33. package/dist/src/flowchart/index.d.ts.map +1 -1
  34. package/dist/src/flowchart/index.js +43 -20
  35. package/dist/src/flowchart/index.js.map +1 -1
  36. package/dist/src/flowchart/library.d.ts +166 -81
  37. package/dist/src/flowchart/library.d.ts.map +1 -1
  38. package/dist/src/flowchart/library.js +232 -105
  39. package/dist/src/flowchart/library.js.map +1 -1
  40. package/dist/src/flowchart/over.d.ts +9 -16
  41. package/dist/src/flowchart/over.d.ts.map +1 -1
  42. package/dist/src/flowchart/over.js +9 -16
  43. package/dist/src/flowchart/over.js.map +1 -1
  44. package/dist/src/flowchart/payload.d.ts +710 -508
  45. package/dist/src/flowchart/payload.d.ts.map +1 -1
  46. package/dist/src/flowchart/payload.js +128 -74
  47. package/dist/src/flowchart/payload.js.map +1 -1
  48. package/dist/src/flowchart/window.d.ts +132 -347
  49. package/dist/src/flowchart/window.d.ts.map +1 -1
  50. package/dist/src/flowchart/window.js +48 -55
  51. package/dist/src/flowchart/window.js.map +1 -1
  52. package/dist/src/index.d.ts +11 -7
  53. package/dist/src/index.d.ts.map +1 -1
  54. package/dist/src/index.js +14 -6
  55. package/dist/src/index.js.map +1 -1
  56. package/dist/src/internal.d.ts +18 -7
  57. package/dist/src/internal.d.ts.map +1 -1
  58. package/dist/src/internal.js +28 -9
  59. package/dist/src/internal.js.map +1 -1
  60. package/dist/src/plan/event-rows.d.ts +26 -10
  61. package/dist/src/plan/event-rows.d.ts.map +1 -1
  62. package/dist/src/plan/event-rows.js +39 -13
  63. package/dist/src/plan/event-rows.js.map +1 -1
  64. package/dist/src/plan/ir.d.ts +244 -815
  65. package/dist/src/plan/ir.d.ts.map +1 -1
  66. package/dist/src/plan/ir.js +8 -6
  67. package/dist/src/plan/ir.js.map +1 -1
  68. package/dist/src/plan/library.d.ts +3 -0
  69. package/dist/src/plan/library.d.ts.map +1 -1
  70. package/dist/src/plan/library.js +1 -1
  71. package/dist/src/plan/library.js.map +1 -1
  72. package/dist/src/plan/ordered-source.d.ts +1 -0
  73. package/dist/src/plan/ordered-source.d.ts.map +1 -1
  74. package/dist/src/plan/plan.d.ts +559 -1667
  75. package/dist/src/plan/plan.d.ts.map +1 -1
  76. package/dist/src/plan/plan.js +123 -45
  77. package/dist/src/plan/plan.js.map +1 -1
  78. package/dist/src/plan/root.d.ts +16 -20
  79. package/dist/src/plan/root.d.ts.map +1 -1
  80. package/dist/src/plan/root.js +23 -33
  81. package/dist/src/plan/root.js.map +1 -1
  82. package/dist/src/roster/coverage.d.ts +17 -231
  83. package/dist/src/roster/coverage.d.ts.map +1 -1
  84. package/dist/src/roster/coverage.js +94 -90
  85. package/dist/src/roster/coverage.js.map +1 -1
  86. package/dist/src/roster/fit.d.ts +59 -0
  87. package/dist/src/roster/fit.d.ts.map +1 -0
  88. package/dist/src/roster/fit.js +155 -0
  89. package/dist/src/roster/fit.js.map +1 -0
  90. package/dist/src/roster/index.d.ts +2862 -3662
  91. package/dist/src/roster/index.d.ts.map +1 -1
  92. package/dist/src/roster/index.js +383 -81
  93. package/dist/src/roster/index.js.map +1 -1
  94. package/dist/src/roster/library.d.ts +37 -92
  95. package/dist/src/roster/library.d.ts.map +1 -1
  96. package/dist/src/roster/library.js +25 -64
  97. package/dist/src/roster/library.js.map +1 -1
  98. package/dist/src/roster/lists.d.ts +195 -0
  99. package/dist/src/roster/lists.d.ts.map +1 -0
  100. package/dist/src/roster/lists.js +233 -0
  101. package/dist/src/roster/lists.js.map +1 -0
  102. package/dist/src/roster/rules.d.ts +17 -260
  103. package/dist/src/roster/rules.d.ts.map +1 -1
  104. package/dist/src/roster/rules.js +135 -132
  105. package/dist/src/roster/rules.js.map +1 -1
  106. package/dist/src/roster/targets.d.ts +115 -0
  107. package/dist/src/roster/targets.d.ts.map +1 -0
  108. package/dist/src/roster/targets.js +213 -0
  109. package/dist/src/roster/targets.js.map +1 -0
  110. package/dist/src/roster/times.d.ts +251 -0
  111. package/dist/src/roster/times.d.ts.map +1 -0
  112. package/dist/src/roster/times.js +174 -0
  113. package/dist/src/roster/times.js.map +1 -0
  114. package/dist/src/roster/types.d.ts +828 -183
  115. package/dist/src/roster/types.d.ts.map +1 -1
  116. package/dist/src/roster/types.js +224 -97
  117. package/dist/src/roster/types.js.map +1 -1
  118. package/dist/src/schedule/accessor.d.ts +28 -0
  119. package/dist/src/schedule/accessor.d.ts.map +1 -0
  120. package/dist/src/schedule/accessor.js +39 -0
  121. package/dist/src/schedule/accessor.js.map +1 -0
  122. package/dist/src/schedule/events.d.ts +170 -42
  123. package/dist/src/schedule/events.d.ts.map +1 -1
  124. package/dist/src/schedule/events.js +431 -171
  125. package/dist/src/schedule/events.js.map +1 -1
  126. package/dist/src/schedule/index.d.ts +41 -4
  127. package/dist/src/schedule/index.d.ts.map +1 -1
  128. package/dist/src/schedule/index.js +35 -3
  129. package/dist/src/schedule/index.js.map +1 -1
  130. package/dist/src/schedule/library.d.ts +13 -3
  131. package/dist/src/schedule/library.d.ts.map +1 -1
  132. package/dist/src/schedule/library.js +11 -4
  133. package/dist/src/schedule/library.js.map +1 -1
  134. package/dist/src/schedule/periods.d.ts +115 -0
  135. package/dist/src/schedule/periods.d.ts.map +1 -0
  136. package/dist/src/schedule/periods.js +215 -0
  137. package/dist/src/schedule/periods.js.map +1 -0
  138. package/dist/src/schedule/record-window.d.ts +25 -3
  139. package/dist/src/schedule/record-window.d.ts.map +1 -1
  140. package/dist/src/schedule/record-window.js +31 -18
  141. package/dist/src/schedule/record-window.js.map +1 -1
  142. package/dist/src/schedule/resources.d.ts +82 -4
  143. package/dist/src/schedule/resources.d.ts.map +1 -1
  144. package/dist/src/schedule/resources.js +44 -14
  145. package/dist/src/schedule/resources.js.map +1 -1
  146. package/dist/src/schedule/session.d.ts +69 -0
  147. package/dist/src/schedule/session.d.ts.map +1 -0
  148. package/dist/src/schedule/session.js +121 -0
  149. package/dist/src/schedule/session.js.map +1 -0
  150. package/dist/src/schedule/templates.d.ts +14 -2
  151. package/dist/src/schedule/templates.d.ts.map +1 -1
  152. package/dist/src/schedule/templates.js +5 -3
  153. package/dist/src/schedule/templates.js.map +1 -1
  154. package/dist/src/schedule/types.d.ts +1671 -37
  155. package/dist/src/schedule/types.d.ts.map +1 -1
  156. package/dist/src/schedule/types.js +196 -5
  157. package/dist/src/schedule/types.js.map +1 -1
  158. package/dist/src/schedule/window.d.ts +14 -0
  159. package/dist/src/schedule/window.d.ts.map +1 -1
  160. package/dist/src/schedule/window.js +14 -4
  161. package/dist/src/schedule/window.js.map +1 -1
  162. package/dist/src/schedule/words.d.ts +22 -0
  163. package/dist/src/schedule/words.d.ts.map +1 -0
  164. package/dist/src/schedule/words.js +25 -0
  165. package/dist/src/schedule/words.js.map +1 -0
  166. package/dist/src/sheet/index.d.ts +61 -27
  167. package/dist/src/sheet/index.d.ts.map +1 -1
  168. package/dist/src/sheet/index.js +59 -25
  169. package/dist/src/sheet/index.js.map +1 -1
  170. package/dist/src/sheet/library.d.ts +21 -3
  171. package/dist/src/sheet/library.d.ts.map +1 -1
  172. package/dist/src/sheet/library.js +23 -6
  173. package/dist/src/sheet/library.js.map +1 -1
  174. package/dist/src/sheet/registers.d.ts +100 -85
  175. package/dist/src/sheet/registers.d.ts.map +1 -1
  176. package/dist/src/sheet/registers.js +100 -85
  177. package/dist/src/sheet/registers.js.map +1 -1
  178. package/dist/src/sheet/root.d.ts +8 -12
  179. package/dist/src/sheet/root.d.ts.map +1 -1
  180. package/dist/src/sheet/root.js +6 -40
  181. package/dist/src/sheet/root.js.map +1 -1
  182. package/dist/src/sheet/sheet.d.ts +598 -2349
  183. package/dist/src/sheet/sheet.d.ts.map +1 -1
  184. package/dist/src/sheet/sheet.js +124 -37
  185. package/dist/src/sheet/sheet.js.map +1 -1
  186. package/dist/src/sheet/sub-rows.d.ts +74 -28
  187. package/dist/src/sheet/sub-rows.d.ts.map +1 -1
  188. package/dist/src/sheet/sub-rows.js +74 -28
  189. package/dist/src/sheet/sub-rows.js.map +1 -1
  190. package/dist/src/sheet/templates.d.ts +98 -34
  191. package/dist/src/sheet/templates.d.ts.map +1 -1
  192. package/dist/src/sheet/templates.js +102 -58
  193. package/dist/src/sheet/templates.js.map +1 -1
  194. package/dist/src/sheet/types.d.ts +289 -1359
  195. package/dist/src/sheet/types.d.ts.map +1 -1
  196. package/dist/src/sheet/types.js +20 -26
  197. package/dist/src/sheet/types.js.map +1 -1
  198. package/dist/src/sheet/views.d.ts +10 -11
  199. package/dist/src/sheet/views.d.ts.map +1 -1
  200. package/dist/src/sheet/views.js +15 -20
  201. package/dist/src/sheet/views.js.map +1 -1
  202. package/dist/src/{roster/visibility.d.ts → utils/key-visibility.d.ts} +42 -7
  203. package/dist/src/utils/key-visibility.d.ts.map +1 -0
  204. package/dist/src/utils/key-visibility.js +36 -0
  205. package/dist/src/utils/key-visibility.js.map +1 -0
  206. package/dist/src/utils/ordered-source.d.ts +1 -0
  207. package/dist/src/utils/ordered-source.d.ts.map +1 -1
  208. package/dist/src/utils/ordered-source.js +8 -3
  209. package/dist/src/utils/ordered-source.js.map +1 -1
  210. package/dist/src/utils/record-sources.d.ts +107 -0
  211. package/dist/src/utils/record-sources.d.ts.map +1 -0
  212. package/dist/src/utils/record-sources.js +132 -0
  213. package/dist/src/utils/record-sources.js.map +1 -0
  214. package/dist/test/allocation/allocation.examples.d.ts +2902 -0
  215. package/dist/test/allocation/allocation.examples.d.ts.map +1 -0
  216. package/dist/test/allocation/allocation.examples.js +643 -0
  217. package/dist/test/allocation/allocation.examples.js.map +1 -0
  218. package/dist/test/allocation/fit.examples.d.ts +546 -0
  219. package/dist/test/allocation/fit.examples.d.ts.map +1 -0
  220. package/dist/test/allocation/fit.examples.js +171 -0
  221. package/dist/test/allocation/fit.examples.js.map +1 -0
  222. package/dist/test/allocation/kiln-yard.d.ts +181 -0
  223. package/dist/test/allocation/kiln-yard.d.ts.map +1 -0
  224. package/dist/test/allocation/kiln-yard.js +228 -0
  225. package/dist/test/allocation/kiln-yard.js.map +1 -0
  226. package/dist/test/bind/data/data.examples.d.ts +651 -1
  227. package/dist/test/bind/data/data.examples.d.ts.map +1 -1
  228. package/dist/test/bind/data/data.examples.js +140 -2
  229. package/dist/test/bind/data/data.examples.js.map +1 -1
  230. package/dist/test/bind/func/func.examples.d.ts +10 -1
  231. package/dist/test/bind/func/func.examples.d.ts.map +1 -1
  232. package/dist/test/bind/func/func.examples.js +8 -1
  233. package/dist/test/bind/func/func.examples.js.map +1 -1
  234. package/dist/test/bind/record/record.examples.d.ts +633 -1
  235. package/dist/test/bind/record/record.examples.d.ts.map +1 -1
  236. package/dist/test/bind/record/record.examples.js +26 -2
  237. package/dist/test/bind/record/record.examples.js.map +1 -1
  238. package/dist/test/calendar/calendar-records.examples.d.ts.map +1 -1
  239. package/dist/test/calendar/calendar-records.examples.js +18 -14
  240. package/dist/test/calendar/calendar-records.examples.js.map +1 -1
  241. package/dist/test/calendar/calendar.examples.d.ts +521 -16
  242. package/dist/test/calendar/calendar.examples.d.ts.map +1 -1
  243. package/dist/test/calendar/calendar.examples.js +182 -135
  244. package/dist/test/calendar/calendar.examples.js.map +1 -1
  245. package/dist/test/examples-package.d.ts +75 -0
  246. package/dist/test/examples-package.d.ts.map +1 -0
  247. package/dist/test/examples-package.js +263 -0
  248. package/dist/test/examples-package.js.map +1 -0
  249. package/dist/test/flowchart/flowchart.examples.d.ts +211 -19
  250. package/dist/test/flowchart/flowchart.examples.d.ts.map +1 -1
  251. package/dist/test/flowchart/flowchart.examples.js +102 -75
  252. package/dist/test/flowchart/flowchart.examples.js.map +1 -1
  253. package/dist/test/plan/plan-events.examples.d.ts +1439 -209
  254. package/dist/test/plan/plan-events.examples.d.ts.map +1 -1
  255. package/dist/test/plan/plan-events.examples.js +199 -59
  256. package/dist/test/plan/plan-events.examples.js.map +1 -1
  257. package/dist/test/plan/plan-records.examples.d.ts +420 -0
  258. package/dist/test/plan/plan-records.examples.d.ts.map +1 -1
  259. package/dist/test/plan/plan-records.examples.js +70 -19
  260. package/dist/test/plan/plan-records.examples.js.map +1 -1
  261. package/dist/test/plan/plan-windows.examples.d.ts +636 -55
  262. package/dist/test/plan/plan-windows.examples.d.ts.map +1 -1
  263. package/dist/test/plan/plan-windows.examples.js +69 -33
  264. package/dist/test/plan/plan-windows.examples.js.map +1 -1
  265. package/dist/test/plan/plan.examples.d.ts +153 -31
  266. package/dist/test/plan/plan.examples.d.ts.map +1 -1
  267. package/dist/test/plan/plan.examples.js +178 -101
  268. package/dist/test/plan/plan.examples.js.map +1 -1
  269. package/dist/test/roster/domain.examples.d.ts +467 -250
  270. package/dist/test/roster/domain.examples.d.ts.map +1 -1
  271. package/dist/test/roster/domain.examples.js +92 -29
  272. package/dist/test/roster/domain.examples.js.map +1 -1
  273. package/dist/test/roster/roster-records.examples.d.ts +430 -586
  274. package/dist/test/roster/roster-records.examples.d.ts.map +1 -1
  275. package/dist/test/roster/roster-records.examples.js +102 -43
  276. package/dist/test/roster/roster-records.examples.js.map +1 -1
  277. package/dist/test/roster/roster.examples.d.ts +2319 -606
  278. package/dist/test/roster/roster.examples.d.ts.map +1 -1
  279. package/dist/test/roster/roster.examples.js +456 -148
  280. package/dist/test/roster/roster.examples.js.map +1 -1
  281. package/dist/test/roster/warehouse.d.ts +172 -63
  282. package/dist/test/roster/warehouse.d.ts.map +1 -1
  283. package/dist/test/roster/warehouse.js +172 -39
  284. package/dist/test/roster/warehouse.js.map +1 -1
  285. package/dist/test/schedule/schedule.examples.d.ts +3499 -0
  286. package/dist/test/schedule/schedule.examples.d.ts.map +1 -0
  287. package/dist/test/schedule/schedule.examples.js +192 -0
  288. package/dist/test/schedule/schedule.examples.js.map +1 -0
  289. package/dist/test/sheet/sheet-records.examples.d.ts.map +1 -1
  290. package/dist/test/sheet/sheet-records.examples.js +22 -18
  291. package/dist/test/sheet/sheet-records.examples.js.map +1 -1
  292. package/dist/test/sheet/sheet.examples.d.ts +715 -26
  293. package/dist/test/sheet/sheet.examples.d.ts.map +1 -1
  294. package/dist/test/sheet/sheet.examples.js +204 -130
  295. package/dist/test/sheet/sheet.examples.js.map +1 -1
  296. package/package.json +17 -10
  297. package/dist/src/roster/people.d.ts +0 -50
  298. package/dist/src/roster/people.d.ts.map +0 -1
  299. package/dist/src/roster/people.js +0 -32
  300. package/dist/src/roster/people.js.map +0 -1
  301. package/dist/src/roster/visibility.d.ts.map +0 -1
  302. package/dist/src/roster/visibility.js +0 -25
  303. package/dist/src/roster/visibility.js.map +0 -1
@@ -1,198 +1,506 @@
1
- import { jsx as _jsx } from "@elaraai/e3-ui/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@elaraai/e3-ui/jsx-runtime";
2
2
  /**
3
3
  * Copyright (c) 2025 Elara AI Pty Ltd
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, DateTimeType, DictType, East, FunctionType, NullType, StringType, StructType, example, some } from "@elaraai/east";
7
+ import { ArrayType, BooleanType, DateTimeType, DictType, East, FloatType, FunctionType, IntegerType, NullType, OptionType, PatchType, StringType, StructType, example, none, some, variant, } from "@elaraai/east";
8
8
  import e3 from "@elaraai/e3";
9
9
  import { Box, Field, Reactive, Slice, UIComponentType, VStack } from "@elaraai/east-ui";
10
- import { Data, Record, Roster } from "@elaraai/e3-ui";
11
- import { WarehouseConfig, WarehousePerson, warehouseConfig, warehouseForecast, warehousePeople, warehouseProposals, warehouseWeeks } from "./warehouse.js";
12
- export const rosterWeeks = e3.record("roster_weeks", Roster.Types.Weeks, warehouseWeeks);
10
+ import { Data, Func, Record, Roster, Schedule } from "@elaraai/e3-ui";
11
+ import { WarehouseActivity, WarehouseAgency, WarehouseCover, WarehouseDuty, WarehousePerson, WarehousePosition, WarehouseShift, WarehouseSuggestion, WarehouseTeam, WarehouseWeek, warehouseActivities, warehouseAgencies, warehouseCover, warehouseDuties, warehousePayRates, warehousePeople, warehousePositions, warehouseShifts, warehouseSuggestions, warehouseTeams, warehouseWeeks, } from "./warehouse.js";
12
+ // The warehouse's row types, and its seeded weeks as the Roster reads them, for the renderer's specs on e3 (#1368).
13
+ export { WarehouseCover, WarehouseDuty, WarehouseRosterWeek, warehouseNextStart, warehousePrevious, warehousePreviousStart, warehouseStart, warehouseWeek, } from "./warehouse.js";
14
+ // The warehouse's own records (`Builder Interfaces Spec.md` §5.7): its duties, the weeks of its rota and its cover
15
+ // targets, which the Roster writes, each through its patch door; and what it reads — its suggestions, its pay rates,
16
+ // its staff and agencies, and its shift times, teams, positions and activities.
17
+ /** The duties, both weeks', each the warehouse's own row. */
18
+ export const rosterDuties = e3.record("roster_duties", DictType(StringType, WarehouseDuty), warehouseDuties);
19
+ export const rosterDutiesPatch = e3.mutation.patch(rosterDuties);
20
+ /** The duties by their times: what the Roster's slice reads a week of them through, its navigation writing the range. */
21
+ export const rosterDutiesByTime = e3.recordIndex("roster_duties_by_time", rosterDuties, { interval: { from: "start", to: "end" } });
22
+ /** The weeks of the rota: the one before issued, this one open. */
23
+ export const rosterWeeks = e3.record("roster_rota_weeks", DictType(DateTimeType, WarehouseWeek), warehouseWeeks);
13
24
  export const rosterWeeksPatch = e3.mutation.patch(rosterWeeks);
25
+ /** The cover targets, a row per slot: the week before's, this week's and the forecast's for the week after. */
26
+ export const rosterCover = e3.record("roster_cover", DictType(StringType, WarehouseCover), warehouseCover);
27
+ export const rosterCoverPatch = e3.mutation.patch(rosterCover);
28
+ /** The suggestions: someone for a slot, and why. */
29
+ export const rosterSuggestions = e3.record("roster_suggestions", DictType(StringType, WarehouseSuggestion), warehouseSuggestions);
30
+ /** The pay rates, an hour, by the warehouse's own pay codes. */
31
+ export const rosterPayRates = e3.record("roster_pay_rates", DictType(StringType, FloatType), warehousePayRates);
14
32
  export const rosterStaff = e3.record("roster_staff", DictType(StringType, WarehousePerson), warehousePeople);
15
- export const rosterConfiguration = e3.record("roster_configuration", WarehouseConfig, warehouseConfig);
16
- export const rosterForecast = e3.record("roster_forecast", DictType(DateTimeType, Roster.Types.Requirements), warehouseForecast);
17
- export const rosterProposals = e3.record("roster_proposals", ArrayType(Roster.Types.Proposal), warehouseProposals);
33
+ export const rosterAgencies = e3.record("roster_agencies", DictType(StringType, WarehouseAgency), warehouseAgencies);
34
+ export const rosterShiftTimes = e3.record("roster_shift_times", DictType(StringType, WarehouseShift), warehouseShifts);
35
+ export const rosterTeams = e3.record("roster_teams", DictType(StringType, WarehouseTeam), warehouseTeams);
36
+ export const rosterPositions = e3.record("roster_positions", DictType(StringType, WarehousePosition), warehousePositions);
37
+ /** Every activity by its key: each group's skills, and the duty of none (#1342). */
38
+ export const rosterActivities = e3.record("roster_activities", DictType(StringType, WarehouseActivity), warehouseActivities);
39
+ // The rail's fields, each on an index of its own: the staff by group then name, position, trainer, agency, skill and name, the activities by group.
40
+ export const rosterStaffByTeamName = e3.recordIndex("roster_staff_by_team_name", rosterStaff, { on: ["team", "name"] });
41
+ export const rosterStaffByPosition = e3.recordIndex("roster_staff_by_position", rosterStaff, { on: ["position"] });
42
+ export const rosterStaffByTrainer = e3.recordIndex("roster_staff_by_trainer", rosterStaff, { on: ["trainer"] });
43
+ export const rosterStaffByAgency = e3.recordIndex("roster_staff_by_agency", rosterStaff, { on: ["agency"] });
44
+ export const rosterStaffBySkill = e3.recordIndex("roster_staff_by_skill", rosterStaff, { on: ["skills"] });
45
+ export const rosterStaffByName = e3.recordIndex("roster_staff_by_name", rosterStaff, { on: ["name"] });
46
+ export const rosterActivitiesByGroup = e3.recordIndex("roster_activities_by_group", rosterActivities, { on: ["group"] });
47
+ /** The warehouse's fit's body: the ready-made rule, then a greedy order — the people off that day with the most spare contract hours first, whatever their group. */
48
+ export const rosterStaffFitBody = East.function([Roster.Types.FitRequest(WarehouseDuty)], Roster.Types.FitResult, ($, req) => {
49
+ const answer = $.const(Roster.answer(WarehouseDuty));
50
+ const rule = $.const(Roster.fit(WarehouseDuty));
51
+ const weekHours = $.const(Roster.weekHours(WarehouseDuty));
52
+ // Every person's hours this week, once: what is left of each one's contract ranks them.
53
+ const hours = $.const(weekHours(req.week, req.context));
54
+ return answer(req, East.function([Roster.Types.FitRequest(WarehouseDuty), Roster.Types.Person], Roster.Types.Fit, ($2, request, person) => {
55
+ const verdict = $2.const(rule(request, person));
56
+ $2.match(verdict, { refused: ($3) => { $3.return(verdict); } });
57
+ const fits = $2.const(verdict.unwrap("fits"));
58
+ return variant("fits", { score: person.contract.subtract(hours.get(person.key, () => 0.0)), why: fits.why, warn: fits.warn });
59
+ }));
60
+ });
61
+ // The warehouse's fit, on e3: who fits a Find fix's need on its slot.
62
+ export const rosterStaffFit = e3.function("roster_staff_fit", rosterStaffFitBody);
63
+ // What the renderer's tests on e3 write and ask beside the examples (#1376): another session's edits of the
64
+ // reference data, a roster the warehouse has locked, and another app's fit over the same staff.
65
+ /** The staff's patch door: another session's edit of the staff. */
66
+ export const rosterStaffPatch = e3.mutation.patch(rosterStaff);
67
+ /** The shift times' patch door: another session's edit of them. */
68
+ export const rosterShiftTimesPatch = e3.mutation.patch(rosterShiftTimes);
69
+ /** The teams' patch door: another session's edit of them. */
70
+ export const rosterTeamsPatch = e3.mutation.patch(rosterTeams);
71
+ /** The positions' patch door: another session's edit of them. */
72
+ export const rosterPositionsPatch = e3.mutation.patch(rosterPositions);
73
+ /** The suggestions' patch door: another session's edit of them. */
74
+ export const rosterSuggestionsPatch = e3.mutation.patch(rosterSuggestions);
75
+ /** The duties of a roster the warehouse has locked: the warehouse's own duties, which no Save changes. */
76
+ export const rosterLockedDuties = e3.record("roster_locked_duties", DictType(StringType, WarehouseDuty), warehouseDuties);
77
+ /** The locked duties by their times: what a roster over them reads a week of them through. */
78
+ export const rosterLockedDutiesByTime = e3.recordIndex("roster_locked_duties_by_time", rosterLockedDuties, { interval: { from: "start", to: "end" } });
79
+ /** The locked roster's door, a checked one: it refuses every change, naming why — what a Save over it is answered. */
80
+ export const rosterLockedDutiesPatch = e3.mutation.reduce("patch", rosterLockedDuties, East.function([DictType(StringType, WarehouseDuty), PatchType(DictType(StringType, WarehouseDuty))], DictType(StringType, WarehouseDuty), ($, duties, _patch) => {
81
+ $.error("the roster is locked for the week");
82
+ return duties;
83
+ }));
84
+ /**
85
+ * Another app's fit over the same staff: the warehouse's order — the most spare contract hours first, whatever
86
+ * their group — and a warning on whoever the slot's shift would take past their contract.
87
+ */
88
+ export const rosterContractFitBody = East.function([Roster.Types.FitRequest(WarehouseDuty)], Roster.Types.FitResult, ($, req) => {
89
+ const answer = $.const(Roster.answer(WarehouseDuty));
90
+ const rule = $.const(Roster.fit(WarehouseDuty));
91
+ const weekHours = $.const(Roster.weekHours(WarehouseDuty));
92
+ const hours = $.const(weekHours(req.week, req.context));
93
+ // What the slot's shift adds to a person's week.
94
+ const shifts = $.const(req.context.shifts.toDict((_$2, shift) => shift.key, (_$2, shift) => shift.hours));
95
+ const shift = $.const(shifts.get(req.slot.shift, () => 0.0));
96
+ return answer(req, East.function([Roster.Types.FitRequest(WarehouseDuty), Roster.Types.Person], Roster.Types.Fit, ($2, request, person) => {
97
+ const verdict = $2.const(rule(request, person));
98
+ $2.match(verdict, { refused: ($3) => { $3.return(verdict); } });
99
+ const fits = $2.const(verdict.unwrap("fits"));
100
+ const spare = $2.const(person.contract.subtract(hours.get(person.key, () => 0.0)));
101
+ const warn = $2.const(East.less(spare, shift).ifElse(() => some("past contract"), () => none), OptionType(StringType));
102
+ return variant("fits", { score: spare, why: fits.why, warn });
103
+ }));
104
+ });
105
+ // Another app's fit, on e3.
106
+ export const rosterContractFit = e3.function("roster_contract_fit", rosterContractFitBody);
18
107
  export const rosterWarehouse = example({
19
- inputs: [], description: 'The mock warehouse roster: 69 bound staff, three groups and three shifts, live configuration and proposals, a People/Activities/Positions library, shared Slice filtering, typed inspector, and whole-week Save and Publish.',
20
- keywords: ["Roster", "Record.bind", "Roster.people", "Slice", "Slice.bind", "Slice.rows", "BuilderFrame", "mobile", "Save", "Publish", "FieldForm"],
108
+ inputs: [], description: "The mock warehouse roster over its own records: its duties, the weeks of its rota, its cover targets and suggestions, 69 staff and an agency, its shift times, teams, positions and activities. One Slice.bindRecords rail over the week's duties, the staff and the activities; the warehouse's pay in its own codes and its check over the pay; Publish; a Positions tab; the inspector; and the warehouse's greedy fit on e3, which a Find fix asks who fits.",
109
+ keywords: ["Roster", "Schedule.events", "Schedule.resources", "Schedule.periods", "Roster.targets", "Roster.shifts", "Roster.groups", "Roster.positions", "Roster.activities", "Roster.proposals", "pay", "check", "Slice", "Slice.bindRecords", "Data.bindPaged", "Func.bind", "e3.function", "fit", "Find", "BuilderFrame", "Save", "Publish", "FieldForm"],
21
110
  fn: East.function([], UIComponentType, _$ => _jsx(Reactive, { children: $ => {
111
+ // What the Roster writes: the duties, the weeks of the rota and the cover targets, each through its patch door.
112
+ const rota = $.let(Record.bind(rosterDuties, [rosterDutiesPatch]));
113
+ const entries = $.let(Data.bindPaged(rosterDuties));
114
+ const byTime = $.let(Data.bindPaged(rosterDuties, { indexes: [rosterDutiesByTime] }));
22
115
  const weeks = $.let(Record.bind(rosterWeeks, [rosterWeeksPatch]));
23
- const staff = $.let(Data.bind(rosterStaff));
24
- const configuration = $.let(Data.bind(rosterConfiguration));
25
- const forecast = $.let(Data.bind(rosterForecast));
26
- const proposals = $.let(Data.bind(rosterProposals));
27
- const cfg = $.const(configuration.read());
28
- const rows = $.let(staff.read().toArray((_$2, person, key) => ({ key, name: person.name, team: person.team, position: person.position, agency: person.agency, trainer: person.trainer })));
29
- const slice = $.let(Slice.bind("ex.rosterWarehouse.scope", rows, {
30
- fields: { name: { label: "Name" }, team: { label: "Group" }, position: { label: "Position" }, agency: { label: "Agency" }, trainer: { label: "Trainer" } },
31
- search: ["name", "team", "position"],
116
+ const cover = $.let(Record.bind(rosterCover, [rosterCoverPatch]));
117
+ // Every person and activity is the calculation scope; the narrowing only chooses which show (#1342).
118
+ const people = $.let(Data.bind(rosterStaff));
119
+ const acts = $.let(Data.bind(rosterActivities));
120
+ const staffIndex = $.let(Data.bindPaged(rosterStaff, { indexes: [rosterStaffByTeamName, rosterStaffByPosition, rosterStaffByTrainer, rosterStaffByAgency, rosterStaffBySkill, rosterStaffByName] }));
121
+ const actIndex = $.let(Data.bindPaged(rosterActivities, { indexes: [rosterActivitiesByGroup] }));
122
+ const slice = $.let(Slice.bindRecords("ex.rosterWarehouse", {
123
+ // The week's duties: the Roster's navigation writes the range; no chip picks the week.
124
+ duty: { source: byTime, range: "start" },
125
+ people: { source: staffIndex, icon: { prefix: "fas", name: "user" }, search: ["name", "skills"],
126
+ fields: { team: { label: "Group", hints: ["inbound", "picking", "dispatch"] }, position: { label: "Position", hints: ["lead", "deputy", "acting", "crew"] },
127
+ trainer: { label: "Trainer" }, agency: { label: "Agency" }, skills: { label: "Skill" }, name: { label: "Name" } } },
128
+ activities: { source: actIndex, icon: { prefix: "fas", name: "list-check" }, fields: { group: { label: "Group", hints: ["inbound", "picking", "dispatch", null] } } },
129
+ }, { presets: [{ id: "leads", name: "Leads", where: { people: { position: ["lead", "deputy", "acting"] } } }] }));
130
+ const firms = $.let(Data.bind(rosterAgencies));
131
+ const times = $.let(Data.bind(rosterShiftTimes));
132
+ const teams = $.let(Data.bind(rosterTeams));
133
+ const roles = $.let(Data.bind(rosterPositions));
134
+ const ideas = $.let(Data.bind(rosterSuggestions));
135
+ const rateRows = $.let(Data.bind(rosterPayRates));
136
+ const rates = $.const(rateRows.read());
137
+ // The app's fit: a Find fix asks it who fits, over every person, and the People tab shows its answer.
138
+ const fit = $.let(Func.bind(rosterStaffFit));
139
+ // The warehouse's pay (§5.6), in its own codes. One person's duties for the week in, or one agency's requests;
140
+ // each duty's cost by code out: ordinary time up to the person's contracted hours in the week, OT1 past them,
141
+ // and an agency's hours — its named worker's, or its request's — at its own rate.
142
+ const pay = $.const(East.function([Roster.Types.PayRequest(WarehouseDuty)], Roster.Types.Pay, ($2, req) => {
143
+ const out = $2.let(new Map(), Roster.Types.Pay);
144
+ const zero = $2.const(0.0, FloatType);
145
+ const agency = $2.let(true, BooleanType);
146
+ const contract = $2.let(0.0, FloatType);
147
+ $2.match(req.who, { person: ($3, key) => {
148
+ $3.if(req.context.people.has(key), ($4) => {
149
+ const person = $4.const(req.context.people.get(key));
150
+ $4.assign(agency, person.agency);
151
+ $4.assign(contract, person.contract);
152
+ });
153
+ } });
154
+ const worked = $2.let(0.0, FloatType);
155
+ // The week's duties in time order, so the contract's hours run out where they do.
156
+ const ordered = $2.const(req.duties.toArray(($3, duty, key) => ({ key, duty })).sort(($3, d) => d.duty.start));
157
+ $2.for(ordered, ($3, d) => {
158
+ const costs = $3.let(new Map(), DictType(StringType, FloatType));
159
+ $3.if(agency, ($4) => {
160
+ $4(costs.insert("AGENCY", d.duty.hours.multiply(rates.get("AGENCY", () => 0.0))));
161
+ }).else(($4) => {
162
+ const ordinary = $4.const(East.max(East.min(d.duty.hours, contract.subtract(worked)), zero));
163
+ const over = $4.const(d.duty.hours.subtract(ordinary));
164
+ $4(costs.insert("ORD", ordinary.multiply(rates.get("ORD", () => 0.0))));
165
+ $4.if(East.greater(over, 0.0), ($5) => { $5(costs.insert("OT1", over.multiply(rates.get("OT1", () => 0.0)))); });
166
+ });
167
+ $3.assign(worked, worked.add(d.duty.hours));
168
+ $3(out.insert(d.key, costs));
169
+ });
170
+ return out;
32
171
  }));
33
- const visible = $.let(Slice.rows(slice).toSet((_$2, person) => person.key));
34
- return _jsx(Box, { width: "100%", height: "760px", children: _jsx(Roster, { id: "rosterWarehouse", weeks: weeks, people: Roster.people(staff.read(), { name: p => p.name, group: p => p.team, position: p => p.position, skills: p => p.skills,
35
- contract: p => p.contract, agency: p => p.agency, trainer: p => p.trainer, trainee: p => p.trainee, usual: p => p.usual }), visiblePeople: visible, slice: { slice, affordances: ["filter", "search"] }, groups: cfg.groups, shifts: cfg.shifts, duties: cfg.duties, positions: cfg.positions.toArray((_$2, p) => p), agencies: cfg.agencies, rules: cfg.rules, costs: cfg.costs, forecast: forecast.read(), proposals: proposals.read(), week: { start: "sunday" }, view: { layout: "shifts", period: "day", date: new Date("2028-03-09T00:00:00Z") }, library: [Roster.library.people(), Roster.library.activities(), Roster.library.tab(cfg.positions, {
36
- name: "Positions", icon: "id-badge", label: p => p.label, meta: p => some(p.code), drop: (_p, key) => Roster.patch({ position: key }),
37
- })], inspector: true }) });
172
+ // The warehouse's check sees the pay too (§5.8): overtime on a duty nobody has agreed.
173
+ const check = $.const(East.function([Roster.Types.CheckContext(WarehouseDuty)], ArrayType(Roster.Types.Issue), ($2, cx) => {
174
+ const issues = $2.let([], ArrayType(Roster.Types.Issue));
175
+ $2.for(cx.week.assignments, ($3, a, id) => {
176
+ const costs = $3.const(cx.pay.get(id, () => East.value(new Map(), DictType(StringType, FloatType))));
177
+ $3.if(costs.has("OT1").and(() => East.notEqual(a.row.agreed, some(true))), ($4) => {
178
+ $4(issues.pushLast({ kind: variant("breach", null), tone: variant("info", null),
179
+ title: "Overtime not agreed", detail: East.str `${a.row.team} · ${a.row.shift}`,
180
+ slot: some(a.slot), assignment: some(id), flag: some("OT"), fix: none }));
181
+ });
182
+ });
183
+ return issues;
184
+ }));
185
+ return _jsx(Box, { width: "100%", height: "760px", children: _jsx(Roster, { id: "rosterWarehouse", week: { start: "sunday" }, slice: { slice }, fit: fit, pay: pay, currency: "AUD", check: check, inspector: true, view: { layout: "shifts", period: "day", date: new Date("2028-03-09T00:00:00Z") }, events: { duty: Schedule.events(rota, {
186
+ name: "Duty", icon: "user-clock", entries,
187
+ start: "start", end: "end", resource: { field: "who", of: { staff: "people", agency: "agencies" } },
188
+ shift: "shift", group: "team", position: "role", activity: "task",
189
+ // The hours a duty counts: its length less its unpaid break.
190
+ hours: d => d.start.durationMinutes(d.end).subtract(d.unpaidBreak).divide(60.0),
191
+ fields: { unpaidBreak: Schedule.field.number({ label: "Unpaid break", unit: "min", step: 15.0, min: 0.0 }),
192
+ costCentre: Schedule.field.text({ label: "Cost centre" }), note: Schedule.field.text({ label: "Note" }) },
193
+ // The app's check on a draft: Save waits until every duty has a cost centre.
194
+ ready: d => East.equal(d.costCentre, "").ifElse(() => variant("invalid", [{ field: "costCentre", message: "Give the duty a cost centre" }]), () => variant("ready", null)),
195
+ }) }, resources: {
196
+ people: Schedule.resources(people.read(), { name: "People", icon: "user", label: p => p.name, group: p => p.team,
197
+ position: p => p.position, skills: p => p.skills, contract: p => p.contract, agency: p => p.agency,
198
+ trainer: p => p.trainer, trainee: p => p.trainee, usual: p => p.usual }),
199
+ agencies: Schedule.resources(firms.read(), { name: "Agencies", icon: "building", label: a => a.name }),
200
+ }, periods: Schedule.periods(weeks, { every: "week", status: { field: "state", draft: "open", published: "issued" }, dismissed: "dismissed" }), targets: Roster.targets(cover, { day: "day", group: "team", shift: "shift", positions: "heads", hours: "hours" }), shifts: Roster.shifts(times.read(), { label: s => s.label, code: s => s.code, start: s => s.start, hours: s => s.hours, order: s => s.order }), groups: Roster.groups(teams.read(), { label: t => t.label, order: t => t.order }), positions: Roster.positions(roles.read(), { label: p => p.label, code: p => p.code, lead: p => p.lead, order: p => p.order }), activities: Roster.activities(acts.read(), { label: a => a.label, code: a => a.code, group: a => a.group, order: a => a.order }), proposals: Roster.proposals(ideas.read(), { person: s => s.person, day: s => s.day, group: s => s.team, shift: s => s.shift, reason: s => s.why }), rules: { rest: 10.0 }, library: [Roster.library.people(), Roster.library.activities(), Roster.library.tab(roles.read(), {
201
+ name: "Positions", icon: "id-badge", label: p => p.label, meta: p => some(p.code),
202
+ drop: (_p, key) => Schedule.patch(WarehouseDuty, { role: key }),
203
+ })] }) });
38
204
  } })),
39
205
  });
40
206
  export const rosterMobile = example({
41
- inputs: [], description: 'The warehouse roster in a phone-width container: explicit Assign, Move, Edit, Remove, Restore, Request, Accept, Reject and target actions. The same session survives resizing; mobile cards register no drag sources or targets.',
42
- keywords: ["Roster", "Record.bind", "Roster.people", "Slice", "Slice.bind", "Slice.rows", "BuilderFrame", "mobile", "Save", "Publish", "FieldForm"],
207
+ inputs: [], description: "The warehouse roster in a phone-width container: explicit Assign, Move, Edit, Remove, Restore, Request, Accept, Reject and target actions over the warehouse's own records. The same session survives resizing; mobile cards register no drag sources or targets.",
208
+ keywords: ["Roster", "Schedule.events", "Schedule.resources", "Schedule.periods", "Roster.targets", "Slice", "Slice.bindRecords", "Data.bindPaged", "BuilderFrame", "mobile", "Save", "Publish", "FieldForm"],
43
209
  fn: East.function([], UIComponentType, _$ => _jsx(Reactive, { children: $ => {
210
+ const rota = $.let(Record.bind(rosterDuties, [rosterDutiesPatch]));
211
+ const entries = $.let(Data.bindPaged(rosterDuties));
212
+ const byTime = $.let(Data.bindPaged(rosterDuties, { indexes: [rosterDutiesByTime] }));
44
213
  const weeks = $.let(Record.bind(rosterWeeks, [rosterWeeksPatch]));
45
- const staff = $.let(Data.bind(rosterStaff));
46
- const configuration = $.let(Data.bind(rosterConfiguration));
47
- const forecast = $.let(Data.bind(rosterForecast));
48
- const proposals = $.let(Data.bind(rosterProposals));
49
- const cfg = $.const(configuration.read());
50
- const rows = $.let(staff.read().toArray((_$2, person, key) => ({ key, name: person.name, team: person.team, position: person.position, agency: person.agency, trainer: person.trainer })));
51
- const slice = $.let(Slice.bind("ex.rosterMobile.scope", rows, {
52
- fields: { name: { label: "Name" }, team: { label: "Group" }, position: { label: "Position" }, agency: { label: "Agency" }, trainer: { label: "Trainer" } },
53
- search: ["name", "team", "position"],
54
- }));
55
- const visible = $.let(Slice.rows(slice).toSet((_$2, person) => person.key));
56
- return _jsx(Box, { width: "100%", maxWidth: "390px", height: "760px", children: _jsx(Roster, { id: "rosterMobile", weeks: weeks, people: Roster.people(staff.read(), { name: p => p.name, group: p => p.team, position: p => p.position, skills: p => p.skills,
57
- contract: p => p.contract, agency: p => p.agency, trainer: p => p.trainer, trainee: p => p.trainee, usual: p => p.usual }), visiblePeople: visible, slice: { slice, affordances: ["filter", "search"] }, groups: cfg.groups, shifts: cfg.shifts, duties: cfg.duties, positions: cfg.positions.toArray((_$2, p) => p), agencies: cfg.agencies, rules: cfg.rules, costs: cfg.costs, forecast: forecast.read(), proposals: proposals.read(), week: { start: "sunday" }, view: { layout: "shifts", period: "day", date: new Date("2028-03-09T00:00:00Z") }, library: [Roster.library.people(), Roster.library.activities(), Roster.library.tab(cfg.positions, {
58
- name: "Positions", icon: "id-badge", label: p => p.label, meta: p => some(p.code), drop: (_p, key) => Roster.patch({ position: key }),
59
- })], inspector: true }) });
214
+ const cover = $.let(Record.bind(rosterCover, [rosterCoverPatch]));
215
+ // Every person and activity is the calculation scope; the narrowing only chooses which show (#1342).
216
+ const people = $.let(Data.bind(rosterStaff));
217
+ const acts = $.let(Data.bind(rosterActivities));
218
+ const staffIndex = $.let(Data.bindPaged(rosterStaff, { indexes: [rosterStaffByTeamName, rosterStaffByPosition, rosterStaffByTrainer, rosterStaffByAgency, rosterStaffBySkill, rosterStaffByName] }));
219
+ const actIndex = $.let(Data.bindPaged(rosterActivities, { indexes: [rosterActivitiesByGroup] }));
220
+ const slice = $.let(Slice.bindRecords("ex.rosterMobile", {
221
+ duty: { source: byTime, range: "start" },
222
+ people: { source: staffIndex, icon: { prefix: "fas", name: "user" }, search: ["name", "skills"],
223
+ fields: { team: { label: "Group", hints: ["inbound", "picking", "dispatch"] }, position: { label: "Position", hints: ["lead", "deputy", "acting", "crew"] },
224
+ trainer: { label: "Trainer" }, agency: { label: "Agency" }, skills: { label: "Skill" }, name: { label: "Name" } } },
225
+ activities: { source: actIndex, icon: { prefix: "fas", name: "list-check" }, fields: { group: { label: "Group", hints: ["inbound", "picking", "dispatch", null] } } },
226
+ }, { presets: [{ id: "leads", name: "Leads", where: { people: { position: ["lead", "deputy", "acting"] } } }] }));
227
+ const firms = $.let(Data.bind(rosterAgencies));
228
+ const times = $.let(Data.bind(rosterShiftTimes));
229
+ const teams = $.let(Data.bind(rosterTeams));
230
+ const roles = $.let(Data.bind(rosterPositions));
231
+ const ideas = $.let(Data.bind(rosterSuggestions));
232
+ return _jsx(Box, { width: "100%", maxWidth: "390px", height: "760px", children: _jsx(Roster, { id: "rosterMobile", week: { start: "sunday" }, slice: { slice }, inspector: true, view: { layout: "shifts", period: "day", date: new Date("2028-03-09T00:00:00Z") }, events: { duty: Schedule.events(rota, {
233
+ name: "Duty", icon: "user-clock", entries,
234
+ start: "start", end: "end", resource: { field: "who", of: { staff: "people", agency: "agencies" } },
235
+ shift: "shift", group: "team", position: "role", activity: "task",
236
+ hours: d => d.start.durationMinutes(d.end).subtract(d.unpaidBreak).divide(60.0),
237
+ }) }, resources: {
238
+ people: Schedule.resources(people.read(), { name: "People", icon: "user", label: p => p.name, group: p => p.team,
239
+ position: p => p.position, skills: p => p.skills, contract: p => p.contract, agency: p => p.agency,
240
+ trainer: p => p.trainer, trainee: p => p.trainee, usual: p => p.usual }),
241
+ agencies: Schedule.resources(firms.read(), { name: "Agencies", icon: "building", label: a => a.name }),
242
+ }, periods: Schedule.periods(weeks, { every: "week", status: { field: "state", draft: "open", published: "issued" }, dismissed: "dismissed" }), targets: Roster.targets(cover, { day: "day", group: "team", shift: "shift", positions: "heads", hours: "hours" }), shifts: Roster.shifts(times.read(), { label: s => s.label, code: s => s.code, start: s => s.start, hours: s => s.hours, order: s => s.order }), groups: Roster.groups(teams.read(), { label: t => t.label, order: t => t.order }), positions: Roster.positions(roles.read(), { label: p => p.label, code: p => p.code, lead: p => p.lead, order: p => p.order }), activities: Roster.activities(acts.read(), { label: a => a.label, code: a => a.code, group: a => a.group, order: a => a.order }), proposals: Roster.proposals(ideas.read(), { person: s => s.person, day: s => s.day, group: s => s.team, shift: s => s.shift, reason: s => s.why }), rules: { rest: 10.0 }, library: [Roster.library.people(), Roster.library.activities(), Roster.library.tab(roles.read(), {
243
+ name: "Positions", icon: "id-badge", label: p => p.label, meta: p => some(p.code),
244
+ drop: (_p, key) => Schedule.patch(WarehouseDuty, { role: key }),
245
+ })] }) });
60
246
  } })),
61
247
  });
62
248
  export const rosterPeople = example({
63
- inputs: [], description: 'Weekly people layout over the same records: each person retains their original key, contract hours and daily shifts, with a separate agency-requests row.',
64
- keywords: ["Roster", "Record.bind", "Roster.people", "Slice", "Slice.bind", "Slice.rows", "BuilderFrame", "mobile", "Save", "Publish", "FieldForm"],
249
+ inputs: [], description: "Weekly people layout over the same records: each person keeps their original key, contract hours and daily duties, with a separate agency-requests row.",
250
+ keywords: ["Roster", "Schedule.events", "Schedule.resources", "Schedule.periods", "Roster.targets", "Slice", "Slice.bindRecords", "Data.bindPaged", "BuilderFrame", "people", "Save", "Publish", "FieldForm"],
65
251
  fn: East.function([], UIComponentType, _$ => _jsx(Reactive, { children: $ => {
252
+ const rota = $.let(Record.bind(rosterDuties, [rosterDutiesPatch]));
253
+ const entries = $.let(Data.bindPaged(rosterDuties));
254
+ const byTime = $.let(Data.bindPaged(rosterDuties, { indexes: [rosterDutiesByTime] }));
66
255
  const weeks = $.let(Record.bind(rosterWeeks, [rosterWeeksPatch]));
67
- const staff = $.let(Data.bind(rosterStaff));
68
- const configuration = $.let(Data.bind(rosterConfiguration));
69
- const forecast = $.let(Data.bind(rosterForecast));
70
- const proposals = $.let(Data.bind(rosterProposals));
71
- const cfg = $.const(configuration.read());
72
- const rows = $.let(staff.read().toArray((_$2, person, key) => ({ key, name: person.name, team: person.team, position: person.position, agency: person.agency, trainer: person.trainer })));
73
- const slice = $.let(Slice.bind("ex.rosterPeople.scope", rows, {
74
- fields: { name: { label: "Name" }, team: { label: "Group" }, position: { label: "Position" }, agency: { label: "Agency" }, trainer: { label: "Trainer" } },
75
- search: ["name", "team", "position"],
76
- }));
77
- const visible = $.let(Slice.rows(slice).toSet((_$2, person) => person.key));
78
- return _jsx(Box, { width: "100%", height: "760px", children: _jsx(Roster, { id: "rosterPeople", weeks: weeks, people: Roster.people(staff.read(), { name: p => p.name, group: p => p.team, position: p => p.position, skills: p => p.skills,
79
- contract: p => p.contract, agency: p => p.agency, trainer: p => p.trainer, trainee: p => p.trainee, usual: p => p.usual }), visiblePeople: visible, slice: { slice, affordances: ["filter", "search"] }, groups: cfg.groups, shifts: cfg.shifts, duties: cfg.duties, positions: cfg.positions.toArray((_$2, p) => p), agencies: cfg.agencies, rules: cfg.rules, costs: cfg.costs, forecast: forecast.read(), proposals: proposals.read(), week: { start: "sunday" }, view: { layout: "people", period: "week", date: new Date("2028-03-09T00:00:00Z") }, library: [Roster.library.people(), Roster.library.activities(), Roster.library.tab(cfg.positions, {
80
- name: "Positions", icon: "id-badge", label: p => p.label, meta: p => some(p.code), drop: (_p, key) => Roster.patch({ position: key }),
81
- })], inspector: true }) });
256
+ const cover = $.let(Record.bind(rosterCover, [rosterCoverPatch]));
257
+ // Every person and activity is the calculation scope; the narrowing only chooses which show (#1342).
258
+ const people = $.let(Data.bind(rosterStaff));
259
+ const acts = $.let(Data.bind(rosterActivities));
260
+ const staffIndex = $.let(Data.bindPaged(rosterStaff, { indexes: [rosterStaffByTeamName, rosterStaffByPosition, rosterStaffByTrainer, rosterStaffByAgency, rosterStaffBySkill, rosterStaffByName] }));
261
+ const actIndex = $.let(Data.bindPaged(rosterActivities, { indexes: [rosterActivitiesByGroup] }));
262
+ const slice = $.let(Slice.bindRecords("ex.rosterPeople", {
263
+ duty: { source: byTime, range: "start" },
264
+ people: { source: staffIndex, icon: { prefix: "fas", name: "user" }, search: ["name", "skills"],
265
+ fields: { team: { label: "Group", hints: ["inbound", "picking", "dispatch"] }, position: { label: "Position", hints: ["lead", "deputy", "acting", "crew"] },
266
+ trainer: { label: "Trainer" }, agency: { label: "Agency" }, skills: { label: "Skill" }, name: { label: "Name" } } },
267
+ activities: { source: actIndex, icon: { prefix: "fas", name: "list-check" }, fields: { group: { label: "Group", hints: ["inbound", "picking", "dispatch", null] } } },
268
+ }, { presets: [{ id: "leads", name: "Leads", where: { people: { position: ["lead", "deputy", "acting"] } } }] }));
269
+ const firms = $.let(Data.bind(rosterAgencies));
270
+ const times = $.let(Data.bind(rosterShiftTimes));
271
+ const teams = $.let(Data.bind(rosterTeams));
272
+ const roles = $.let(Data.bind(rosterPositions));
273
+ const ideas = $.let(Data.bind(rosterSuggestions));
274
+ return _jsx(Box, { width: "100%", height: "760px", children: _jsx(Roster, { id: "rosterPeople", week: { start: "sunday" }, slice: { slice }, inspector: true, view: { layout: "people", period: "week", date: new Date("2028-03-09T00:00:00Z") }, events: { duty: Schedule.events(rota, {
275
+ name: "Duty", icon: "user-clock", entries,
276
+ start: "start", end: "end", resource: { field: "who", of: { staff: "people", agency: "agencies" } },
277
+ shift: "shift", group: "team", position: "role", activity: "task",
278
+ hours: d => d.start.durationMinutes(d.end).subtract(d.unpaidBreak).divide(60.0),
279
+ }) }, resources: {
280
+ people: Schedule.resources(people.read(), { name: "People", icon: "user", label: p => p.name, group: p => p.team,
281
+ position: p => p.position, skills: p => p.skills, contract: p => p.contract, agency: p => p.agency,
282
+ trainer: p => p.trainer, trainee: p => p.trainee, usual: p => p.usual }),
283
+ agencies: Schedule.resources(firms.read(), { name: "Agencies", icon: "building", label: a => a.name }),
284
+ }, periods: Schedule.periods(weeks, { every: "week", status: { field: "state", draft: "open", published: "issued" }, dismissed: "dismissed" }), targets: Roster.targets(cover, { day: "day", group: "team", shift: "shift", positions: "heads", hours: "hours" }), shifts: Roster.shifts(times.read(), { label: s => s.label, code: s => s.code, start: s => s.start, hours: s => s.hours, order: s => s.order }), groups: Roster.groups(teams.read(), { label: t => t.label, order: t => t.order }), positions: Roster.positions(roles.read(), { label: p => p.label, code: p => p.code, lead: p => p.lead, order: p => p.order }), activities: Roster.activities(acts.read(), { label: a => a.label, code: a => a.code, group: a => a.group, order: a => a.order }), proposals: Roster.proposals(ideas.read(), { person: s => s.person, day: s => s.day, group: s => s.team, shift: s => s.shift, reason: s => s.why }), rules: { rest: 10.0 }, library: [Roster.library.people(), Roster.library.activities(), Roster.library.tab(roles.read(), {
285
+ name: "Positions", icon: "id-badge", label: p => p.label, meta: p => some(p.code),
286
+ drop: (_p, key) => Schedule.patch(WarehouseDuty, { role: key }),
287
+ })] }) });
82
288
  } })),
83
289
  });
84
290
  export const rosterWeek = example({
85
- inputs: [], description: 'Seven days of group-by-shift coverage, opening any day for detailed staffing.',
86
- keywords: ["Roster", "Record.bind", "Roster.people", "Slice", "Slice.bind", "Slice.rows", "BuilderFrame", "mobile", "Save", "Publish", "FieldForm"],
291
+ inputs: [], description: "Seven days of group-by-shift coverage over the warehouse's own records, opening any day for detailed staffing.",
292
+ keywords: ["Roster", "Schedule.events", "Schedule.resources", "Schedule.periods", "Roster.targets", "Slice", "Slice.bindRecords", "Data.bindPaged", "BuilderFrame", "week", "Save", "Publish", "FieldForm"],
87
293
  fn: East.function([], UIComponentType, _$ => _jsx(Reactive, { children: $ => {
294
+ const rota = $.let(Record.bind(rosterDuties, [rosterDutiesPatch]));
295
+ const entries = $.let(Data.bindPaged(rosterDuties));
296
+ const byTime = $.let(Data.bindPaged(rosterDuties, { indexes: [rosterDutiesByTime] }));
88
297
  const weeks = $.let(Record.bind(rosterWeeks, [rosterWeeksPatch]));
89
- const staff = $.let(Data.bind(rosterStaff));
90
- const configuration = $.let(Data.bind(rosterConfiguration));
91
- const forecast = $.let(Data.bind(rosterForecast));
92
- const proposals = $.let(Data.bind(rosterProposals));
93
- const cfg = $.const(configuration.read());
94
- const rows = $.let(staff.read().toArray((_$2, person, key) => ({ key, name: person.name, team: person.team, position: person.position, agency: person.agency, trainer: person.trainer })));
95
- const slice = $.let(Slice.bind("ex.rosterWeek.scope", rows, {
96
- fields: { name: { label: "Name" }, team: { label: "Group" }, position: { label: "Position" }, agency: { label: "Agency" }, trainer: { label: "Trainer" } },
97
- search: ["name", "team", "position"],
98
- }));
99
- const visible = $.let(Slice.rows(slice).toSet((_$2, person) => person.key));
100
- return _jsx(Box, { width: "100%", height: "760px", children: _jsx(Roster, { id: "rosterWeek", weeks: weeks, people: Roster.people(staff.read(), { name: p => p.name, group: p => p.team, position: p => p.position, skills: p => p.skills,
101
- contract: p => p.contract, agency: p => p.agency, trainer: p => p.trainer, trainee: p => p.trainee, usual: p => p.usual }), visiblePeople: visible, slice: { slice, affordances: ["filter", "search"] }, groups: cfg.groups, shifts: cfg.shifts, duties: cfg.duties, positions: cfg.positions.toArray((_$2, p) => p), agencies: cfg.agencies, rules: cfg.rules, costs: cfg.costs, forecast: forecast.read(), proposals: proposals.read(), week: { start: "sunday" }, view: { layout: "shifts", period: "week", date: new Date("2028-03-09T00:00:00Z") }, library: [Roster.library.people(), Roster.library.activities(), Roster.library.tab(cfg.positions, {
102
- name: "Positions", icon: "id-badge", label: p => p.label, meta: p => some(p.code), drop: (_p, key) => Roster.patch({ position: key }),
103
- })], inspector: true }) });
298
+ const cover = $.let(Record.bind(rosterCover, [rosterCoverPatch]));
299
+ // Every person and activity is the calculation scope; the narrowing only chooses which show (#1342).
300
+ const people = $.let(Data.bind(rosterStaff));
301
+ const acts = $.let(Data.bind(rosterActivities));
302
+ const staffIndex = $.let(Data.bindPaged(rosterStaff, { indexes: [rosterStaffByTeamName, rosterStaffByPosition, rosterStaffByTrainer, rosterStaffByAgency, rosterStaffBySkill, rosterStaffByName] }));
303
+ const actIndex = $.let(Data.bindPaged(rosterActivities, { indexes: [rosterActivitiesByGroup] }));
304
+ const slice = $.let(Slice.bindRecords("ex.rosterWeek", {
305
+ duty: { source: byTime, range: "start" },
306
+ people: { source: staffIndex, icon: { prefix: "fas", name: "user" }, search: ["name", "skills"],
307
+ fields: { team: { label: "Group", hints: ["inbound", "picking", "dispatch"] }, position: { label: "Position", hints: ["lead", "deputy", "acting", "crew"] },
308
+ trainer: { label: "Trainer" }, agency: { label: "Agency" }, skills: { label: "Skill" }, name: { label: "Name" } } },
309
+ activities: { source: actIndex, icon: { prefix: "fas", name: "list-check" }, fields: { group: { label: "Group", hints: ["inbound", "picking", "dispatch", null] } } },
310
+ }, { presets: [{ id: "leads", name: "Leads", where: { people: { position: ["lead", "deputy", "acting"] } } }] }));
311
+ const firms = $.let(Data.bind(rosterAgencies));
312
+ const times = $.let(Data.bind(rosterShiftTimes));
313
+ const teams = $.let(Data.bind(rosterTeams));
314
+ const roles = $.let(Data.bind(rosterPositions));
315
+ const ideas = $.let(Data.bind(rosterSuggestions));
316
+ return _jsx(Box, { width: "100%", height: "760px", children: _jsx(Roster, { id: "rosterWeek", week: { start: "sunday" }, slice: { slice }, inspector: true, view: { layout: "shifts", period: "week", date: new Date("2028-03-09T00:00:00Z") }, events: { duty: Schedule.events(rota, {
317
+ name: "Duty", icon: "user-clock", entries,
318
+ start: "start", end: "end", resource: { field: "who", of: { staff: "people", agency: "agencies" } },
319
+ shift: "shift", group: "team", position: "role", activity: "task",
320
+ hours: d => d.start.durationMinutes(d.end).subtract(d.unpaidBreak).divide(60.0),
321
+ }) }, resources: {
322
+ people: Schedule.resources(people.read(), { name: "People", icon: "user", label: p => p.name, group: p => p.team,
323
+ position: p => p.position, skills: p => p.skills, contract: p => p.contract, agency: p => p.agency,
324
+ trainer: p => p.trainer, trainee: p => p.trainee, usual: p => p.usual }),
325
+ agencies: Schedule.resources(firms.read(), { name: "Agencies", icon: "building", label: a => a.name }),
326
+ }, periods: Schedule.periods(weeks, { every: "week", status: { field: "state", draft: "open", published: "issued" }, dismissed: "dismissed" }), targets: Roster.targets(cover, { day: "day", group: "team", shift: "shift", positions: "heads", hours: "hours" }), shifts: Roster.shifts(times.read(), { label: s => s.label, code: s => s.code, start: s => s.start, hours: s => s.hours, order: s => s.order }), groups: Roster.groups(teams.read(), { label: t => t.label, order: t => t.order }), positions: Roster.positions(roles.read(), { label: p => p.label, code: p => p.code, lead: p => p.lead, order: p => p.order }), activities: Roster.activities(acts.read(), { label: a => a.label, code: a => a.code, group: a => a.group, order: a => a.order }), proposals: Roster.proposals(ideas.read(), { person: s => s.person, day: s => s.day, group: s => s.team, shift: s => s.shift, reason: s => s.why }), rules: { rest: 10.0 }, library: [Roster.library.people(), Roster.library.activities(), Roster.library.tab(roles.read(), {
327
+ name: "Positions", icon: "id-badge", label: p => p.label, meta: p => some(p.code),
328
+ drop: (_p, key) => Schedule.patch(WarehouseDuty, { role: key }),
329
+ })] }) });
104
330
  } })),
105
331
  });
106
332
  export const rosterWindowed = example({
107
- inputs: [], description: 'A bounded history window using Data.bindPaged on the same weeks record. The author-bound Slice range and roster navigation choose the same week. Only that week, the previous copy source and the open picker month are requested.',
108
- keywords: ["Roster", "Record.bind", "Roster.people", "Slice", "Slice.bind", "Slice.rows", "BuilderFrame", "mobile", "Save", "Publish", "FieldForm"],
333
+ inputs: [], description: "The duties read a week at a time through the slice's duty source — an interval index over their times, its range the Roster's own navigation — and the rota's weeks by key: only the week, the week before it (Copy's source) and the open picker's month are requested, never a record whole.",
334
+ keywords: ["Roster", "Schedule.events", "Schedule.periods", "Slice.bindRecords", "Data.bindPaged", "recordIndex", "interval", "history", "BuilderFrame", "Save", "Publish"],
109
335
  fn: East.function([], UIComponentType, _$ => _jsx(Reactive, { children: $ => {
336
+ const rota = $.let(Record.bind(rosterDuties, [rosterDutiesPatch]));
337
+ const entries = $.let(Data.bindPaged(rosterDuties));
338
+ const byTime = $.let(Data.bindPaged(rosterDuties, { indexes: [rosterDutiesByTime] }));
339
+ // The weeks of the rota read by key: the week, the week before it and the open picker's month.
110
340
  const weeks = $.let(Record.bind(rosterWeeks, [rosterWeeksPatch]));
111
- const staff = $.let(Data.bind(rosterStaff));
112
- const configuration = $.let(Data.bind(rosterConfiguration));
113
- const forecast = $.let(Data.bind(rosterForecast));
114
- const proposals = $.let(Data.bind(rosterProposals));
115
- const cfg = $.const(configuration.read());
116
- const history = $.let(Data.bindPaged(rosterWeeks));
117
- const WeekKey = StructType({ week: DateTimeType });
118
- const first = $.const(new Date("2028-03-12T00:00:00Z"), DateTimeType);
119
- // A bounded horizon of keys, independent of the record's rows and history.
120
- const horizon = $.let(East.Array.generate(13n, WeekKey, (_$2, i) => ({ week: first.addDays(i.subtract(6n).multiply(7n)) })));
121
- const slice = $.let(Slice.bind("ex.rosterWindowed.weeks", horizon, {
122
- fields: { week: { label: "Week", format: { date: "MMM D" } } },
123
- range: "week",
124
- initial: { week: { from: first, to: first.addDays(7n).addMilliseconds(-1n) } },
125
- }));
126
- return _jsx(Box, { width: "100%", height: "760px", children: _jsx(Roster, { id: "rosterWindowed", weeks: weeks, window: history, people: Roster.people(staff.read(), { name: p => p.name, group: p => p.team, position: p => p.position, skills: p => p.skills,
127
- contract: p => p.contract, agency: p => p.agency, trainer: p => p.trainer, trainee: p => p.trainee, usual: p => p.usual }), slice: { slice, affordances: ["range"] }, groups: cfg.groups, shifts: cfg.shifts, duties: cfg.duties, positions: cfg.positions.toArray((_$2, p) => p), agencies: cfg.agencies, rules: cfg.rules, costs: cfg.costs, forecast: forecast.read(), proposals: proposals.read(), week: { start: "sunday" }, view: { layout: "shifts", period: "day", date: new Date("2028-03-12T00:00:00Z") }, library: [Roster.library.people(), Roster.library.activities(), Roster.library.tab(cfg.positions, {
128
- name: "Positions", icon: "id-badge", label: p => p.label, meta: p => some(p.code), drop: (_p, key) => Roster.patch({ position: key }),
129
- })], inspector: true }) });
341
+ const weekEntries = $.let(Data.bindPaged(rosterWeeks));
342
+ const cover = $.let(Record.bind(rosterCover, [rosterCoverPatch]));
343
+ const people = $.let(Data.bind(rosterStaff));
344
+ const acts = $.let(Data.bind(rosterActivities));
345
+ // One source: the week's duties, its range the Roster's navigation.
346
+ const slice = $.let(Slice.bindRecords("ex.rosterWindowed", { duty: { source: byTime, range: "start" } }));
347
+ const firms = $.let(Data.bind(rosterAgencies));
348
+ const times = $.let(Data.bind(rosterShiftTimes));
349
+ const teams = $.let(Data.bind(rosterTeams));
350
+ const roles = $.let(Data.bind(rosterPositions));
351
+ const ideas = $.let(Data.bind(rosterSuggestions));
352
+ return _jsx(Box, { width: "100%", height: "760px", children: _jsx(Roster, { id: "rosterWindowed", week: { start: "sunday" }, slice: { slice }, inspector: true, view: { layout: "shifts", period: "day", date: new Date("2028-03-12T00:00:00Z") }, events: { duty: Schedule.events(rota, {
353
+ name: "Duty", icon: "user-clock", entries,
354
+ start: "start", end: "end", resource: { field: "who", of: { staff: "people", agency: "agencies" } },
355
+ shift: "shift", group: "team", position: "role", activity: "task",
356
+ hours: d => d.start.durationMinutes(d.end).subtract(d.unpaidBreak).divide(60.0),
357
+ }) }, resources: {
358
+ people: Schedule.resources(people.read(), { name: "People", icon: "user", label: p => p.name, group: p => p.team,
359
+ position: p => p.position, skills: p => p.skills, contract: p => p.contract, agency: p => p.agency,
360
+ trainer: p => p.trainer, trainee: p => p.trainee, usual: p => p.usual }),
361
+ agencies: Schedule.resources(firms.read(), { name: "Agencies", icon: "building", label: a => a.name }),
362
+ }, periods: Schedule.periods(weeks, { every: "week", status: { field: "state", draft: "open", published: "issued" }, dismissed: "dismissed", entries: weekEntries }), targets: Roster.targets(cover, { day: "day", group: "team", shift: "shift", positions: "heads", hours: "hours" }), shifts: Roster.shifts(times.read(), { label: s => s.label, code: s => s.code, start: s => s.start, hours: s => s.hours, order: s => s.order }), groups: Roster.groups(teams.read(), { label: t => t.label, order: t => t.order }), positions: Roster.positions(roles.read(), { label: p => p.label, code: p => p.code, lead: p => p.lead, order: p => p.order }), activities: Roster.activities(acts.read(), { label: a => a.label, code: a => a.code, group: a => a.group, order: a => a.order }), proposals: Roster.proposals(ideas.read(), { person: s => s.person, day: s => s.day, group: s => s.team, shift: s => s.shift, reason: s => s.why }), rules: { rest: 10.0 }, library: [Roster.library.people(), Roster.library.activities(), Roster.library.tab(roles.read(), {
363
+ name: "Positions", icon: "id-badge", label: p => p.label, meta: p => some(p.code),
364
+ drop: (_p, key) => Schedule.patch(WarehouseDuty, { role: key }),
365
+ })] }) });
130
366
  } })),
131
367
  });
368
+ // The smallest roster (§5.7): duties with their times, shift and team; staff; shift times; teams.
369
+ /** A duty of the smallest roster: who, as a staff key; when, as worked; its shift and its team. */
370
+ export const MinimalDuty = StructType({ person: StringType, start: DateTimeType, end: DateTimeType, shift: StringType, team: StringType });
371
+ /** One of its staff: a name and a team. */
372
+ export const MinimalWorker = StructType({ name: StringType, team: StringType });
373
+ /** One of its shifts: a name, a code, when it starts, its hours and where it shows. */
374
+ export const MinimalShiftTime = StructType({ name: StringType, code: StringType, starts: Schedule.Types.Clock, hours: FloatType, order: IntegerType });
375
+ /** One of its teams: a name. */
376
+ export const MinimalTeam = StructType({ name: StringType });
377
+ export const minimalDuties = e3.record("rota_minimal_duties", DictType(StringType, MinimalDuty), new Map([
378
+ ["d-1", { person: "w-01", start: new Date("2028-03-06T06:00:00Z"), end: new Date("2028-03-06T14:00:00Z"), shift: "early", team: "inbound" }],
379
+ ["d-2", { person: "w-02", start: new Date("2028-03-06T14:00:00Z"), end: new Date("2028-03-06T22:00:00Z"), shift: "late", team: "dispatch" }],
380
+ ["d-3", { person: "w-01", start: new Date("2028-03-07T05:30:00Z"), end: new Date("2028-03-07T14:00:00Z"), shift: "early", team: "inbound" }],
381
+ ]));
382
+ export const minimalDutiesPatch = e3.mutation.patch(minimalDuties);
383
+ export const minimalWorkers = e3.record("rota_minimal_workers", DictType(StringType, MinimalWorker), new Map([
384
+ ["w-01", { name: "Ada Brook", team: "inbound" }],
385
+ ["w-02", { name: "Tom Vale", team: "dispatch" }],
386
+ ]));
387
+ export const minimalShiftTimes = e3.record("rota_minimal_shift_times", DictType(StringType, MinimalShiftTime), new Map([
388
+ ["early", { name: "Early", code: "E", starts: { hour: 6n, minute: 0n }, hours: 8.0, order: 1n }],
389
+ ["late", { name: "Late", code: "L", starts: { hour: 14n, minute: 0n }, hours: 8.0, order: 2n }],
390
+ ]));
391
+ export const minimalTeams = e3.record("rota_minimal_teams", DictType(StringType, MinimalTeam), new Map([
392
+ ["inbound", { name: "Inbound" }], ["dispatch", { name: "Dispatch" }],
393
+ ]));
132
394
  export const rosterMinimal = example({
133
- inputs: [], description: 'A minimal roster without a library or inspector pane. Explicit assignment actions and forecast targets still use the shared week session.',
134
- keywords: ["Roster", "Record.bind", "Roster.people", "Slice", "Slice.bind", "Slice.rows", "BuilderFrame", "mobile", "Save", "Publish", "FieldForm"],
395
+ inputs: [], description: "The smallest roster over the app's own records: duties with their times, shift and team; staff; shift times; teams. No library or inspector pane, and no Publish; explicit assignment actions still use the shared session.",
396
+ keywords: ["Roster", "Schedule.events", "Schedule.resources", "Roster.shifts", "Roster.groups", "Record.bind", "minimal"],
135
397
  fn: East.function([], UIComponentType, _$ => _jsx(Reactive, { children: $ => {
136
- const weeks = $.let(Record.bind(rosterWeeks, [rosterWeeksPatch]));
137
- const staff = $.let(Data.bind(rosterStaff));
138
- const configuration = $.let(Data.bind(rosterConfiguration));
139
- const forecast = $.let(Data.bind(rosterForecast));
140
- const proposals = $.let(Data.bind(rosterProposals));
141
- const cfg = $.const(configuration.read());
142
- const rows = $.let(staff.read().toArray((_$2, person, key) => ({ key, name: person.name, team: person.team, position: person.position, agency: person.agency, trainer: person.trainer })));
143
- const slice = $.let(Slice.bind("ex.rosterMinimal.scope", rows, {
144
- fields: { name: { label: "Name" }, team: { label: "Group" }, position: { label: "Position" }, agency: { label: "Agency" }, trainer: { label: "Trainer" } },
145
- search: ["name", "team", "position"],
146
- }));
147
- const visible = $.let(Slice.rows(slice).toSet((_$2, person) => person.key));
148
- return _jsx(Box, { width: "100%", height: "760px", children: _jsx(Roster, { id: "rosterMinimal", weeks: weeks, people: Roster.people(staff.read(), { name: p => p.name, group: p => p.team, position: p => p.position, skills: p => p.skills,
149
- contract: p => p.contract, agency: p => p.agency, trainer: p => p.trainer, trainee: p => p.trainee, usual: p => p.usual }), visiblePeople: visible, slice: { slice, affordances: ["filter", "search"] }, groups: cfg.groups, shifts: cfg.shifts, duties: cfg.duties, positions: cfg.positions.toArray((_$2, p) => p), agencies: cfg.agencies, rules: cfg.rules, costs: cfg.costs, forecast: forecast.read(), proposals: proposals.read(), week: { start: "sunday" }, view: { layout: "shifts", period: "day", date: new Date("2028-03-12T00:00:00Z") } }) });
398
+ const rota = $.let(Record.bind(minimalDuties, [minimalDutiesPatch]));
399
+ const staff = $.let(Data.bind(minimalWorkers));
400
+ const times = $.let(Data.bind(minimalShiftTimes));
401
+ const groups = $.let(Data.bind(minimalTeams));
402
+ return _jsx(Box, { width: "100%", height: "760px", children: _jsx(Roster, { id: "rosterMinimal", view: { date: new Date("2028-03-06T00:00:00Z") }, events: { duty: Schedule.events(rota, {
403
+ name: "Duty", icon: "user-clock",
404
+ start: "start", end: "end", resource: { field: "person", of: "people" },
405
+ shift: "shift", group: "team",
406
+ }) }, resources: { people: Schedule.resources(staff.read(), { name: "People", icon: "user", label: w => w.name, group: w => w.team }) }, shifts: Roster.shifts(times.read(), { label: s => s.name, code: s => s.code, start: s => s.starts, hours: s => s.hours, order: s => s.order }), groups: Roster.groups(groups.read(), { label: t => t.name }) }) });
150
407
  } })),
151
408
  });
152
409
  export const rosterPublished = example({
153
- inputs: [], description: 'The previous published roster is read-only: navigation, staff filtering, coverage and inspection remain available.',
154
- keywords: ["Roster", "Record.bind", "Roster.people", "Slice", "Slice.bind", "Slice.rows", "BuilderFrame", "mobile", "Save", "Publish", "FieldForm"],
410
+ inputs: [], description: "The week before, published, is read-only: navigation, staff filtering, coverage and inspection remain available.",
411
+ keywords: ["Roster", "Schedule.events", "Schedule.resources", "Schedule.periods", "Roster.targets", "Slice", "Slice.bindRecords", "Data.bindPaged", "BuilderFrame", "published", "read-only", "Publish"],
155
412
  fn: East.function([], UIComponentType, _$ => _jsx(Reactive, { children: $ => {
413
+ const rota = $.let(Record.bind(rosterDuties, [rosterDutiesPatch]));
414
+ const entries = $.let(Data.bindPaged(rosterDuties));
415
+ const byTime = $.let(Data.bindPaged(rosterDuties, { indexes: [rosterDutiesByTime] }));
156
416
  const weeks = $.let(Record.bind(rosterWeeks, [rosterWeeksPatch]));
157
- const staff = $.let(Data.bind(rosterStaff));
158
- const configuration = $.let(Data.bind(rosterConfiguration));
159
- const forecast = $.let(Data.bind(rosterForecast));
160
- const proposals = $.let(Data.bind(rosterProposals));
161
- const cfg = $.const(configuration.read());
162
- const rows = $.let(staff.read().toArray((_$2, person, key) => ({ key, name: person.name, team: person.team, position: person.position, agency: person.agency, trainer: person.trainer })));
163
- const slice = $.let(Slice.bind("ex.rosterPublished.scope", rows, {
164
- fields: { name: { label: "Name" }, team: { label: "Group" }, position: { label: "Position" }, agency: { label: "Agency" }, trainer: { label: "Trainer" } },
165
- search: ["name", "team", "position"],
166
- }));
167
- const visible = $.let(Slice.rows(slice).toSet((_$2, person) => person.key));
168
- return _jsx(Box, { width: "100%", height: "760px", children: _jsx(Roster, { id: "rosterPublished", weeks: weeks, people: Roster.people(staff.read(), { name: p => p.name, group: p => p.team, position: p => p.position, skills: p => p.skills,
169
- contract: p => p.contract, agency: p => p.agency, trainer: p => p.trainer, trainee: p => p.trainee, usual: p => p.usual }), visiblePeople: visible, slice: { slice, affordances: ["filter", "search"] }, groups: cfg.groups, shifts: cfg.shifts, duties: cfg.duties, positions: cfg.positions.toArray((_$2, p) => p), agencies: cfg.agencies, rules: cfg.rules, costs: cfg.costs, forecast: forecast.read(), proposals: proposals.read(), week: { start: "sunday" }, view: { layout: "shifts", period: "day", date: new Date("2028-03-02T00:00:00Z") }, library: [Roster.library.people(), Roster.library.activities(), Roster.library.tab(cfg.positions, {
170
- name: "Positions", icon: "id-badge", label: p => p.label, meta: p => some(p.code), drop: (_p, key) => Roster.patch({ position: key }),
171
- })], inspector: true }) });
417
+ const cover = $.let(Record.bind(rosterCover, [rosterCoverPatch]));
418
+ // Every person and activity is the calculation scope; the narrowing only chooses which show (#1342).
419
+ const people = $.let(Data.bind(rosterStaff));
420
+ const acts = $.let(Data.bind(rosterActivities));
421
+ const staffIndex = $.let(Data.bindPaged(rosterStaff, { indexes: [rosterStaffByTeamName, rosterStaffByPosition, rosterStaffByTrainer, rosterStaffByAgency, rosterStaffBySkill, rosterStaffByName] }));
422
+ const actIndex = $.let(Data.bindPaged(rosterActivities, { indexes: [rosterActivitiesByGroup] }));
423
+ const slice = $.let(Slice.bindRecords("ex.rosterPublished", {
424
+ duty: { source: byTime, range: "start" },
425
+ people: { source: staffIndex, icon: { prefix: "fas", name: "user" }, search: ["name", "skills"],
426
+ fields: { team: { label: "Group", hints: ["inbound", "picking", "dispatch"] }, position: { label: "Position", hints: ["lead", "deputy", "acting", "crew"] },
427
+ trainer: { label: "Trainer" }, agency: { label: "Agency" }, skills: { label: "Skill" }, name: { label: "Name" } } },
428
+ activities: { source: actIndex, icon: { prefix: "fas", name: "list-check" }, fields: { group: { label: "Group", hints: ["inbound", "picking", "dispatch", null] } } },
429
+ }, { presets: [{ id: "leads", name: "Leads", where: { people: { position: ["lead", "deputy", "acting"] } } }] }));
430
+ const firms = $.let(Data.bind(rosterAgencies));
431
+ const times = $.let(Data.bind(rosterShiftTimes));
432
+ const teams = $.let(Data.bind(rosterTeams));
433
+ const roles = $.let(Data.bind(rosterPositions));
434
+ const ideas = $.let(Data.bind(rosterSuggestions));
435
+ return _jsx(Box, { width: "100%", height: "760px", children: _jsx(Roster, { id: "rosterPublished", week: { start: "sunday" }, slice: { slice }, inspector: true, view: { layout: "shifts", period: "day", date: new Date("2028-03-02T00:00:00Z") }, events: { duty: Schedule.events(rota, {
436
+ name: "Duty", icon: "user-clock", entries,
437
+ start: "start", end: "end", resource: { field: "who", of: { staff: "people", agency: "agencies" } },
438
+ shift: "shift", group: "team", position: "role", activity: "task",
439
+ hours: d => d.start.durationMinutes(d.end).subtract(d.unpaidBreak).divide(60.0),
440
+ }) }, resources: {
441
+ people: Schedule.resources(people.read(), { name: "People", icon: "user", label: p => p.name, group: p => p.team,
442
+ position: p => p.position, skills: p => p.skills, contract: p => p.contract, agency: p => p.agency,
443
+ trainer: p => p.trainer, trainee: p => p.trainee, usual: p => p.usual }),
444
+ agencies: Schedule.resources(firms.read(), { name: "Agencies", icon: "building", label: a => a.name }),
445
+ }, periods: Schedule.periods(weeks, { every: "week", status: { field: "state", draft: "open", published: "issued" }, dismissed: "dismissed" }), targets: Roster.targets(cover, { day: "day", group: "team", shift: "shift", positions: "heads", hours: "hours" }), shifts: Roster.shifts(times.read(), { label: s => s.label, code: s => s.code, start: s => s.start, hours: s => s.hours, order: s => s.order }), groups: Roster.groups(teams.read(), { label: t => t.label, order: t => t.order }), positions: Roster.positions(roles.read(), { label: p => p.label, code: p => p.code, lead: p => p.lead, order: p => p.order }), activities: Roster.activities(acts.read(), { label: a => a.label, code: a => a.code, group: a => a.group, order: a => a.order }), proposals: Roster.proposals(ideas.read(), { person: s => s.person, day: s => s.day, group: s => s.team, shift: s => s.shift, reason: s => s.why }), rules: { rest: 10.0 }, library: [Roster.library.people(), Roster.library.activities(), Roster.library.tab(roles.read(), {
446
+ name: "Positions", icon: "id-badge", label: p => p.label, meta: p => some(p.code),
447
+ drop: (_p, key) => Schedule.patch(WarehouseDuty, { role: key }),
448
+ })] }) });
172
449
  } })),
173
450
  });
174
451
  export const rosterCustomInspector = example({
175
- inputs: [], description: 'Typed author assignment and requirement forms replace the built-in fields while preserving the shared headers, coverage, actions and undo history.',
176
- keywords: ["Roster", "Record.bind", "Roster.people", "Slice", "Slice.bind", "Slice.rows", "BuilderFrame", "mobile", "Save", "Publish", "FieldForm"],
452
+ inputs: [], description: "The duty kind's own inspector and the targets' own form replace the built-in fields while preserving the shared headers, coverage, actions and undo history.",
453
+ keywords: ["Roster", "Schedule.events", "inspector", "Roster.targets", "Schedule.resources", "Schedule.periods", "Slice", "Slice.bindRecords", "Data.bindPaged", "BuilderFrame", "Save", "Publish", "FieldForm"],
177
454
  fn: East.function([], UIComponentType, _$ => _jsx(Reactive, { children: $ => {
455
+ const rota = $.let(Record.bind(rosterDuties, [rosterDutiesPatch]));
456
+ const entries = $.let(Data.bindPaged(rosterDuties));
457
+ const byTime = $.let(Data.bindPaged(rosterDuties, { indexes: [rosterDutiesByTime] }));
178
458
  const weeks = $.let(Record.bind(rosterWeeks, [rosterWeeksPatch]));
179
- const staff = $.let(Data.bind(rosterStaff));
180
- const configuration = $.let(Data.bind(rosterConfiguration));
181
- const forecast = $.let(Data.bind(rosterForecast));
182
- const proposals = $.let(Data.bind(rosterProposals));
183
- const cfg = $.const(configuration.read());
184
- const rows = $.let(staff.read().toArray((_$2, person, key) => ({ key, name: person.name, team: person.team, position: person.position, agency: person.agency, trainer: person.trainer })));
185
- const slice = $.let(Slice.bind("ex.rosterCustomInspector.scope", rows, {
186
- fields: { name: { label: "Name" }, team: { label: "Group" }, position: { label: "Position" }, agency: { label: "Agency" }, trainer: { label: "Trainer" } },
187
- search: ["name", "team", "position"],
188
- }));
189
- const visible = $.let(Slice.rows(slice).toSet((_$2, person) => person.key));
190
- const assignment = $.const(East.function([Roster.Types.Assignment, FunctionType([Roster.Types.Assignment], NullType)], UIComponentType, (_$2, row, update) => _jsx(VStack, { children: _jsx(Field.FloatInput, { label: "Custom overtime", value: row.overtime, min: 0, max: 4, step: 0.5, onChange: (_$3, value) => update({ slot: row.slot, who: row.who, position: row.position, activity: row.activity, offset: row.offset, overtime: value, agreed: row.agreed }) }) })));
191
- const requirement = $.const(East.function([Roster.Types.Requirement, FunctionType([Roster.Types.Requirement], NullType)], UIComponentType, (_$2, row, update) => _jsx(Field.IntegerInput, { label: "Custom positions", value: row.positions, min: 0n, step: 1n, onChange: (_$3, value) => update({ positions: value, hours: row.hours }) })));
192
- return _jsx(Box, { width: "100%", height: "760px", children: _jsx(Roster, { id: "rosterCustomInspector", weeks: weeks, people: Roster.people(staff.read(), { name: p => p.name, group: p => p.team, position: p => p.position, skills: p => p.skills,
193
- contract: p => p.contract, agency: p => p.agency, trainer: p => p.trainer, trainee: p => p.trainee, usual: p => p.usual }), visiblePeople: visible, slice: { slice, affordances: ["filter", "search"] }, groups: cfg.groups, shifts: cfg.shifts, duties: cfg.duties, positions: cfg.positions.toArray((_$2, p) => p), agencies: cfg.agencies, rules: cfg.rules, costs: cfg.costs, forecast: forecast.read(), proposals: proposals.read(), week: { start: "sunday" }, view: { layout: "shifts", period: "day", date: new Date("2028-03-09T00:00:00Z") }, library: [Roster.library.people(), Roster.library.activities(), Roster.library.tab(cfg.positions, {
194
- name: "Positions", icon: "id-badge", label: p => p.label, meta: p => some(p.code), drop: (_p, key) => Roster.patch({ position: key }),
195
- })], inspector: { assignment, requirement } }) });
459
+ const cover = $.let(Record.bind(rosterCover, [rosterCoverPatch]));
460
+ // Every person and activity is the calculation scope; the narrowing only chooses which show (#1342).
461
+ const people = $.let(Data.bind(rosterStaff));
462
+ const acts = $.let(Data.bind(rosterActivities));
463
+ const staffIndex = $.let(Data.bindPaged(rosterStaff, { indexes: [rosterStaffByTeamName, rosterStaffByPosition, rosterStaffByTrainer, rosterStaffByAgency, rosterStaffBySkill, rosterStaffByName] }));
464
+ const actIndex = $.let(Data.bindPaged(rosterActivities, { indexes: [rosterActivitiesByGroup] }));
465
+ const slice = $.let(Slice.bindRecords("ex.rosterCustomInspector", {
466
+ duty: { source: byTime, range: "start" },
467
+ people: { source: staffIndex, icon: { prefix: "fas", name: "user" }, search: ["name", "skills"],
468
+ fields: { team: { label: "Group", hints: ["inbound", "picking", "dispatch"] }, position: { label: "Position", hints: ["lead", "deputy", "acting", "crew"] },
469
+ trainer: { label: "Trainer" }, agency: { label: "Agency" }, skills: { label: "Skill" }, name: { label: "Name" } } },
470
+ activities: { source: actIndex, icon: { prefix: "fas", name: "list-check" }, fields: { group: { label: "Group", hints: ["inbound", "picking", "dispatch", null] } } },
471
+ }, { presets: [{ id: "leads", name: "Leads", where: { people: { position: ["lead", "deputy", "acting"] } } }] }));
472
+ const firms = $.let(Data.bind(rosterAgencies));
473
+ const times = $.let(Data.bind(rosterShiftTimes));
474
+ const teams = $.let(Data.bind(rosterTeams));
475
+ const roles = $.let(Data.bind(rosterPositions));
476
+ const ideas = $.let(Data.bind(rosterSuggestions));
477
+ // The duty's own form: its unpaid break and its note, each change the duty written back whole.
478
+ const dutyForm = $.const(East.function([WarehouseDuty, FunctionType([WarehouseDuty], NullType)], UIComponentType, (_$2, row, update) => _jsxs(VStack, { children: [_jsx(Field.FloatInput, { label: "Custom unpaid break", value: row.unpaidBreak, min: 0, max: 120, step: 15, onChange: (_$3, value) => update({
479
+ who: row.who, start: row.start, end: row.end, shift: row.shift, team: row.team, role: row.role, task: row.task,
480
+ unpaidBreak: value, agreed: row.agreed, costCentre: row.costCentre, note: row.note
481
+ }) }), _jsx(Field.StringInput, { label: "Custom note", value: row.note, onChange: (_$3, value) => update({
482
+ who: row.who, start: row.start, end: row.end, shift: row.shift, team: row.team, role: row.role, task: row.task,
483
+ unpaidBreak: row.unpaidBreak, agreed: row.agreed, costCentre: row.costCentre, note: value
484
+ }) })] })));
485
+ // The target's own form: the heads it needs.
486
+ const coverForm = $.const(East.function([WarehouseCover, FunctionType([WarehouseCover], NullType)], UIComponentType, (_$2, row, update) => _jsx(Field.IntegerInput, { label: "Custom heads", value: row.heads, min: 0n, step: 1n, onChange: (_$3, value) => update({
487
+ day: row.day, team: row.team, shift: row.shift, heads: value, hours: row.hours, basis: "planner"
488
+ }) })));
489
+ return _jsx(Box, { width: "100%", height: "760px", children: _jsx(Roster, { id: "rosterCustomInspector", week: { start: "sunday" }, slice: { slice }, inspector: true, view: { layout: "shifts", period: "day", date: new Date("2028-03-09T00:00:00Z") }, events: { duty: Schedule.events(rota, {
490
+ name: "Duty", icon: "user-clock", entries,
491
+ start: "start", end: "end", resource: { field: "who", of: { staff: "people", agency: "agencies" } },
492
+ shift: "shift", group: "team", position: "role", activity: "task",
493
+ hours: d => d.start.durationMinutes(d.end).subtract(d.unpaidBreak).divide(60.0),
494
+ inspector: dutyForm,
495
+ }) }, resources: {
496
+ people: Schedule.resources(people.read(), { name: "People", icon: "user", label: p => p.name, group: p => p.team,
497
+ position: p => p.position, skills: p => p.skills, contract: p => p.contract, agency: p => p.agency,
498
+ trainer: p => p.trainer, trainee: p => p.trainee, usual: p => p.usual }),
499
+ agencies: Schedule.resources(firms.read(), { name: "Agencies", icon: "building", label: a => a.name }),
500
+ }, periods: Schedule.periods(weeks, { every: "week", status: { field: "state", draft: "open", published: "issued" }, dismissed: "dismissed" }), targets: Roster.targets(cover, { day: "day", group: "team", shift: "shift", positions: "heads", hours: "hours", inspector: coverForm }), shifts: Roster.shifts(times.read(), { label: s => s.label, code: s => s.code, start: s => s.start, hours: s => s.hours, order: s => s.order }), groups: Roster.groups(teams.read(), { label: t => t.label, order: t => t.order }), positions: Roster.positions(roles.read(), { label: p => p.label, code: p => p.code, lead: p => p.lead, order: p => p.order }), activities: Roster.activities(acts.read(), { label: a => a.label, code: a => a.code, group: a => a.group, order: a => a.order }), proposals: Roster.proposals(ideas.read(), { person: s => s.person, day: s => s.day, group: s => s.team, shift: s => s.shift, reason: s => s.why }), rules: { rest: 10.0 }, library: [Roster.library.people(), Roster.library.activities(), Roster.library.tab(roles.read(), {
501
+ name: "Positions", icon: "id-badge", label: p => p.label, meta: p => some(p.code),
502
+ drop: (_p, key) => Schedule.patch(WarehouseDuty, { role: key }),
503
+ })] }) });
196
504
  } })),
197
505
  });
198
506
  //# sourceMappingURL=roster.examples.js.map