@micha.bigler/ui-core-micha 3.14.3 → 4.0.0

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 (367) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +7 -0
  3. package/dist/auth/UserMenu.js +1 -1
  4. package/dist/auth/apiClient.js +7 -6
  5. package/dist/chat/ChatList.js +1 -1
  6. package/dist/chat/ChatThread.js +2 -1
  7. package/dist/chat/ChatWorkspace.js +1 -1
  8. package/dist/chat/ModelPicker.js +2 -5
  9. package/dist/chat/kitSxRegistry.js +9 -0
  10. package/dist/chat/useChatSocket.js +1 -1
  11. package/dist/components/LoginForm.js +1 -1
  12. package/dist/components/SecurityComponent.js +1 -1
  13. package/dist/components/UserListComponent.js +2 -8
  14. package/dist/components/charts/ChartFrame.js +1 -1
  15. package/dist/components/charts/ScatterChart.js +2 -1
  16. package/dist/components/charts/kitSxRegistry.js +6 -0
  17. package/dist/entries/charts.js +6 -0
  18. package/dist/entries/chat.js +8 -0
  19. package/dist/entries/markdown.js +1 -0
  20. package/dist/index.js +0 -14
  21. package/dist/layout/SectionNav.js +1 -1
  22. package/dist/messaging/AttachmentList.js +2 -2
  23. package/dist/messaging/Composer.js +2 -1
  24. package/dist/messaging/ConversationLaunchers.js +1 -4
  25. package/dist/messaging/ConversationList.js +1 -1
  26. package/dist/messaging/DirectMessageLauncher.js +1 -1
  27. package/dist/messaging/MessagingProvider.js +0 -0
  28. package/dist/messaging/PollCard.js +7 -5
  29. package/dist/messaging/ReadTicks.js +2 -2
  30. package/dist/messaging/Thread.js +5 -3
  31. package/dist/notifications/NotificationBell.js +4 -7
  32. package/dist/notifications/NotificationSettings.js +1 -1
  33. package/dist/onboarding/OnboardingProvider.js +3 -6
  34. package/dist/pages/AccountPage.js +1 -2
  35. package/dist/pages/LoginPage.js +2 -3
  36. package/dist/pages/PasswordChangePage.js +2 -3
  37. package/dist/pages/PasswordInvitePage.js +2 -3
  38. package/dist/pages/PasswordResetRequestPage.js +2 -3
  39. package/dist/pages/SignUpPage.js +2 -3
  40. package/dist/pages/SignupConfirmPage.js +2 -3
  41. package/dist/theme/createAppTheme.js +5 -4
  42. package/dist/theme/kitSxEntry.js +11 -0
  43. package/dist/theme/kitSxRegistry.js +2 -15
  44. package/dist/theme/themeCompleteness.js +3 -12
  45. package/dist/utils/auth-errors.js +4 -3
  46. package/package.json +41 -16
  47. package/.github/workflows/publish.yml +0 -126
  48. package/DESIGN.md +0 -60
  49. package/WORK_ORDERS.md +0 -148
  50. package/dev/entries.jsx +0 -721
  51. package/dev/fixtures.js +0 -26
  52. package/dev/main.jsx +0 -75
  53. package/dev/mockTransport.jsx +0 -128
  54. package/docs/CHART-LAYOUT.md +0 -157
  55. package/docs/DARK-TOKENS.md +0 -140
  56. package/docs/TYPE-SCALE.md +0 -111
  57. package/docs/messaging-deviations.md +0 -118
  58. package/index.html +0 -12
  59. package/renovate.json +0 -10
  60. package/scripts/chart-api-census.mjs +0 -415
  61. package/src/auth/AuthContext.jsx +0 -158
  62. package/src/auth/ReauthModal.jsx +0 -82
  63. package/src/auth/UserMenu.jsx +0 -121
  64. package/src/auth/apiClient.jsx +0 -232
  65. package/src/auth/authApi.jsx +0 -539
  66. package/src/auth/authConfig.jsx +0 -34
  67. package/src/auth/reauth.js +0 -47
  68. package/src/chat/ChatComposer.jsx +0 -36
  69. package/src/chat/ChatList.jsx +0 -87
  70. package/src/chat/ChatThread.jsx +0 -128
  71. package/src/chat/ChatWorkspace.jsx +0 -244
  72. package/src/chat/MarkdownReply.jsx +0 -22
  73. package/src/chat/ModelPicker.jsx +0 -85
  74. package/src/chat/api.js +0 -18
  75. package/src/chat/useChatSocket.js +0 -32
  76. package/src/chat/wire.js +0 -7
  77. package/src/components/AccessCodeManager.jsx +0 -288
  78. package/src/components/AccessCodeSingleUseToggle.jsx +0 -92
  79. package/src/components/AllowedEmailDomainsManager.jsx +0 -92
  80. package/src/components/AuthFactorRequirementCard.jsx +0 -95
  81. package/src/components/BulkInviteCsvTab.jsx +0 -198
  82. package/src/components/EmptyState.jsx +0 -38
  83. package/src/components/LoginForm.jsx +0 -159
  84. package/src/components/MFAComponent.jsx +0 -350
  85. package/src/components/MfaLoginComponent.jsx +0 -252
  86. package/src/components/MobileBottomNav.jsx +0 -82
  87. package/src/components/PasskeysComponent.jsx +0 -252
  88. package/src/components/PasswordChangeForm.jsx +0 -87
  89. package/src/components/PasswordResetRequestForm.jsx +0 -43
  90. package/src/components/PasswordSetForm.jsx +0 -97
  91. package/src/components/ProfileComponent.jsx +0 -348
  92. package/src/components/QrSignupManager.jsx +0 -404
  93. package/src/components/QrSignupValidityManager.jsx +0 -106
  94. package/src/components/RegistrationMethodsManager.jsx +0 -150
  95. package/src/components/SecurityComponent.jsx +0 -141
  96. package/src/components/SocialLoginButtons.jsx +0 -83
  97. package/src/components/SoftChip.jsx +0 -93
  98. package/src/components/StatTile.jsx +0 -39
  99. package/src/components/SupportRecoveryRequestsTab.jsx +0 -482
  100. package/src/components/TurnstileWidget.jsx +0 -99
  101. package/src/components/UserInviteComponent.jsx +0 -76
  102. package/src/components/UserListComponent.jsx +0 -618
  103. package/src/components/charts/BarChart.jsx +0 -102
  104. package/src/components/charts/ChartFrame.jsx +0 -214
  105. package/src/components/charts/LineChart.jsx +0 -126
  106. package/src/components/charts/ScatterChart.jsx +0 -299
  107. package/src/components/charts/TimeSeriesChart.jsx +0 -300
  108. package/src/components/charts/chartDefaults.js +0 -644
  109. package/src/components/charts/chartLabels.js +0 -36
  110. package/src/components/charts/exportChart.js +0 -258
  111. package/src/components/charts/formatters.js +0 -39
  112. package/src/components/charts/palette.js +0 -33
  113. package/src/components/charts/yearTickInterval.js +0 -7
  114. package/src/i18n/authTranslations.ts +0 -2651
  115. package/src/i18n/chartsTranslations.ts +0 -91
  116. package/src/i18n/chatTranslations.ts +0 -55
  117. package/src/i18n/messagingTranslations.ts +0 -120
  118. package/src/i18n/notificationsTranslations.ts +0 -32
  119. package/src/i18n/onboardingTranslations.ts +0 -29
  120. package/src/i18n/sectionNavTranslations.ts +0 -20
  121. package/src/i18n/uiCoreTranslations.js +0 -29
  122. package/src/i18n/userMenuTranslations.ts +0 -5
  123. package/src/index.js +0 -158
  124. package/src/layout/PageLayout.jsx +0 -36
  125. package/src/layout/SectionNav.jsx +0 -320
  126. package/src/messaging/AttachmentList.jsx +0 -145
  127. package/src/messaging/Composer.jsx +0 -197
  128. package/src/messaging/ConversationLaunchers.jsx +0 -51
  129. package/src/messaging/ConversationList.jsx +0 -109
  130. package/src/messaging/DirectMessageLauncher.jsx +0 -112
  131. package/src/messaging/MessageBubble.jsx +0 -183
  132. package/src/messaging/MessagingProvider.jsx +0 -0
  133. package/src/messaging/MessagingScopeConfig.jsx +0 -24
  134. package/src/messaging/PollCard.jsx +0 -163
  135. package/src/messaging/ReactionBar.jsx +0 -43
  136. package/src/messaging/ReadTicks.jsx +0 -82
  137. package/src/messaging/Thread.jsx +0 -195
  138. package/src/messaging/api.js +0 -48
  139. package/src/messaging/composerImageCompression.js +0 -35
  140. package/src/messaging/linkifyText.jsx +0 -37
  141. package/src/notifications/NotificationBell.jsx +0 -84
  142. package/src/notifications/NotificationSettings.jsx +0 -330
  143. package/src/notifications/NotificationsProvider.jsx +0 -224
  144. package/src/notifications/PopupSurface.jsx +0 -105
  145. package/src/notifications/api.js +0 -55
  146. package/src/notifications/feedApi.js +0 -18
  147. package/src/notifications/realtime.jsx +0 -138
  148. package/src/notifications/serviceWorker/sw.js +0 -50
  149. package/src/onboarding/OnboardingProvider.jsx +0 -326
  150. package/src/onboarding/OnboardingWizard.jsx +0 -61
  151. package/src/onboarding/WizardDialogShell.jsx +0 -51
  152. package/src/onboarding/api.js +0 -13
  153. package/src/onboarding/stepSelection.js +0 -16
  154. package/src/onboarding/steps/BrowserPushStep.jsx +0 -121
  155. package/src/onboarding/steps/CompleteNameStep.jsx +0 -46
  156. package/src/onboarding/steps/CookieConsentStep.jsx +0 -133
  157. package/src/onboarding/steps/PwaInstallStep.jsx +0 -47
  158. package/src/pages/AccountPage.jsx +0 -394
  159. package/src/pages/LoginPage.jsx +0 -343
  160. package/src/pages/PasswordChangePage.jsx +0 -62
  161. package/src/pages/PasswordInvitePage.jsx +0 -177
  162. package/src/pages/PasswordResetRequestPage.jsx +0 -68
  163. package/src/pages/SignUpPage.jsx +0 -268
  164. package/src/pages/SignupConfirmPage.jsx +0 -155
  165. package/src/testing/checkKitIntegration.js +0 -233
  166. package/src/theme/createAppTheme.js +0 -423
  167. package/src/theme/fonts.js +0 -3
  168. package/src/theme/index.js +0 -21
  169. package/src/theme/kitSxRegistry.js +0 -285
  170. package/src/theme/themeCompleteness.js +0 -1045
  171. package/src/theme/tokens.js +0 -478
  172. package/src/utils/auth-errors.js +0 -76
  173. package/src/utils/authService.js +0 -223
  174. package/src/utils/resolveErrorText.js +0 -28
  175. package/src/utils/webauthn.js +0 -51
  176. package/tests/AccountPage.profileStatements.test.jsx +0 -162
  177. package/tests/AccountPage.test.jsx +0 -200
  178. package/tests/AuthContext.test.jsx +0 -129
  179. package/tests/BarChart.test.jsx +0 -112
  180. package/tests/BrowserPushStep.test.jsx +0 -110
  181. package/tests/ChartFrame.test.jsx +0 -336
  182. package/tests/ConversationList.test.jsx +0 -144
  183. package/tests/CookieConsentStep.test.jsx +0 -69
  184. package/tests/EmptyState.test.jsx +0 -62
  185. package/tests/LineChart.test.jsx +0 -80
  186. package/tests/MessagingProvider.test.jsx +0 -50
  187. package/tests/MobileBottomNav.test.jsx +0 -319
  188. package/tests/NotificationBell.test.jsx +0 -60
  189. package/tests/NotificationSettings.test.jsx +0 -276
  190. package/tests/NotificationsProvider.test.jsx +0 -240
  191. package/tests/OnboardingProvider.test.jsx +0 -294
  192. package/tests/OnboardingWizard.test.jsx +0 -143
  193. package/tests/PasswordChangeForm.test.jsx +0 -70
  194. package/tests/PasswordInvitePage.test.jsx +0 -142
  195. package/tests/PasswordSetForm.test.jsx +0 -70
  196. package/tests/PopupSurface.test.jsx +0 -142
  197. package/tests/ProfileComponent.refresh.test.jsx +0 -194
  198. package/tests/QrSignupManager.registrationContext.test.jsx +0 -106
  199. package/tests/QrSignupManagerInfoBox.test.jsx +0 -49
  200. package/tests/ScatterChart.test.jsx +0 -270
  201. package/tests/SectionNav.test.jsx +0 -363
  202. package/tests/SignUpPage.test.jsx +0 -208
  203. package/tests/SoftChip.test.jsx +0 -105
  204. package/tests/StatTile.test.jsx +0 -57
  205. package/tests/Thread.test.jsx +0 -151
  206. package/tests/TimeSeriesChart.test.jsx +0 -367
  207. package/tests/UserListComponent.test.jsx +0 -192
  208. package/tests/UserMenu.test.jsx +0 -171
  209. package/tests/apiClientLanguage.test.js +0 -33
  210. package/tests/apiClientPublicPaths.test.js +0 -178
  211. package/tests/authApiLogin.test.js +0 -42
  212. package/tests/authApiRegistration.test.js +0 -31
  213. package/tests/authErrorCodeRendering.test.jsx +0 -77
  214. package/tests/authErrors.test.js +0 -74
  215. package/tests/authTranslations.test.js +0 -74
  216. package/tests/chart-api-census.test.js +0 -366
  217. package/tests/chartAxisTickBand.test.jsx +0 -95
  218. package/tests/chartDefaults.test.js +0 -902
  219. package/tests/chartLabels.test.js +0 -50
  220. package/tests/chartsPalette.test.js +0 -99
  221. package/tests/chatApi.test.js +0 -48
  222. package/tests/chatDriverNames.test.jsx +0 -71
  223. package/tests/chatExports.test.js +0 -3
  224. package/tests/chatList.test.jsx +0 -84
  225. package/tests/chatPicker.test.jsx +0 -82
  226. package/tests/chatSocket.test.jsx +0 -26
  227. package/tests/chatThread.test.jsx +0 -116
  228. package/tests/chatTranslations.test.js +0 -47
  229. package/tests/chatWorkspace.test.jsx +0 -234
  230. package/tests/checkKitIntegration.test.jsx +0 -219
  231. package/tests/createAppTheme.test.js +0 -132
  232. package/tests/createAppThemeDark.test.js +0 -127
  233. package/tests/createAppThemeLightGuard.test.js +0 -40
  234. package/tests/createAppThemePageContrast.test.js +0 -108
  235. package/tests/createAppThemeTypography.test.js +0 -53
  236. package/tests/errorCallSiteGuard.test.js +0 -32
  237. package/tests/exportChart.test.js +0 -313
  238. package/tests/feedApi.test.js +0 -46
  239. package/tests/fixtures/light-theme-baseline.json +0 -2234
  240. package/tests/i18nAggregate.test.js +0 -120
  241. package/tests/i18nGermanVariant.test.js +0 -65
  242. package/tests/loginPageErrorResolution.test.jsx +0 -102
  243. package/tests/messagingApi.test.js +0 -32
  244. package/tests/messagingChunk6.test.jsx +0 -116
  245. package/tests/messagingCompactBubble.test.jsx +0 -64
  246. package/tests/messagingComposer.test.jsx +0 -136
  247. package/tests/messagingComposerChunk5.test.jsx +0 -113
  248. package/tests/messagingContractConformance.test.js +0 -69
  249. package/tests/messagingDirectMessageLauncher.test.jsx +0 -151
  250. package/tests/messagingExports.test.js +0 -21
  251. package/tests/messagingInteractions.test.jsx +0 -136
  252. package/tests/messagingLastMessagePreview.test.jsx +0 -93
  253. package/tests/messagingLinkify.test.jsx +0 -51
  254. package/tests/messagingMessageActions.test.jsx +0 -163
  255. package/tests/messagingMsg6.test.jsx +0 -105
  256. package/tests/messagingMsg6f.test.jsx +0 -155
  257. package/tests/messagingMsg6g.test.jsx +0 -166
  258. package/tests/messagingMsg6h.test.jsx +0 -136
  259. package/tests/messagingMsg6i.test.jsx +0 -129
  260. package/tests/messagingPollRendering.test.jsx +0 -142
  261. package/tests/messagingReactionMerge.test.jsx +0 -77
  262. package/tests/messagingReadTicksTooltip.test.jsx +0 -36
  263. package/tests/messagingThreadReceipts.test.jsx +0 -178
  264. package/tests/messagingThreadingQuoting.test.jsx +0 -87
  265. package/tests/messagingUnreadLifecycle.test.jsx +0 -78
  266. package/tests/notificationsApi.test.js +0 -41
  267. package/tests/notificationsExports.test.js +0 -34
  268. package/tests/onboardingDescriptors.test.js +0 -132
  269. package/tests/packageTreeShaking.test.js +0 -126
  270. package/tests/realtime.test.jsx +0 -219
  271. package/tests/resolveErrorText.test.js +0 -72
  272. package/tests/stepSelection.test.js +0 -53
  273. package/tests/themeCompleteness.test.js +0 -626
  274. package/tests/timeSeriesLegend.test.js +0 -99
  275. package/tests/userScreenTranslations.test.jsx +0 -218
  276. package/tsconfig.build.json +0 -18
  277. package/vite.config.mjs +0 -6
  278. package/work-orders/AUTH-1.md +0 -74
  279. package/work-orders/AUTH-2.md +0 -74
  280. package/work-orders/AUTH-4.md +0 -183
  281. package/work-orders/AUTH-5.md +0 -154
  282. package/work-orders/AUTH-7.md +0 -181
  283. package/work-orders/AUTH-8.md +0 -184
  284. package/work-orders/CHART-1.md +0 -285
  285. package/work-orders/CHART-2.md +0 -222
  286. package/work-orders/CHART-3.md +0 -163
  287. package/work-orders/CHART-4.md +0 -203
  288. package/work-orders/CHART-5.md +0 -226
  289. package/work-orders/CHART-6.md +0 -428
  290. package/work-orders/CHART-7.md +0 -175
  291. package/work-orders/DX-1.md +0 -169
  292. package/work-orders/DX-2.md +0 -188
  293. package/work-orders/DX-3.md +0 -195
  294. package/work-orders/I18N-1.md +0 -254
  295. package/work-orders/I18N-3.md +0 -207
  296. package/work-orders/MSG-3.md +0 -375
  297. package/work-orders/MSG-3b.md +0 -1002
  298. package/work-orders/MSG-3c.md +0 -378
  299. package/work-orders/MSG-3d.md +0 -268
  300. package/work-orders/MSG-3e.md +0 -164
  301. package/work-orders/MSG-3f.md +0 -391
  302. package/work-orders/MSG-6e.md +0 -308
  303. package/work-orders/MSG-6f.md +0 -214
  304. package/work-orders/MSG-6g.md +0 -244
  305. package/work-orders/MSG-6h.md +0 -238
  306. package/work-orders/MSG-6i.md +0 -271
  307. package/work-orders/MSG-6j.md +0 -117
  308. package/work-orders/NOTIF-12.md +0 -214
  309. package/work-orders/NOTIF-13.md +0 -142
  310. package/work-orders/NOTIF-14.md +0 -151
  311. package/work-orders/PRIM-1.md +0 -177
  312. package/work-orders/SHELL-1.md +0 -255
  313. package/work-orders/SHELL-2.md +0 -197
  314. package/work-orders/SHELL-3.md +0 -359
  315. package/work-orders/SHELL-4.md +0 -351
  316. package/work-orders/SHELL-5.md +0 -345
  317. package/work-orders/SHELL-6.md +0 -210
  318. package/work-orders/THEME-1.md +0 -587
  319. package/work-orders/THEME-10.md +0 -303
  320. package/work-orders/THEME-11.md +0 -208
  321. package/work-orders/THEME-2.md +0 -339
  322. package/work-orders/THEME-3.md +0 -210
  323. package/work-orders/THEME-4.md +0 -271
  324. package/work-orders/THEME-5.md +0 -341
  325. package/work-orders/THEME-6.md +0 -247
  326. package/work-orders/THEME-7.md +0 -82
  327. package/work-orders/THEME-8.md +0 -211
  328. package/work-orders/THEME-9.md +0 -194
  329. package/work-orders/UCM-AUTH-10.md +0 -490
  330. package/work-orders/UCM-AUTH-11.md +0 -185
  331. package/work-orders/UCM-AUTH-12.md +0 -98
  332. package/work-orders/UCM-AUTH-13.md +0 -47
  333. package/work-orders/UCM-AUTH-9.md +0 -207
  334. package/work-orders/UCM-CHART-10.md +0 -150
  335. package/work-orders/UCM-CHART-11.md +0 -132
  336. package/work-orders/UCM-CHART-12.md +0 -348
  337. package/work-orders/UCM-CHART-13.md +0 -122
  338. package/work-orders/UCM-CHART-14.md +0 -198
  339. package/work-orders/UCM-CHART-15.md +0 -187
  340. package/work-orders/UCM-CHART-16.md +0 -169
  341. package/work-orders/UCM-CHART-17.md +0 -195
  342. package/work-orders/UCM-CHART-18.md +0 -200
  343. package/work-orders/UCM-CHART-8.md +0 -147
  344. package/work-orders/UCM-CHART-9.md +0 -265
  345. package/work-orders/UCM-CHAT-1.md +0 -190
  346. package/work-orders/UCM-CHAT-2.md +0 -75
  347. package/work-orders/UCM-DEP-1.md +0 -63
  348. package/work-orders/UCM-I18N-4.md +0 -226
  349. package/work-orders/UCM-I18N-5.md +0 -211
  350. package/work-orders/UCM-MSG-20.md +0 -192
  351. package/work-orders/UCM-PRIM-2.md +0 -114
  352. package/work-orders/UCM-SHELL-7.md +0 -134
  353. package/work-orders/UCM-SHELL-8.md +0 -125
  354. package/work-orders/UCM-TEST-1.md +0 -304
  355. package/work-orders/UCM-THEME-13.md +0 -69
  356. package/work-orders/UCM-THEME-14.md +0 -312
  357. package/work-orders/UCM-THEME-15.md +0 -356
  358. package/work-orders/UCM-THEME-16.md +0 -80
  359. package/work-orders/UCM-THEME-17.md +0 -111
  360. package/work-orders/UCM-THEME-18.md +0 -184
  361. package/work-orders/assets/AUTH-4-user-list-card.html +0 -407
  362. package/work-orders/assets/PRIM-1-dashboard-primitives.html +0 -624
  363. package/work-orders/assets/SHELL-2-account-navigation.html +0 -1159
  364. package/work-orders/assets/THEME-1-baseline-reference-sheet.html +0 -1352
  365. package/work-orders/assets/UCM-CHAT-1-ai-chat.html +0 -447
  366. package/work-orders/assets/UCM-THEME-13-dark-prototype.html +0 -390
  367. package/work-orders/assets/UCM-THEME-17-type-scale.html +0 -391
@@ -1,222 +0,0 @@
1
- # WORK ORDER CHART-2 (ui-core-micha) — a time-series chart preset with range picker and series toggles
2
-
3
- **EXECUTION DIRECTIVE.** If you are the implementer reading this as your own spec, this section is not
4
- addressed to you — it tells the Orchestrator how to invoke you; you ARE that invocation, do not shell
5
- out to `codex exec`. Orchestrator: implement through `codex exec` in the background, invoked **directly
6
- via Bash** (never the `debugger`/`*_coder` Agent wrappers) with **both** flags `--skip-git-repo-check`
7
- and `--dangerously-bypass-approvals-and-sandbox`, prompt passed as a positional argument from this
8
- file. Fall back to direct Claude implementation only on Codex quota / rate-limit / non-zero exit.
9
-
10
- Feature-level WO (Expertenchat envelope). Extends CHART-1. Operator request 2026-08-04 from jg's event
11
- activity chart; the same controls are wanted wherever a time series is charted, and `django-core-micha`
12
- ACT-1 makes activity data available to every app.
13
-
14
- ## TIER
15
- Tier 2 — shared-core UI consumed by every app. Independent `reviewer` + `ui_reviewer` mandatory.
16
-
17
- ## WHY
18
-
19
- CHART-1 already ships `ChartFrame` (Paper + title + **toolbar slot** + loading / error / empty +
20
- responsive container + SVG/PNG export + a11y), a themed `BarChart`, `getNeutralChartPalette` and
21
- `chartsTranslations`. The frame's toolbar slot exists for exactly this and is currently unused by any
22
- consumer.
23
-
24
- jg's `ActivitySection.jsx` bypasses the kit entirely (`import { BarChart } from '@mui/x-charts/BarChart'`
25
- at `:9`) and hand-rolls its own `Paper`, loading, error and empty states. Rather than jg building a
26
- range picker and series checkboxes locally, they belong in the kit — every app charting a time series
27
- wants both.
28
-
29
- ## SCOPE
30
-
31
- **A. A range picker for the toolbar slot.** Presets **1 day / 1 week / 1 month / 1 year** (operator,
32
- 2026-08-04), emitting the selected range to the host. The host owns fetching; the picker owns the choice.
33
-
34
- The range must also carry the **granularity** the consumer should request — dcm ACT-1's query endpoint
35
- aggregates server-side, and a year of 4-hour buckets is ~2190 points, which no chart can render
36
- usefully. A picker that changes the window but not the resolution reproduces exactly the
37
- unreadable-density problem this WO exists to fix.
38
-
39
- **The range-to-resolution mapping (operator, 2026-08-04) — implement exactly this:**
40
-
41
- | Range | Resolution | Points |
42
- |---|---|---|
43
- | 1 day | 1 hour | 24 |
44
- | 1 week | 4 hours | ~42 |
45
- | 1 month | 1 day | ~30 |
46
- | 1 year | 1 month | 12 |
47
-
48
- Every preset lands between a dozen and fifty points, which is what keeps the chart readable at any
49
- range. **The resolution follows the window; it is not an independent control** and must not be exposed
50
- as one.
51
-
52
-
53
- **B. Series toggles for the toolbar slot.** Check/uncheck each series independently.
54
-
55
- **These are not cosmetic.** In jg's chart two series share one y-axis with different units — user counts
56
- reach 28 while presence-hours sit near zero, so the second series is invisible. Toggling one off
57
- rescales the axis and makes the other readable. Whether to offer a second y-axis instead is a real
58
- alternative: **if you do not add one, say so deliberately** rather than leaving it unconsidered.
59
-
60
- Deselecting every series must leave a stable empty chart — not a crash, not a permanent spinner.
61
-
62
- **C. Compose them into a preset** on `ChartFrame` + the existing `BarChart`, so a consumer gets frame,
63
- chart, picker and toggles from one component and passes data plus callbacks.
64
-
65
- **Presentational only — the host fetches.** Take `data`, the current range and series selection, and
66
- callbacks; do not call any endpoint. If all four hosts later write the identical fetch against ACT-1's
67
- endpoint, *that* is the signal to add an adapter — and note the inverse trap from this estate's recent
68
- history: building the shared thing before two identical consumers exist is how you generalise against a
69
- single case and get it wrong.
70
-
71
- ## CONTEXT PACKAGE — verified current state (Orchestrator, Implementation map)
72
-
73
- Work from this package; do not explore broadly from scratch — open only the named files to verify.
74
-
75
- **New file: `src/components/charts/TimeSeriesChart.jsx`** (generic name — non-goal bars anything
76
- app-specific; this is not called `ActivityChart`). Export it in `src/index.js` right after the
77
- existing `LineChart` export (`:46`): `export { TimeSeriesChart } from './components/charts/TimeSeriesChart';`.
78
-
79
- **`ChartFrame`'s toolbar slot (`src/components/charts/ChartFrame.jsx`)** — the prop is named
80
- **`toolbar`** (not `toolbarSlot`), destructured `:20`, rendered `:74` inside a `Box` next to the
81
- title/subtitle, `justifyContent: 'space-between'`. Full prop list `:17-34`:
82
- `title, subtitle, toolbar, loading, error, isEmpty, emptyMessage, minHeight, aspect, exportOptions,
83
- onExportSvg, onExportPng, ariaLabel, children, variant, sx`. State precedence `:58-65`:
84
- `loading → error → isEmpty → children` — `TimeSeriesChart` passes its own `loading`/`error`/`isEmpty`
85
- straight through, it does not reimplement these.
86
-
87
- **`BarChart` (`src/components/charts/BarChart.jsx`, 44 lines)** — required props `xAxisLabel`/
88
- `yAxisLabel` (throws if absent, `:27-29`). `series` is a bare array of MUI X-Charts series objects,
89
- e.g. `{ data: [...], label, ... }` — no ucm wrapping. Colors: `palette || neutralPalette.categorical`
90
- (from `useNeutralChartPalette()`), assigned **positionally by index** — series `i` gets
91
- `categorical[i]`. `hideLegend={series.length <= 1}`.
92
-
93
- **Data contract for `TimeSeriesChart`** (design decision, state it): the host has already fetched
94
- and shaped the series (dcm ACT-1's query endpoint returns rolled-up buckets; this component knows
95
- nothing about that origin). Prop shape:
96
- ```jsx
97
- <TimeSeriesChart
98
- title
99
- xAxisLabel
100
- yAxisLabel
101
- data={{ xLabels: [...], series: [{ key: 'users', label: 'Users', data: [...] }, { key: 'presence', label: 'Presence', data: [...] }] }}
102
- loading
103
- error
104
- onRangeChange={(rangeKey, granularity) => {...}} // rangeKey: '1d'|'1w'|'1m'|'1y'
105
- />
106
- ```
107
- Range selection is **self-managed internal state** (default a sensible preset, e.g. `'1w'`), emitting
108
- `onRangeChange(rangeKey, granularity)` on every change so the host can refetch `data` for the new
109
- window — the host owns fetching (non-goal), this component owns only which range is currently
110
- selected and telling the host what changed. Series-toggle visibility is **also self-managed internal
111
- state** (`Set` of visible series keys, all visible by default) — deselecting is a pure client-side
112
- filter over already-fetched `data`, no host round-trip needed, so no `onSeriesToggle` callback is
113
- required (optional to add if a reviewer disagrees, but the WO's own framing — "these are not
114
- cosmetic... toggling rescales the axis" — describes a display-only operation).
115
-
116
- **Range-to-granularity mapping — implement exactly, no per-app override:**
117
- `'1d' → 'hour'`, `'1w' → '4hour'`, `'1m' → 'day'`, `'1y' → 'month'` (match whatever granularity
118
- vocabulary dcm ACT-1's query endpoint expects once it publishes — since CHART-2 does not fetch, the
119
- exact string values are this component's own contract, name them clearly, e.g. `'hour' | '4hour' |
120
- 'day' | 'month'`, and state the chosen vocabulary in the completion note so ACT-1/ACT-3 can align).
121
-
122
- **Range picker widget — decide and state (per WO's own "if you consider X, say so"):** this repo has
123
- **zero existing `ToggleButtonGroup` usage** (grepped, confirmed) but an established, deliberate
124
- `RadioGroup` + `FormControlLabel` convention for single-choice controls
125
- (`src/messaging/PollCard.jsx:1-140`, code comment there explains why: native arrow-key nav + grouped
126
- screen-reader announcement — "a bare ungrouped `Radio` per option... loses both"). Prefer
127
- `RadioGroup`/`FormControlLabel` to match this repo's convention unless a segmented-button visual is
128
- judged clearly better for a 4-preset picker — either is acceptable, but state which and why.
129
-
130
- **Series toggles — `Checkbox` + `FormControlLabel` per series**, matching this repo's established
131
- multi-choice pattern (`PollCard.jsx`'s multi-choice mode; also `Composer.jsx:181`,
132
- `ProfileComponent.jsx:232,244`). **Accessibility requirement, verified as a real gap**: colors from
133
- `getNeutralChartPalette` are assigned positionally with no name-to-color map and no shape
134
- differentiation (`src/components/charts/palette.js`, full file) — so each checkbox's `label` MUST be
135
- the series' text name (`FormControlLabel` gives this for free), not a bare colored swatch. Optionally
136
- also render a small colored swatch next to the label (matching the series' `BarChart` color at the
137
- same index) as a secondary, non-exclusive cue.
138
-
139
- **Second y-axis — decision required by the WO, make it explicitly:** `BarChart` already supports a
140
- dual-axis shape (`yAxisKey: 'secondary'` per series, paired with `yAxis={[{id:'primary'},
141
- {id:'secondary'}]}`), so it is technically available — but **do not use it here**. Scope B's series
142
- toggles are the WO's chosen, required fix for the shared-axis-different-units problem (jg's user
143
- counts vs. presence-hours); a second y-axis is the *alternative* the WO explicitly did not choose.
144
- State this decision plainly in the completion note — do not leave it to be inferred from absence.
145
-
146
- **Empty state — all series deselected:** `ChartFrame`'s `isEmpty` prop drives its own empty-state
147
- `Alert` (`:58-65`). When every series is toggled off, `TimeSeriesChart` must NOT pass an empty
148
- `series` array to `BarChart` (which would likely throw or render a blank axis) — instead set
149
- `isEmpty` truthy on `ChartFrame` and skip rendering `BarChart` entirely for that render, matching
150
- required test 3 ("stable empty chart: no crash, no spinner").
151
-
152
- **i18n — extend `src/i18n/chartsTranslations.ts` in place** (single `chartsTranslations` object,
153
- `<ComponentName>.<CONSTANT_KEY>` convention, all four `de/fr/en/sw`, `:1-38` for the existing shape
154
- to match). New keys needed: `TimeSeriesChart.RANGE_1_DAY`, `RANGE_1_WEEK`, `RANGE_1_MONTH`,
155
- `RANGE_1_YEAR`, and a label for the series-toggle group (e.g. `TimeSeriesChart.SERIES_LABEL`).
156
-
157
- **Tests — new `tests/TimeSeriesChart.test.jsx`**, matching `tests/ChartFrame.test.jsx`/
158
- `tests/BarChart.test.jsx` conventions (both fully precedent): `// @vitest-environment jsdom` header,
159
- vitest + `@testing-library/react`, `afterEach(cleanup)`, a minimal per-file `i18next.createInstance()`
160
- + `I18nextProvider` (not the app's real i18n config), wrapped in `ThemeProvider theme={createTheme()}`.
161
- For test 1 (range emits granularity) and the series-filter tests, mock `@mui/x-charts/BarChart` itself
162
- via `vi.hoisted` + `vi.mock` to inspect exact props passed through (`BarChart.test.jsx`'s own pattern,
163
- `chartSpy.mock.calls[0][0]`) — do not mock ucm's own `BarChart`/`ChartFrame`, the point is testing the
164
- real composition.
165
-
166
- ## NON-GOALS / DO NOT TOUCH
167
- - Do not fetch data, and do not import anything app-specific.
168
- - Do not change `ChartFrame`, `BarChart`, `LineChart` or the palette beyond what composing requires. If
169
- `ChartFrame` lacks something, **extend it deliberately and say so** — do not work around it in the
170
- preset.
171
- - Do not add a charting dependency; MUI X-Charts is already the kit's basis.
172
- - No persistence of the picker or toggle state — that is the host's decision.
173
-
174
- ## RISKS
175
- - The toolbar slot has no consumer today, so this is its first real use. Expect to find gaps in it;
176
- fixing them is in scope, working around them is not.
177
- - Accessibility: the picker and checkboxes are controls, not decoration — labels, keyboard operation and
178
- state must be right. Series colour alone must not be the only way to tell which toggle maps to which
179
- series. `ui_reviewer` should check this specifically.
180
- - Every host inherits the preset; a visible behaviour change beyond jg.
181
-
182
- ## REQUIRED TESTS TO WRITE
183
- Narrow and behavioural. Do NOT run the full suite.
184
-
185
- 1. Selecting a range emits both the range **and** the mapped granularity to the callback — assert the
186
- granularity, which is the half most likely to be dropped.
187
- 2. Toggling a series off removes it from the rendered chart and leaves the others.
188
- 3. Toggling **all** series off renders a stable empty chart: no crash, no spinner.
189
- 4. The preset renders `ChartFrame`'s empty state for empty data, exactly once.
190
-
191
- **Non-vacuity:** test 1 must fail if only the range is emitted. Assert the emitted payload, not that a
192
- callback fired.
193
-
194
- ## TEST SCOPE FOR THE GATE (orchestrator)
195
- The chart component tests. Not the full suite.
196
-
197
- ## TARGET REPO
198
- `C:\Users\biglmi\Documents\webapps\ui-core-micha`. Branch `main` (or `develop` if present). Publish per
199
- the repo's release flow; consumer pin bumps are separate.
200
-
201
- ## MINI-HANDOVER (pastable)
202
-
203
- > Repo: `C:\Users\biglmi\Documents\webapps\ui-core-micha` (branch `main`, or `develop` if present).
204
- > Work order: `work-orders/CHART-2.md` — read it fully, then follow the `orchestrate-codex` skill.
205
- >
206
- > Extends CHART-1, whose `ChartFrame` already has an unused **toolbar slot** — that is where the range
207
- > picker and series toggles go. **Presentational only: the host fetches, this component takes data and
208
- > callbacks.**
209
- >
210
- > The one thing most likely to be half-done: the range picker must emit a **granularity** alongside the
211
- > window. dcm ACT-1 aggregates server-side, and a 1-year range at 4-hour buckets is ~2190 points — a
212
- > picker that widens the window without coarsening the resolution reproduces the unreadable chart this
213
- > WO exists to fix. Test 1 asserts the granularity for that reason.
214
- >
215
- > The series toggles are a real fix, not a preference: jg's two series share one y-axis with different
216
- > units, which is why one of them is invisible today. If you consider a second y-axis instead, decide
217
- > deliberately and say so.
218
-
219
- ## PROGRESS CONTRACT
220
- Emit `PLAN: <steps>` up front, then a single-line `PROGRESS: [<n>/<total>] <action>` before every
221
- relevant action and `PROGRESS: [<n>/<total>] done` on completion, spaced so no gap exceeds ~2 min,
222
- stdout unbuffered, and exactly one final `RESULT: DONE|BLOCKED <reason>`.
@@ -1,163 +0,0 @@
1
- # WORK ORDER CHART-3 (ui-core-micha) — fix TimeSeriesChart rendering nothing on async data load
2
-
3
- **EXECUTION DIRECTIVE.** If you are the implementer reading this as your own spec, this section is not
4
- addressed to you — it tells the Orchestrator how to invoke you; you ARE that invocation, do not shell
5
- out to `codex exec`. Orchestrator: implement through `codex exec` in the background, invoked **directly
6
- via Bash** (never the `debugger`/`*_coder` Agent wrappers) with **both** flags `--skip-git-repo-check`
7
- and `--dangerously-bypass-approvals-and-sandbox`, prompt passed as a positional argument from this
8
- file. Fall back to direct Claude implementation only on Codex quota / rate-limit / non-zero exit.
9
-
10
- Bug-fix WO on the landed CHART-2. Found live in jg-ferien's "Aktivität" screen (ACT-3 consumer): the
11
- toolbar (range picker + series checkboxes) renders correctly, but the chart body never appears —
12
- `ChartFrame`'s empty-state `Alert` shows permanently, even once real data has loaded.
13
-
14
- ## TIER
15
- Tier 2 — shared-core UI consumed by every app that adopts `TimeSeriesChart`.
16
- Independent `reviewer` + `ui_reviewer` mandatory.
17
-
18
- ## WHY
19
-
20
- `TimeSeriesChart` initializes `visibleKeys` via a **lazy `useState` initializer** that reads
21
- `seriesConfig` (derived from the `data` prop) only once, at first mount:
22
-
23
- ```js
24
- const [visibleKeys, setVisibleKeys] = useState(
25
- () => new Set(seriesConfig.map((series) => series.key)),
26
- );
27
- ```
28
-
29
- jg-ferien's `ActivitySection.jsx` mounts `TimeSeriesChart` with `data = { xLabels: [], series: [] }`
30
- (its own initial `useState`), then fetches asynchronously in a `useEffect` and updates `data` only
31
- once the API call resolves — the standard "mount empty, fill later" pattern. Because the lazy
32
- initializer never re-runs on prop updates, `visibleKeys` stays a permanently empty `Set` even after
33
- real series arrive. Downstream:
34
-
35
- ```js
36
- const visibleSeries = seriesConfig.filter((series) => visibleKeys.has(series.key)); // always []
37
- const isDataEmpty = seriesConfig.length === 0 || visibleSeries.length === 0; // always true
38
- ```
39
-
40
- `isDataEmpty` is permanently `true`, so `ChartFrame` renders its empty-state `Alert` forever, no
41
- matter how much data loads. The toolbar renders fine because the range picker and the series
42
- checkboxes are driven directly off the live `seriesConfig`, not off `visibleKeys` — only the chart
43
- body is gated on the stale set. This reproduces on every host that fetches asynchronously after
44
- mount (the overwhelmingly common case), not just jg-ferien.
45
-
46
- ## SCOPE
47
-
48
- **Fix `visibleKeys` to track newly-appeared series without discarding a user's manual toggle.**
49
-
50
- Required behaviour:
51
- 1. When `data`/`seriesConfig` changes and introduces a series key not seen before, that key must
52
- become visible by default (matches current first-mount behaviour, just not mount-only).
53
- 2. A series key the user has manually toggled off must **stay off** across a range change that
54
- still includes that key (e.g. don't reset all toggles to "all visible" on every `data` update —
55
- that would silently undo the user's own toggle, a second bug of the opposite kind).
56
- 3. A key that disappears from `seriesConfig` (e.g. a range change that drops a series) and later
57
- reappears is treated as new for step 1 (simplest correct behaviour; do not attempt to remember
58
- toggle state for absent keys unless it falls out naturally from the fix).
59
-
60
- This is a "seen this key before, default it visible; otherwise respect the user's own toggle" sync,
61
- not a full rewrite of the toggle model — `toggleSeries`, the checkbox rendering, and the empty-state
62
- logic in SCOPE stay as they are; only the initialization/sync of `visibleKeys` changes.
63
-
64
- ## NON-GOALS / DO NOT TOUCH
65
- - Do not change `ChartFrame`, `BarChart`, `LineChart`, or the palette.
66
- - Do not change the range-picker or the range-to-granularity mapping (`RANGE_OPTIONS`).
67
- - Do not add an `onSeriesToggle` callback or any other new prop/API surface — this is a bug fix
68
- within the existing component contract, not a scope extension.
69
- - Do not touch `chartsTranslations.ts` — this WO is unrelated to i18n (a separate, already-diagnosed
70
- issue in a different repo is being fixed independently).
71
-
72
- ## RISKS
73
- - The fix must not reintroduce the mount-time bug in a different form (e.g. resetting on every
74
- render, or resetting on every `data` change including in-place mutations of the same series set —
75
- that would silently discard the user's manual toggles on every range change, which is the
76
- behaviour test 2 below exists to catch).
77
- - Every host that has adopted `TimeSeriesChart` since CHART-2 shipped is affected by this fix
78
- (currently only jg-ferien via ACT-3); this changes rendering behaviour for all of them, in the
79
- correct direction (chart now actually renders).
80
-
81
- ## REQUIRED TESTS TO WRITE
82
- Narrow and behavioural, extending `tests/TimeSeriesChart.test.jsx`. Do NOT run the full suite.
83
-
84
- 1. **Regression test for this exact bug**: mount `TimeSeriesChart` with empty `data`
85
- (`{ xLabels: [], series: [] }`), then rerender with populated `data` (real `xLabels`/`series`) —
86
- assert the chart body renders (not `ChartFrame`'s empty state), i.e. `isEmpty` becomes `false`
87
- and `BarChart` receives the new series. This must fail against the current code and pass after
88
- the fix — it is the non-vacuity check for this WO.
89
- 2. **Toggle persistence across a data update**: toggle one series off, then rerender with updated
90
- `data` that still contains that series key (e.g. a value change, same keys) — assert that series
91
- is still filtered out (the user's manual toggle was not reset by the data update).
92
- 3. **New series key appearing later defaults to visible**: mount with one series, rerender with a
93
- second series key added — assert the new key is visible by default while any prior manual toggle
94
- on the first key is preserved.
95
-
96
- **Non-vacuity:** test 1 must fail against the current `visibleKeys` lazy-initializer code (verify
97
- this before considering the fix complete) — it is reproducing the exact live bug from jg-ferien.
98
-
99
- ## CONTEXT PACKAGE — verified current state (Orchestrator, Implementation map)
100
-
101
- Work from this package; do not explore broadly from scratch — open only the named files to verify.
102
-
103
- **File to change: `src/components/charts/TimeSeriesChart.jsx`.**
104
-
105
- Current state (verified, full relevant slice):
106
- ```js
107
- const seriesConfig = data?.series || [];
108
-
109
- const [range, setRange] = useState(defaultRange);
110
- // Series identity (keys) is assumed stable for a given chart instance — the
111
- // toggle set is initialized once from the first data the chart receives.
112
- const [visibleKeys, setVisibleKeys] = useState(
113
- () => new Set(seriesConfig.map((series) => series.key)),
114
- );
115
- ```
116
- (`TimeSeriesChart.jsx:52-59`) — the comment above `visibleKeys` states the now-disproven assumption
117
- ("series identity is stable for a given chart instance") that this WO corrects; update or remove it
118
- to match the new behaviour.
119
-
120
- `toggleSeries` (`:68-75`), `visibleSeries`/`visiblePalette`/`isDataEmpty` derivation (`:77-84`), and
121
- the checkbox rendering (`:103-123`) are all downstream of `visibleKeys` and should not need changes
122
- — only how `visibleKeys` is initialized/kept in sync with `seriesConfig` changes.
123
-
124
- **Suggested shape (not prescriptive — implement whatever satisfies SCOPE correctly):** replace the
125
- lazy `useState` with a `useEffect` keyed on the set of series keys in `seriesConfig`, that adds any
126
- newly-seen key to `visibleKeys` (defaulting it visible) without touching keys already present in the
127
- set — e.g. track previously-seen keys in a ref to detect "new" vs "toggled off", since a key
128
- present in both old and new `seriesConfig` but absent from `visibleKeys` must stay absent (it was a
129
- deliberate user toggle-off, not a fresh key). Keep `visibleKeys`'s existing `Set`-of-keys shape,
130
- `toggleSeries`'s update pattern, and the checkbox/empty-state code untouched.
131
-
132
- **Test file to extend: `tests/TimeSeriesChart.test.jsx`** — matches `tests/ChartFrame.test.jsx`/
133
- `tests/BarChart.test.jsx` conventions: `// @vitest-environment jsdom` header, vitest +
134
- `@testing-library/react`, `afterEach(cleanup)`, a minimal per-file `i18next.createInstance()` +
135
- `I18nextProvider`, wrapped in `ThemeProvider theme={createTheme()}`. Use `rerender()` from
136
- `@testing-library/react`'s `render()` return value to simulate the host updating `data` after mount
137
- (this is the exact scenario CHART-2's own tests never covered — they only mounted once with
138
- synchronous populated data).
139
-
140
- ## TARGET REPO
141
- `C:\Users\biglmi\Documents\webapps\ui-core-micha`. Branch `main`. Publish per the repo's release
142
- flow (patch bump — bug fix, no interface change); consumer pin bumps are separate, out of scope.
143
-
144
- ## TEST SCOPE FOR THE GATE (orchestrator)
145
- `tests/TimeSeriesChart.test.jsx` only. Not the full suite.
146
-
147
- ## MINI-HANDOVER (pastable)
148
-
149
- > Repo: `C:\Users\biglmi\Documents\webapps\ui-core-micha` (branch `main`).
150
- > Work order: `work-orders/CHART-3.md` — read it fully, then follow the `orchestrate-codex` skill.
151
- >
152
- > Bug fix on CHART-2's `TimeSeriesChart`: `visibleKeys` is initialized once via a lazy `useState`
153
- > from whatever `data` is at first mount, so any host that fetches asynchronously after mount (the
154
- > normal case) ends up with a permanently-empty `visibleKeys` and the chart body never renders —
155
- > `ChartFrame`'s empty state shows forever even once real data arrives. Fix the sync so newly-seen
156
- > series keys default to visible while a user's manual toggle-off on an already-seen key survives a
157
- > data update. Regression test 1 is the non-vacuity check — verify it fails against current code
158
- > first.
159
-
160
- ## PROGRESS CONTRACT
161
- Emit `PLAN: <steps>` up front, then a single-line `PROGRESS: [<n>/<total>] <action>` before every
162
- relevant action and `PROGRESS: [<n>/<total>] done` on completion, spaced so no gap exceeds ~2 min,
163
- stdout unbuffered, and exactly one final `RESULT: DONE|BLOCKED <reason>`.
@@ -1,203 +0,0 @@
1
- # WORK ORDER CHART-4 (ui-core-micha) — TimeSeriesChart renders no chart body (zero height)
2
-
3
- **EXECUTION DIRECTIVE.** If you are the implementer reading this as your own spec, this section is not
4
- addressed to you — it tells the Orchestrator how to invoke you; you ARE that invocation, do not shell
5
- out to `codex exec`. Orchestrator: implement through `codex exec` in the background, invoked **directly
6
- via Bash** (never the `debugger`/`*_coder` Agent wrappers) with **both** flags `--skip-git-repo-check`
7
- and `--dangerously-bypass-approvals-and-sandbox`, prompt passed as a positional argument from this
8
- file. Fall back to direct Claude implementation only on Codex quota / rate-limit / non-zero exit.
9
- **Known at WO authoring time: the Codex workspace is out of credits for this session — the fallback
10
- applies immediately, do not spend time retrying `codex exec` first.**
11
-
12
- Bug-fix WO on CHART-2/CHART-3. Found live in jg-ferien's "Aktivität" screen (ACT-3/ACT-5 consumer)
13
- immediately after CHART-3 shipped and jg's pin was bumped: the toolbar, series checkboxes, and MUI
14
- X-Charts **legend** now all render correctly (proving CHART-3's fix works and real data is present),
15
- but the chart canvas itself — bars and axes — is entirely absent. The card ends right after the
16
- legend row with visible empty space beneath it.
17
-
18
- ## TIER
19
- Tier 2 — shared-core UI consumed by every app that adopts `TimeSeriesChart`.
20
- Independent `reviewer` + `ui_reviewer` mandatory.
21
-
22
- ## WHY
23
-
24
- `TimeSeriesChart` composes `ChartFrame` + `BarChart` but never supplies either with a height:
25
-
26
- ```jsx
27
- <ChartFrame title={title} subtitle={subtitle} toolbar={toolbar} loading={loading} error={error} isEmpty={isDataEmpty}>
28
- {!isDataEmpty && (
29
- <BarChart xAxisLabel={xAxisLabel} yAxisLabel={yAxisLabel} xAxis={[{ data: data?.xLabels || [] }]}
30
- series={visibleSeries.map(...)} palette={visiblePalette} />
31
- )}
32
- </ChartFrame>
33
- ```
34
-
35
- Neither `minHeight`/`aspect` (ChartFrame's own sizing props) nor `minHeight`/`aspect`/a raw MUI
36
- `height` (BarChart's) are passed. `ChartFrame`'s inner content `Box` has `display: flex, alignItems:
37
- center, justifyContent: center` with no defined height — with `alignItems: center` (not `stretch`),
38
- a flex child gets its own intrinsic height, not the parent's. `BarChart`'s own wrapping `Box` (`sx:
39
- {width:'100%', minHeight, aspectRatio}`) therefore also has no height when neither prop is supplied.
40
- MUI X-Charts' internal responsive container measures its actual parent height via `ResizeObserver` to
41
- size the SVG — with a zero-height parent, the chart canvas (bars, axes) renders at zero size. The
42
- **legend** is a separate MUI X-Charts sub-component sized by its own content (text + swatches), so it
43
- renders regardless of container height — which is exactly why the legend was the only visible sign
44
- of a mounted chart in the live screenshot.
45
-
46
- This gap was invisible until now because CHART-3's own bug (`visibleKeys` never populating) kept
47
- `isDataEmpty` permanently `true`, so `BarChart` never actually mounted in any live deployment before
48
- today. It also went uncaught by CHART-2's and CHART-3's own tests: both `tests/BarChart.test.jsx` and
49
- `tests/TimeSeriesChart.test.jsx` mock `@mui/x-charts/BarChart` itself (`vi.mock('@mui/x-charts/BarChart', ...)`
50
- per CHART-2's own required-tests convention), so no test in this repo has ever mounted the *real* MUI
51
- chart and observed its rendered size. The working reference is `dev/entries.jsx`'s `BarChartEntry`
52
- (DX-1 harness), which explicitly sets `minHeight={360}` on `ChartFrame` and `height={320}` directly
53
- on `BarChart` — i.e. every other real consumer of these primitives in this repo already knows it must
54
- supply a height; `TimeSeriesChart`, as a preset built on top, was supposed to supply this itself
55
- (hosts don't see `ChartFrame`/`BarChart` directly through the preset) and doesn't.
56
-
57
- ## SCOPE
58
-
59
- **Give `TimeSeriesChart` a sensible built-in height for the chart it composes**, matching the
60
- DX-1-proven working pattern:
61
- 1. Pass `minHeight` to `ChartFrame` (sizes the loading/error/empty states too, not just the chart).
62
- 2. Pass a height down to `BarChart` so the actual MUI chart canvas gets a real, non-zero pixel size —
63
- either `BarChart`'s own `minHeight`/`aspect` wrapper props, or a raw `height` forwarded via
64
- `BarChart`'s `...chartProps` pass-through to `MuiBarChart` (the mechanism `dev/entries.jsx` uses
65
- directly against `BarChart`, confirmed working there). Pick whichever actually renders a non-zero
66
- chart canvas when verified (see REQUIRED TESTS) — do not assume `aspectRatio` CSS alone reliably
67
- drives MUI X-Charts' `ResizeObserver` sizing without checking; the raw `height` prop is the
68
- confirmed-working mechanism in this repo's own reference usage and is the safer default if there
69
- is any doubt.
70
- 3. Pick one fixed value (e.g. `320`/`360`, matching `dev/entries.jsx`'s own numbers) as a preset
71
- default — this is a preset, not a raw primitive; the host does not choose chart height, matching
72
- `TimeSeriesChart`'s existing "no new props" presentational contract. Do not add a new prop for
73
- this unless a reviewer specifically asks for one.
74
-
75
- **This WO's real fix is the test gap, not just the pixels.** The reason this shipped broken twice is
76
- that every test in this file mocks the real MUI chart component. Required test 1 below must exercise
77
- the **unmocked** `@mui/x-charts/BarChart` and assert a real rendered size — a mocked-chart test would
78
- be exactly the kind of vacuous coverage that let this ship.
79
-
80
- ## NON-GOALS / DO NOT TOUCH
81
- - Do not change the range picker, series toggles, `chartsTranslations`, or the CHART-3 `visibleKeys`
82
- sync logic — this WO is scoped to sizing only.
83
- - Do not change `ChartFrame`'s or `BarChart`'s own prop contracts (`minHeight`/`aspect` already exist
84
- on both) — only how `TimeSeriesChart` calls them.
85
- - Do not add a new prop to `TimeSeriesChart`'s public API for this — see SCOPE point 3.
86
-
87
- ## RISKS
88
- - Every host that has adopted `TimeSeriesChart` is affected (currently jg-ferien via ACT-3/ACT-5) —
89
- this changes rendering behaviour for all of them, in the correct direction (chart canvas now
90
- actually visible).
91
- - Getting this wrong a second time (e.g. an `aspectRatio`-only fix that still doesn't reliably drive
92
- MUI X-Charts' internal measurement) ships a third silent failure in the same component. The
93
- required unmocked-render test exists specifically to catch that before it reaches a live app again.
94
-
95
- ## REQUIRED TESTS TO WRITE
96
- Extend `tests/TimeSeriesChart.test.jsx`. Do NOT run the full suite.
97
-
98
- 1. **The real bug's mechanism, not a jsdom layout measurement.** An initial attempt at this WO wrote
99
- an *unmocked* test (real `@mui/x-charts/BarChart`, no `vi.mock`) asserting the rendered chart
100
- surface's `viewBox` height was non-zero — verified, in practice, to be **vacuous**: it stayed green
101
- even with the pre-fix code (no `height` prop anywhere), because jsdom's `ResizeObserver`/layout
102
- handling does not reproduce the real-browser zero-height-parent behaviour this bug depends on. Do
103
- not repeat that approach. Instead, keep the existing `chartSpy` mock convention (`vi.hoisted` +
104
- `vi.mock('@mui/x-charts/BarChart', ...)`, already at `tests/TimeSeriesChart.test.jsx:9-10`) and
105
- assert directly on the props the real `MuiBarChart` receives: `chartSpy.mock.calls.at(-1)[0].height`
106
- must be a real, non-zero number. This is what actually distinguishes fixed from broken — confirmed
107
- by reverting the fix and re-running: the prop assertion fails pre-fix (`props.height` is
108
- `undefined`), the unmocked viewBox assertion does not.
109
- 2. **Non-vacuity**: this test must fail against the current (pre-fix) code — verify before considering
110
- the fix complete (see note above — already verified once for the prop-assertion approach).
111
- 3. Existing mocked-`BarChart` tests (range/toggle/empty-state behaviour) stay as they are.
112
-
113
- ## CONTEXT PACKAGE — verified current state (Orchestrator, Implementation map)
114
-
115
- Work from this package; do not explore broadly from scratch — open only the named files to verify.
116
-
117
- **File to change: `src/components/charts/TimeSeriesChart.jsx`.**
118
-
119
- Current return statement (verified, full slice, `:150-169`):
120
- ```jsx
121
- return (
122
- <ChartFrame
123
- title={title}
124
- subtitle={subtitle}
125
- toolbar={toolbar}
126
- loading={loading}
127
- error={error}
128
- isEmpty={isDataEmpty}
129
- >
130
- {!isDataEmpty && (
131
- <BarChart
132
- xAxisLabel={xAxisLabel}
133
- yAxisLabel={yAxisLabel}
134
- xAxis={[{ data: data?.xLabels || [] }]}
135
- series={visibleSeries.map((series) => ({ data: series.data, label: series.label }))}
136
- palette={visiblePalette}
137
- />
138
- )}
139
- </ChartFrame>
140
- );
141
- ```
142
-
143
- **Reference (working) pattern — `dev/entries.jsx:35-39`:**
144
- ```jsx
145
- function BarChartEntry() {
146
- return (
147
- <ChartFrame title="Monthly cases" subtitle="Standalone BarChart entry" minHeight={360}>
148
- <BarChart {...barChartFixture} xAxisLabel="Month" yAxisLabel="Cases" height={320} />
149
- </ChartFrame>
150
- );
151
- }
152
- ```
153
- Note `height={320}` on `BarChart` here is NOT `BarChart`'s own `minHeight`/`aspect` wrapper prop — it
154
- lands in `BarChart`'s `...chartProps` rest spread (`BarChart.jsx:24,34`) and is forwarded straight to
155
- `MuiBarChart`'s own native `height` prop, which MUI X-Charts uses as a fixed pixel height instead of
156
- measuring the parent via `ResizeObserver`. This is the confirmed-working mechanism in this repo.
157
-
158
- **`ChartFrame.jsx:86-99`** — the content `Box`: `sx={{ width:'100%', minHeight, aspectRatio: aspect,
159
- display:'flex', alignItems:'center', justifyContent:'center' }}`. `alignItems:'center'` means a flex
160
- child (here, `BarChart`'s own `Box`) is NOT stretched to fill this Box's height — it keeps its own
161
- intrinsic height. Passing `minHeight` to `ChartFrame` sizes this outer Box correctly for the
162
- loading/error/empty `Alert`/`CircularProgress` states (which have no fixed height of their own and
163
- DO benefit from the flex-centered parent height), but does **not**, by itself, give `BarChart`'s
164
- child `Box` any height — that must come from `BarChart` itself (its own `minHeight`/`aspect`, or the
165
- raw `height` chartProp per the reference pattern above).
166
-
167
- **`BarChart.jsx:15-44`** — full file, already shown above in this WO's WHY section — `minHeight`/
168
- `aspect` are explicit named props (`:22-23`) applied to `BarChart`'s own wrapping `Box` (`:32`);
169
- anything else lands in `...chartProps` (`:24`) and is spread onto `MuiBarChart` directly (`:34`),
170
- which is how a raw `height` reaches the underlying MUI component.
171
-
172
- **Test file: `tests/TimeSeriesChart.test.jsx`** — already mocks `@mui/x-charts/BarChart` via
173
- `vi.hoisted` + `vi.mock` (`:9-10`) and every existing test reads `chartSpy.mock.calls.at(-1)[0]` to
174
- assert exact props (e.g. the `series`/`colors` assertions at `:87-89,94-99`). Add the new test in the
175
- same style, asserting `props.height` — see REQUIRED TESTS above for why an unmocked render test does
176
- NOT work here.
177
-
178
- ## TARGET REPO
179
- `C:\Users\biglmi\Documents\webapps\ui-core-micha`. Branch `main`. Publish per the repo's release
180
- flow (patch bump — bug fix, no interface change).
181
-
182
- ## TEST SCOPE FOR THE GATE (orchestrator)
183
- `tests/TimeSeriesChart.test.jsx`. Not the full suite.
184
-
185
- ## MINI-HANDOVER (pastable)
186
-
187
- > Repo: `C:\Users\biglmi\Documents\webapps\ui-core-micha` (branch `main`).
188
- > Work order: `work-orders/CHART-4.md` — read it fully, then follow the `orchestrate-codex` skill.
189
- > **Codex workspace is out of credits this session — go straight to the direct-Claude fallback, do
190
- > not attempt `codex exec` first.**
191
- >
192
- > Second bug in `TimeSeriesChart` (after CHART-3): the chart legend renders but the actual bars/axes
193
- > never appear because neither `ChartFrame` nor `BarChart` ever receive a height — MUI X-Charts'
194
- > responsive container measures a zero-height parent and draws nothing. `dev/entries.jsx`'s
195
- > `BarChartEntry` is the proof this is fixable: `minHeight={360}` on `ChartFrame` + `height={320}`
196
- > forwarded straight to `BarChart` (MUI's own native height prop, not the wrapper's `minHeight`/
197
- > `aspect`). The required regression test must NOT mock `@mui/x-charts/BarChart` — every existing test
198
- > in this component mocks it, which is exactly how two sizing/rendering bugs shipped undetected.
199
-
200
- ## PROGRESS CONTRACT
201
- Emit `PLAN: <steps>` up front, then a single-line `PROGRESS: [<n>/<total>] <action>` before every
202
- relevant action and `PROGRESS: [<n>/<total>] done` on completion, spaced so no gap exceeds ~2 min,
203
- stdout unbuffered, and exactly one final `RESULT: DONE|BLOCKED <reason>`.