prime-ui-kit 0.14.0 → 0.15.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 (490) hide show
  1. package/README.md +13 -14
  2. package/dist/chunks/{chunk-P6ZO37JM.js → chunk-2IKH55MI.js} +1 -1
  3. package/dist/chunks/{chunk-SYEX7OVT.js → chunk-THFZIHKU.js} +276 -263
  4. package/dist/chunks/{chunk-WXKRUVSE.js → chunk-XJAVS6FH.js} +49 -48
  5. package/dist/color-picker.js +2 -2
  6. package/dist/components/dnd/Dnd.d.ts +1 -1
  7. package/dist/components/login-form/LoginForm.d.ts +5 -1
  8. package/dist/index.css +1 -1
  9. package/dist/index.js +2 -2
  10. package/dist/internal/swatch.d.ts +6 -2
  11. package/dist/layout/app-header/AppHeader.d.ts +9 -28
  12. package/dist/layout/index.d.ts +1 -1
  13. package/dist/skill/SKILL.md +9 -7
  14. package/dist/skill/api-contract.md +2 -2
  15. package/dist/skill/cheatsheet.md +9 -2
  16. package/dist/skill/checklist.md +9 -5
  17. package/dist/skill/choosing.md +7 -9
  18. package/dist/skill/components.md +5 -12
  19. package/dist/skill/composition.md +281 -54
  20. package/dist/skill/foundations.md +1 -1
  21. package/dist/skill/layouts.md +70 -37
  22. package/dist/skill/reference/components/avatar/examples/states.tsx +4 -4
  23. package/dist/skill/reference/components/badge/examples/applied-filters.tsx +13 -19
  24. package/dist/skill/reference/components/badge/examples/examples.module.css +1 -8
  25. package/dist/skill/reference/components/badge/examples/server-tags.tsx +2 -6
  26. package/dist/skill/reference/components/banner/examples/examples.module.css +1 -5
  27. package/dist/skill/reference/components/banner/examples/page-strip.tsx +16 -22
  28. package/dist/skill/reference/components/breadcrumb/COMPONENT.md +4 -3
  29. package/dist/skill/reference/components/breadcrumb/examples/ellipsis.tsx +2 -2
  30. package/dist/skill/reference/components/button-group/COMPONENT.md +2 -2
  31. package/dist/skill/reference/components/button-group/examples/in-form.tsx +22 -2
  32. package/dist/skill/reference/components/button-group/examples/states.tsx +10 -1
  33. package/dist/skill/reference/components/card/COMPONENT.md +2 -1
  34. package/dist/skill/reference/components/card/examples/screen.tsx +362 -0
  35. package/dist/skill/reference/components/card/examples/variants.tsx +15 -1
  36. package/dist/skill/reference/components/chart-frame/COMPONENT.md +2 -1
  37. package/dist/skill/reference/components/chart-frame/examples/fill-height.tsx +119 -0
  38. package/dist/skill/reference/components/chart-frame/examples/states.tsx +3 -1
  39. package/dist/skill/reference/components/chat/COMPONENT.md +3 -1
  40. package/dist/skill/reference/components/chat/examples/group-chat.tsx +15 -2
  41. package/dist/skill/reference/components/chat/examples/reactions.tsx +121 -0
  42. package/dist/skill/reference/components/chat/examples/screen.module.css +9 -0
  43. package/dist/skill/{patterns/chat.tsx → reference/components/chat/examples/screen.tsx} +170 -351
  44. package/dist/skill/reference/components/chat-list/COMPONENT.md +1 -0
  45. package/dist/skill/reference/components/chat-list/examples/links.tsx +72 -0
  46. package/dist/skill/reference/components/checkbox/COMPONENT.md +1 -1
  47. package/dist/skill/reference/components/checkbox/examples/examples.module.css +5 -1
  48. package/dist/skill/reference/components/checkbox/examples/indicator.tsx +13 -7
  49. package/dist/skill/reference/components/checkbox/examples/states.tsx +6 -1
  50. package/dist/skill/reference/components/color-field/COMPONENT.md +1 -1
  51. package/dist/skill/reference/components/color-picker/COMPONENT.md +4 -3
  52. package/dist/skill/reference/components/color-picker/examples/controlled-open.tsx +37 -0
  53. package/dist/skill/reference/components/color-picker/examples/presets.tsx +2 -2
  54. package/dist/skill/reference/components/color-picker/examples/states.tsx +23 -14
  55. package/dist/skill/reference/components/composer/COMPONENT.md +2 -1
  56. package/dist/skill/reference/components/composer/examples/attach-menu.tsx +78 -0
  57. package/dist/skill/reference/components/composer/examples/attachments.tsx +2 -2
  58. package/dist/skill/reference/components/crossfade/COMPONENT.md +1 -0
  59. package/dist/skill/reference/components/crossfade/examples/screen.tsx +197 -0
  60. package/dist/skill/reference/components/data-table/COMPONENT.md +3 -2
  61. package/dist/skill/reference/components/data-table/examples/detail-panel.tsx +36 -37
  62. package/dist/skill/reference/components/data-table/examples/examples.module.css +0 -12
  63. package/dist/skill/reference/components/data-table/examples/numeric.tsx +36 -4
  64. package/dist/skill/reference/components/data-table/examples/overview.tsx +1 -0
  65. package/dist/skill/reference/components/data-table/examples/row-open.tsx +7 -36
  66. package/dist/skill/{patterns/list-page.module.css → reference/components/data-table/examples/screen.module.css} +1 -7
  67. package/dist/skill/{patterns/list-page.tsx → reference/components/data-table/examples/screen.tsx} +84 -64
  68. package/dist/skill/reference/components/datepicker/COMPONENT.md +5 -4
  69. package/dist/skill/reference/components/datepicker/examples/examples.module.css +2 -6
  70. package/dist/skill/reference/components/datepicker/examples/in-form.tsx +47 -55
  71. package/dist/skill/reference/components/datepicker/examples/locale.tsx +35 -0
  72. package/dist/skill/reference/components/datepicker/examples/range-presets.tsx +11 -2
  73. package/dist/skill/reference/components/datepicker/examples/states.tsx +9 -1
  74. package/dist/skill/reference/components/description-list/COMPONENT.md +1 -0
  75. package/dist/skill/{patterns/detail-page.module.css → reference/components/description-list/examples/screen.module.css} +2 -9
  76. package/dist/skill/reference/components/description-list/examples/screen.tsx +214 -0
  77. package/dist/skill/reference/components/description-list/examples/structure.tsx +1 -1
  78. package/dist/skill/reference/components/digit-input/COMPONENT.md +3 -1
  79. package/dist/skill/reference/components/digit-input/examples/states.tsx +3 -1
  80. package/dist/skill/reference/components/digit-input/examples/without-focus-ring.tsx +13 -0
  81. package/dist/skill/reference/components/divider/examples/with-icon.tsx +2 -2
  82. package/dist/skill/reference/components/dnd/COMPONENT.md +4 -2
  83. package/dist/skill/reference/components/dnd/examples/drop-zones.tsx +82 -49
  84. package/dist/skill/reference/components/dnd/examples/examples.module.css +50 -0
  85. package/dist/skill/reference/components/dnd/examples/states.tsx +54 -0
  86. package/dist/skill/reference/components/dnd/examples/table.tsx +113 -0
  87. package/dist/skill/reference/components/drawer/COMPONENT.md +1 -0
  88. package/dist/skill/reference/components/drawer/examples/dismiss.tsx +5 -3
  89. package/dist/skill/reference/components/drawer/examples/examples.module.css +0 -13
  90. package/dist/skill/reference/components/drawer/examples/header-actions.tsx +5 -9
  91. package/dist/skill/reference/components/drawer/examples/overview.tsx +6 -18
  92. package/dist/skill/reference/components/drawer/examples/screen.tsx +298 -0
  93. package/dist/skill/reference/components/dropdown/examples/examples.module.css +1 -5
  94. package/dist/skill/reference/components/empty-page/COMPONENT.md +1 -1
  95. package/dist/skill/reference/components/empty-page/examples/region-error.tsx +8 -2
  96. package/dist/skill/reference/components/example-frame/COMPONENT.md +3 -2
  97. package/dist/skill/reference/components/example-frame/examples/initial-view.tsx +44 -0
  98. package/dist/skill/reference/components/field-list/COMPONENT.md +2 -1
  99. package/dist/skill/reference/components/field-list/examples/errors.tsx +5 -2
  100. package/dist/skill/{patterns/record-editor.tsx → reference/components/field-list/examples/screen.tsx} +3 -43
  101. package/dist/skill/reference/components/file-upload/COMPONENT.md +7 -6
  102. package/dist/skill/reference/components/file-upload/examples/custom-body.tsx +2 -1
  103. package/dist/skill/reference/components/file-upload/examples/examples.module.css +1 -7
  104. package/dist/skill/reference/components/file-upload/examples/file-cards.tsx +10 -1
  105. package/dist/skill/reference/components/file-upload/examples/gallery.tsx +2 -2
  106. package/dist/skill/reference/components/file-upload/examples/in-form.tsx +4 -9
  107. package/dist/skill/reference/components/file-upload/examples/overview.tsx +5 -1
  108. package/dist/skill/reference/components/file-upload/examples/row-parts.tsx +24 -1
  109. package/dist/skill/{patterns/media-library.module.css → reference/components/file-upload/examples/screen.module.css} +2 -1
  110. package/dist/skill/reference/components/file-upload/examples/screen.tsx +646 -0
  111. package/dist/skill/reference/components/file-upload/examples/states.tsx +6 -2
  112. package/dist/skill/reference/components/file-upload/examples/validation.tsx +11 -2
  113. package/dist/skill/reference/components/file-upload/examples/variants.tsx +3 -20
  114. package/dist/skill/reference/components/gantt/COMPONENT.md +2 -1
  115. package/dist/skill/reference/components/gantt/examples/screen.module.css +14 -0
  116. package/dist/skill/{patterns/schedule.tsx → reference/components/gantt/examples/screen.tsx} +174 -170
  117. package/dist/skill/reference/components/gantt/examples/states.tsx +6 -2
  118. package/dist/skill/reference/components/input/COMPONENT.md +2 -2
  119. package/dist/skill/reference/components/input/examples/examples.module.css +0 -6
  120. package/dist/skill/reference/components/input/examples/in-form.tsx +1 -9
  121. package/dist/skill/reference/components/input/examples/password-strength.tsx +34 -8
  122. package/dist/skill/reference/components/input/examples/states.tsx +6 -1
  123. package/dist/skill/reference/components/kanban/COMPONENT.md +3 -2
  124. package/dist/skill/reference/components/kanban/examples/columns.tsx +8 -5
  125. package/dist/skill/reference/components/kanban/examples/screen.module.css +17 -0
  126. package/dist/skill/{patterns/kanban-board.tsx → reference/components/kanban/examples/screen.tsx} +130 -172
  127. package/dist/skill/reference/components/kanban/examples/states.tsx +17 -6
  128. package/dist/skill/reference/components/login-form/COMPONENT.md +6 -5
  129. package/dist/skill/reference/components/login-form/examples/examples.module.css +2 -21
  130. package/dist/skill/reference/components/login-form/examples/forgot-password.tsx +28 -32
  131. package/dist/skill/reference/components/login-form/examples/narrow.tsx +1 -1
  132. package/dist/skill/reference/components/login-form/examples/overview.tsx +45 -47
  133. package/dist/skill/reference/components/login-form/examples/register.tsx +64 -68
  134. package/dist/skill/reference/components/login-form/examples/reset-password.tsx +50 -47
  135. package/dist/skill/reference/components/login-form/examples/sizes.tsx +4 -6
  136. package/dist/skill/reference/components/login-form/examples/states.tsx +53 -57
  137. package/dist/skill/reference/components/login-form/examples/structure.tsx +18 -22
  138. package/dist/skill/reference/components/login-form/examples/verification-code.tsx +41 -45
  139. package/dist/skill/reference/components/media-viewer/COMPONENT.md +1 -1
  140. package/dist/skill/reference/components/media-viewer/examples/dismiss.tsx +5 -1
  141. package/dist/skill/reference/components/message/COMPONENT.md +4 -3
  142. package/dist/skill/reference/components/message/examples/file.tsx +5 -1
  143. package/dist/skill/reference/components/message/examples/media-open.tsx +48 -0
  144. package/dist/skill/reference/components/message/examples/status.tsx +21 -3
  145. package/dist/skill/reference/components/message/examples/text-formatting.tsx +7 -1
  146. package/dist/skill/reference/components/modal/COMPONENT.md +2 -1
  147. package/dist/skill/reference/components/modal/examples/custom-container.tsx +20 -20
  148. package/dist/skill/reference/components/modal/examples/dismiss.tsx +5 -3
  149. package/dist/skill/reference/components/modal/examples/enter-key.tsx +116 -0
  150. package/dist/skill/reference/components/modal/examples/examples.module.css +7 -13
  151. package/dist/skill/reference/components/modal/examples/structure.tsx +5 -2
  152. package/dist/skill/reference/components/native-select/COMPONENT.md +1 -1
  153. package/dist/skill/reference/components/native-select/examples/states.tsx +5 -1
  154. package/dist/skill/reference/components/notification/COMPONENT.md +1 -1
  155. package/dist/skill/reference/components/notification/examples/dismiss.tsx +2 -2
  156. package/dist/skill/reference/components/page-content/COMPONENT.md +1 -0
  157. package/dist/skill/reference/components/page-content/examples/list-detail.tsx +0 -3
  158. package/dist/skill/{patterns/settings-page.tsx → reference/components/page-content/examples/screen.tsx} +3 -3
  159. package/dist/skill/reference/components/page-content/examples/variants.tsx +4 -11
  160. package/dist/skill/reference/components/page-toolbar/COMPONENT.md +17 -15
  161. package/dist/skill/reference/components/page-toolbar/examples/narrow.tsx +6 -3
  162. package/dist/skill/reference/components/pagination/examples/controlled.tsx +29 -29
  163. package/dist/skill/reference/components/pagination/examples/examples.module.css +0 -2
  164. package/dist/skill/reference/components/progress-bar/COMPONENT.md +1 -1
  165. package/dist/skill/reference/components/progress-bar/examples/segments.tsx +2 -2
  166. package/dist/skill/reference/components/progress-circle/COMPONENT.md +1 -1
  167. package/dist/skill/reference/components/progress-circle/examples/segments.tsx +8 -3
  168. package/dist/skill/reference/components/radio/COMPONENT.md +1 -0
  169. package/dist/skill/reference/components/radio/examples/examples.module.css +14 -0
  170. package/dist/skill/reference/components/radio/examples/in-form.tsx +45 -0
  171. package/dist/skill/reference/components/scroll-container/COMPONENT.md +1 -0
  172. package/dist/skill/reference/components/scroll-container/examples/auto-scroll.tsx +3 -3
  173. package/dist/skill/reference/components/scroll-container/examples/chat.tsx +3 -3
  174. package/dist/skill/reference/components/scroll-container/examples/edges.tsx +6 -3
  175. package/dist/skill/reference/components/scroll-container/examples/examples.module.css +4 -17
  176. package/dist/skill/reference/components/scroll-container/examples/load-more.tsx +51 -0
  177. package/dist/skill/reference/components/scroll-container/examples/lock.tsx +3 -5
  178. package/dist/skill/reference/components/scroll-container/examples/overscroll.tsx +3 -3
  179. package/dist/skill/reference/components/scroll-container/examples/overview.tsx +3 -3
  180. package/dist/skill/reference/components/scroll-container/examples/scrollbar.tsx +13 -7
  181. package/dist/skill/reference/components/scroll-container/examples/variants.tsx +13 -7
  182. package/dist/skill/reference/components/segmented-control/COMPONENT.md +2 -2
  183. package/dist/skill/reference/components/segmented-control/examples/in-form.tsx +5 -1
  184. package/dist/skill/reference/components/segmented-control/examples/states.tsx +10 -1
  185. package/dist/skill/reference/components/select/COMPONENT.md +1 -1
  186. package/dist/skill/reference/components/select/examples/examples.module.css +2 -7
  187. package/dist/skill/reference/components/select/examples/states.tsx +13 -1
  188. package/dist/skill/reference/components/sheet/COMPONENT.md +2 -0
  189. package/dist/skill/reference/components/sheet/examples/guard.tsx +72 -0
  190. package/dist/skill/reference/components/sheet/examples/header-actions.tsx +56 -0
  191. package/dist/skill/reference/components/slider/COMPONENT.md +2 -1
  192. package/dist/skill/reference/components/slider/examples/examples.module.css +8 -0
  193. package/dist/skill/reference/components/slider/examples/in-form.tsx +47 -0
  194. package/dist/skill/reference/components/slider/examples/states.tsx +4 -1
  195. package/dist/skill/reference/components/smart-filter/COMPONENT.md +1 -1
  196. package/dist/skill/reference/components/smart-filter/examples/controlled.tsx +3 -3
  197. package/dist/skill/reference/components/smart-filter/examples/ranges.tsx +44 -10
  198. package/dist/skill/reference/components/sparkline/COMPONENT.md +1 -1
  199. package/dist/skill/reference/components/sparkline/examples/reference.tsx +2 -1
  200. package/dist/skill/reference/components/tabs/COMPONENT.md +1 -1
  201. package/dist/skill/reference/components/tabs/examples/sizes.tsx +1 -1
  202. package/dist/skill/reference/components/tag-select/COMPONENT.md +1 -1
  203. package/dist/skill/reference/components/tag-select/examples/states.tsx +8 -1
  204. package/dist/skill/reference/components/textarea/COMPONENT.md +1 -1
  205. package/dist/skill/reference/components/textarea/examples/examples.module.css +0 -6
  206. package/dist/skill/reference/components/textarea/examples/in-form.tsx +1 -9
  207. package/dist/skill/reference/components/textarea/examples/states.tsx +2 -1
  208. package/dist/skill/reference/components/thumbnail/COMPONENT.md +2 -1
  209. package/dist/skill/reference/components/thumbnail/examples/examples.module.css +1 -1
  210. package/dist/skill/reference/components/thumbnail/examples/fit.tsx +24 -0
  211. package/dist/skill/reference/components/thumbnail/examples/video.tsx +20 -1
  212. package/dist/skill/reference/components/timeline/COMPONENT.md +1 -1
  213. package/dist/skill/reference/components/timeline/examples/links.tsx +55 -14
  214. package/dist/skill/reference/components/tooltip/COMPONENT.md +1 -1
  215. package/dist/skill/reference/components/tooltip/examples/toolbar.tsx +2 -2
  216. package/dist/skill/reference/components/typography/examples/weights.tsx +3 -0
  217. package/dist/skill/reference/components/video-note/COMPONENT.md +2 -2
  218. package/dist/skill/reference/components/video-note/examples/controlled.tsx +19 -2
  219. package/dist/skill/reference/components/video-note/examples/in-message.tsx +5 -2
  220. package/dist/skill/reference/components/visually-hidden/COMPONENT.md +2 -1
  221. package/dist/skill/reference/components/visually-hidden/examples/assertive.tsx +39 -0
  222. package/dist/skill/reference/components/voice-player/COMPONENT.md +2 -2
  223. package/dist/skill/reference/components/voice-player/examples/call-recording.tsx +19 -9
  224. package/dist/skill/reference/components/voice-player/examples/controlled.tsx +20 -3
  225. package/dist/skill/reference/layout/app-header/COMPONENT.md +46 -63
  226. package/dist/skill/reference/layout/app-header/examples/breadcrumbs.tsx +10 -9
  227. package/dist/skill/reference/layout/app-header/examples/examples.module.css +1 -1
  228. package/dist/skill/reference/layout/app-header/examples/in-app-shell.tsx +0 -1
  229. package/dist/skill/reference/layout/app-header/examples/narrow.tsx +4 -5
  230. package/dist/skill/reference/layout/app-header/examples/overview.tsx +8 -10
  231. package/dist/skill/reference/layout/app-header/examples/page-title.tsx +3 -10
  232. package/dist/skill/reference/layout/app-shell/COMPONENT.md +4 -3
  233. package/dist/skill/reference/layout/app-shell/examples/overview.tsx +15 -30
  234. package/dist/skill/reference/layout/app-shell/examples/screen.tsx +262 -0
  235. package/dist/skill/reference/layout/app-shell/examples/template.tsx +29 -9
  236. package/dist/skill/reference/layout/sidebar/COMPONENT.md +7 -4
  237. package/dist/skill/reference/layout/sidebar/examples/account.tsx +2 -2
  238. package/dist/skill/reference/layout/sidebar/examples/collapsible-groups.tsx +12 -4
  239. package/dist/skill/reference/layout/sidebar/examples/custom-part.tsx +53 -0
  240. package/dist/skill/reference/layout/sidebar/examples/examples.module.css +5 -0
  241. package/dist/skill/reference/layout/sidebar/examples/narrow.tsx +53 -0
  242. package/dist/skill/reference/layout/sidebar/examples/persist.tsx +57 -0
  243. package/dist/skill/reference/layout/sidebar/examples/router.tsx +14 -2
  244. package/dist/skill/reference/layout/sidebar/examples/structure.tsx +7 -2
  245. package/dist/skill/responsive.md +2 -2
  246. package/package.json +1 -1
  247. package/src/components/avatar/examples/states.tsx +4 -4
  248. package/src/components/badge/examples/applied-filters.tsx +13 -19
  249. package/src/components/badge/examples/examples.module.css +1 -8
  250. package/src/components/badge/examples/server-tags.tsx +2 -6
  251. package/src/components/banner/examples/examples.module.css +1 -5
  252. package/src/components/banner/examples/page-strip.tsx +16 -22
  253. package/src/components/breadcrumb/COMPONENT.md +4 -3
  254. package/src/components/breadcrumb/examples/ellipsis.tsx +2 -2
  255. package/src/components/button-group/COMPONENT.md +2 -2
  256. package/src/components/button-group/examples/in-form.tsx +22 -2
  257. package/src/components/button-group/examples/states.tsx +10 -1
  258. package/src/components/card/COMPONENT.md +2 -1
  259. package/src/components/card/examples/screen.module.css +14 -0
  260. package/src/components/card/examples/screen.tsx +362 -0
  261. package/src/components/card/examples/variants.tsx +15 -1
  262. package/src/components/chart-frame/COMPONENT.md +2 -1
  263. package/src/components/chart-frame/examples/fill-height.tsx +119 -0
  264. package/src/components/chart-frame/examples/states.tsx +3 -1
  265. package/src/components/chat/COMPONENT.md +3 -1
  266. package/src/components/chat/examples/group-chat.tsx +15 -2
  267. package/src/components/chat/examples/reactions.tsx +121 -0
  268. package/src/components/chat/examples/screen.module.css +9 -0
  269. package/src/components/chat/examples/screen.tsx +718 -0
  270. package/src/components/chat-list/COMPONENT.md +1 -0
  271. package/src/components/chat-list/examples/links.tsx +72 -0
  272. package/src/components/checkbox/COMPONENT.md +1 -1
  273. package/src/components/checkbox/examples/examples.module.css +5 -1
  274. package/src/components/checkbox/examples/indicator.tsx +13 -7
  275. package/src/components/checkbox/examples/states.tsx +6 -1
  276. package/src/components/color-field/COMPONENT.md +1 -1
  277. package/src/components/color-picker/COMPONENT.md +4 -3
  278. package/src/components/color-picker/examples/controlled-open.tsx +37 -0
  279. package/src/components/color-picker/examples/presets.tsx +2 -2
  280. package/src/components/color-picker/examples/states.tsx +23 -14
  281. package/src/components/composer/COMPONENT.md +2 -1
  282. package/src/components/composer/examples/attach-menu.tsx +78 -0
  283. package/src/components/composer/examples/attachments.tsx +2 -2
  284. package/src/components/crossfade/COMPONENT.md +1 -0
  285. package/src/components/crossfade/examples/screen.module.css +40 -0
  286. package/src/components/crossfade/examples/screen.tsx +197 -0
  287. package/src/components/data-table/COMPONENT.md +3 -2
  288. package/src/components/data-table/examples/detail-panel.tsx +36 -37
  289. package/src/components/data-table/examples/examples.module.css +0 -12
  290. package/src/components/data-table/examples/numeric.tsx +36 -4
  291. package/src/components/data-table/examples/overview.tsx +1 -0
  292. package/src/components/data-table/examples/row-open.tsx +7 -36
  293. package/src/components/data-table/examples/screen.module.css +13 -0
  294. package/src/components/data-table/examples/screen.tsx +354 -0
  295. package/src/components/datepicker/COMPONENT.md +5 -4
  296. package/src/components/datepicker/examples/examples.module.css +2 -6
  297. package/src/components/datepicker/examples/in-form.tsx +47 -55
  298. package/src/components/datepicker/examples/locale.tsx +35 -0
  299. package/src/components/datepicker/examples/range-presets.tsx +11 -2
  300. package/src/components/datepicker/examples/states.tsx +9 -1
  301. package/src/components/description-list/COMPONENT.md +1 -0
  302. package/src/components/description-list/examples/screen.module.css +64 -0
  303. package/src/components/description-list/examples/screen.tsx +214 -0
  304. package/src/components/description-list/examples/structure.tsx +1 -1
  305. package/src/components/digit-input/COMPONENT.md +3 -1
  306. package/src/components/digit-input/examples/states.tsx +3 -1
  307. package/src/components/digit-input/examples/without-focus-ring.tsx +13 -0
  308. package/src/components/divider/examples/with-icon.tsx +2 -2
  309. package/src/components/dnd/COMPONENT.md +4 -2
  310. package/src/components/dnd/examples/drop-zones.tsx +82 -49
  311. package/src/components/dnd/examples/examples.module.css +50 -0
  312. package/src/components/dnd/examples/states.tsx +54 -0
  313. package/src/components/dnd/examples/table.tsx +113 -0
  314. package/src/components/drawer/COMPONENT.md +1 -0
  315. package/src/components/drawer/examples/dismiss.tsx +5 -3
  316. package/src/components/drawer/examples/examples.module.css +0 -13
  317. package/src/components/drawer/examples/header-actions.tsx +5 -9
  318. package/src/components/drawer/examples/overview.tsx +6 -18
  319. package/src/components/drawer/examples/screen.module.css +14 -0
  320. package/src/components/drawer/examples/screen.tsx +298 -0
  321. package/src/components/dropdown/examples/examples.module.css +1 -5
  322. package/src/components/empty-page/COMPONENT.md +1 -1
  323. package/src/components/empty-page/examples/region-error.tsx +8 -2
  324. package/src/components/example-frame/COMPONENT.md +3 -2
  325. package/src/components/example-frame/examples/initial-view.tsx +44 -0
  326. package/src/components/field-list/COMPONENT.md +2 -1
  327. package/src/components/field-list/examples/errors.tsx +5 -2
  328. package/src/components/field-list/examples/screen.module.css +18 -0
  329. package/src/components/field-list/examples/screen.tsx +316 -0
  330. package/src/components/file-upload/COMPONENT.md +7 -6
  331. package/src/components/file-upload/examples/custom-body.tsx +2 -1
  332. package/src/components/file-upload/examples/examples.module.css +1 -7
  333. package/src/components/file-upload/examples/file-cards.tsx +10 -1
  334. package/src/components/file-upload/examples/gallery.tsx +2 -2
  335. package/src/components/file-upload/examples/in-form.tsx +4 -9
  336. package/src/components/file-upload/examples/overview.tsx +5 -1
  337. package/src/components/file-upload/examples/row-parts.tsx +24 -1
  338. package/src/components/file-upload/examples/screen.module.css +45 -0
  339. package/src/components/file-upload/examples/screen.tsx +646 -0
  340. package/src/components/file-upload/examples/states.tsx +6 -2
  341. package/src/components/file-upload/examples/validation.tsx +11 -2
  342. package/src/components/file-upload/examples/variants.tsx +3 -20
  343. package/src/components/gantt/COMPONENT.md +2 -1
  344. package/src/components/gantt/examples/screen.module.css +14 -0
  345. package/src/components/gantt/examples/screen.tsx +726 -0
  346. package/src/components/gantt/examples/states.tsx +6 -2
  347. package/src/components/input/COMPONENT.md +2 -2
  348. package/src/components/input/examples/examples.module.css +0 -6
  349. package/src/components/input/examples/in-form.tsx +1 -9
  350. package/src/components/input/examples/password-strength.tsx +34 -8
  351. package/src/components/input/examples/states.tsx +6 -1
  352. package/src/components/kanban/COMPONENT.md +3 -2
  353. package/src/components/kanban/examples/columns.tsx +8 -5
  354. package/src/components/kanban/examples/screen.module.css +17 -0
  355. package/src/components/kanban/examples/screen.tsx +572 -0
  356. package/src/components/kanban/examples/states.tsx +17 -6
  357. package/src/components/login-form/COMPONENT.md +6 -5
  358. package/src/components/login-form/examples/examples.module.css +2 -21
  359. package/src/components/login-form/examples/forgot-password.tsx +28 -32
  360. package/src/components/login-form/examples/narrow.tsx +1 -1
  361. package/src/components/login-form/examples/overview.tsx +45 -47
  362. package/src/components/login-form/examples/register.tsx +64 -68
  363. package/src/components/login-form/examples/reset-password.tsx +50 -47
  364. package/src/components/login-form/examples/sizes.tsx +4 -6
  365. package/src/components/login-form/examples/states.tsx +53 -57
  366. package/src/components/login-form/examples/structure.tsx +18 -22
  367. package/src/components/login-form/examples/verification-code.tsx +41 -45
  368. package/src/components/media-viewer/COMPONENT.md +1 -1
  369. package/src/components/media-viewer/examples/dismiss.tsx +5 -1
  370. package/src/components/message/COMPONENT.md +4 -3
  371. package/src/components/message/examples/file.tsx +5 -1
  372. package/src/components/message/examples/media-open.tsx +48 -0
  373. package/src/components/message/examples/status.tsx +21 -3
  374. package/src/components/message/examples/text-formatting.tsx +7 -1
  375. package/src/components/modal/COMPONENT.md +2 -1
  376. package/src/components/modal/examples/custom-container.tsx +20 -20
  377. package/src/components/modal/examples/dismiss.tsx +5 -3
  378. package/src/components/modal/examples/enter-key.tsx +116 -0
  379. package/src/components/modal/examples/examples.module.css +7 -13
  380. package/src/components/modal/examples/structure.tsx +5 -2
  381. package/src/components/native-select/COMPONENT.md +1 -1
  382. package/src/components/native-select/examples/states.tsx +5 -1
  383. package/src/components/notification/COMPONENT.md +1 -1
  384. package/src/components/notification/examples/dismiss.tsx +2 -2
  385. package/src/components/page-content/COMPONENT.md +1 -0
  386. package/src/components/page-content/examples/list-detail.tsx +0 -3
  387. package/src/components/page-content/examples/screen.module.css +27 -0
  388. package/src/components/page-content/examples/screen.tsx +196 -0
  389. package/src/components/page-content/examples/variants.tsx +4 -11
  390. package/src/components/page-toolbar/COMPONENT.md +17 -15
  391. package/src/components/page-toolbar/examples/narrow.tsx +6 -3
  392. package/src/components/pagination/examples/controlled.tsx +29 -29
  393. package/src/components/pagination/examples/examples.module.css +0 -2
  394. package/src/components/progress-bar/COMPONENT.md +1 -1
  395. package/src/components/progress-bar/examples/segments.tsx +2 -2
  396. package/src/components/progress-circle/COMPONENT.md +1 -1
  397. package/src/components/progress-circle/examples/segments.tsx +8 -3
  398. package/src/components/radio/COMPONENT.md +1 -0
  399. package/src/components/radio/examples/examples.module.css +14 -0
  400. package/src/components/radio/examples/in-form.tsx +45 -0
  401. package/src/components/scroll-container/COMPONENT.md +1 -0
  402. package/src/components/scroll-container/examples/auto-scroll.tsx +3 -3
  403. package/src/components/scroll-container/examples/chat.tsx +3 -3
  404. package/src/components/scroll-container/examples/edges.tsx +6 -3
  405. package/src/components/scroll-container/examples/examples.module.css +4 -17
  406. package/src/components/scroll-container/examples/load-more.tsx +51 -0
  407. package/src/components/scroll-container/examples/lock.tsx +3 -5
  408. package/src/components/scroll-container/examples/overscroll.tsx +3 -3
  409. package/src/components/scroll-container/examples/overview.tsx +3 -3
  410. package/src/components/scroll-container/examples/scrollbar.tsx +13 -7
  411. package/src/components/scroll-container/examples/variants.tsx +13 -7
  412. package/src/components/segmented-control/COMPONENT.md +2 -2
  413. package/src/components/segmented-control/examples/in-form.tsx +5 -1
  414. package/src/components/segmented-control/examples/states.tsx +10 -1
  415. package/src/components/select/COMPONENT.md +1 -1
  416. package/src/components/select/examples/examples.module.css +2 -7
  417. package/src/components/select/examples/states.tsx +13 -1
  418. package/src/components/sheet/COMPONENT.md +2 -0
  419. package/src/components/sheet/examples/guard.tsx +72 -0
  420. package/src/components/sheet/examples/header-actions.tsx +56 -0
  421. package/src/components/slider/COMPONENT.md +2 -1
  422. package/src/components/slider/examples/examples.module.css +8 -0
  423. package/src/components/slider/examples/in-form.tsx +47 -0
  424. package/src/components/slider/examples/states.tsx +4 -1
  425. package/src/components/smart-filter/COMPONENT.md +1 -1
  426. package/src/components/smart-filter/examples/controlled.tsx +3 -3
  427. package/src/components/smart-filter/examples/ranges.tsx +44 -10
  428. package/src/components/sparkline/COMPONENT.md +1 -1
  429. package/src/components/sparkline/examples/reference.tsx +2 -1
  430. package/src/components/tabs/COMPONENT.md +1 -1
  431. package/src/components/tabs/examples/sizes.tsx +1 -1
  432. package/src/components/tag-select/COMPONENT.md +1 -1
  433. package/src/components/tag-select/examples/states.tsx +8 -1
  434. package/src/components/textarea/COMPONENT.md +1 -1
  435. package/src/components/textarea/examples/examples.module.css +0 -6
  436. package/src/components/textarea/examples/in-form.tsx +1 -9
  437. package/src/components/textarea/examples/states.tsx +2 -1
  438. package/src/components/thumbnail/COMPONENT.md +2 -1
  439. package/src/components/thumbnail/examples/examples.module.css +1 -1
  440. package/src/components/thumbnail/examples/fit.tsx +24 -0
  441. package/src/components/thumbnail/examples/video.tsx +20 -1
  442. package/src/components/timeline/COMPONENT.md +1 -1
  443. package/src/components/timeline/examples/links.tsx +55 -14
  444. package/src/components/tooltip/COMPONENT.md +1 -1
  445. package/src/components/tooltip/examples/toolbar.tsx +2 -2
  446. package/src/components/typography/examples/weights.tsx +3 -0
  447. package/src/components/video-note/COMPONENT.md +2 -2
  448. package/src/components/video-note/examples/controlled.tsx +19 -2
  449. package/src/components/video-note/examples/in-message.tsx +5 -2
  450. package/src/components/visually-hidden/COMPONENT.md +2 -1
  451. package/src/components/visually-hidden/examples/assertive.tsx +39 -0
  452. package/src/components/voice-player/COMPONENT.md +2 -2
  453. package/src/components/voice-player/examples/call-recording.tsx +19 -9
  454. package/src/components/voice-player/examples/controlled.tsx +20 -3
  455. package/src/layout/app-header/COMPONENT.md +46 -63
  456. package/src/layout/app-header/examples/breadcrumbs.tsx +10 -9
  457. package/src/layout/app-header/examples/examples.module.css +1 -1
  458. package/src/layout/app-header/examples/in-app-shell.tsx +0 -1
  459. package/src/layout/app-header/examples/narrow.tsx +4 -5
  460. package/src/layout/app-header/examples/overview.tsx +8 -10
  461. package/src/layout/app-header/examples/page-title.tsx +3 -10
  462. package/src/layout/app-shell/COMPONENT.md +4 -3
  463. package/src/layout/app-shell/examples/overview.tsx +15 -30
  464. package/src/layout/app-shell/examples/screen.module.css +11 -0
  465. package/src/layout/app-shell/examples/screen.tsx +262 -0
  466. package/src/layout/app-shell/examples/template.tsx +29 -9
  467. package/src/layout/sidebar/COMPONENT.md +7 -4
  468. package/src/layout/sidebar/examples/account.tsx +2 -2
  469. package/src/layout/sidebar/examples/collapsible-groups.tsx +12 -4
  470. package/src/layout/sidebar/examples/custom-part.tsx +53 -0
  471. package/src/layout/sidebar/examples/examples.module.css +5 -0
  472. package/src/layout/sidebar/examples/narrow.tsx +53 -0
  473. package/src/layout/sidebar/examples/persist.tsx +57 -0
  474. package/src/layout/sidebar/examples/router.tsx +14 -2
  475. package/src/layout/sidebar/examples/structure.tsx +7 -2
  476. package/dist/skill/patterns/app-shell.tsx +0 -303
  477. package/dist/skill/patterns/chat.module.css +0 -8
  478. package/dist/skill/patterns/dashboard.tsx +0 -340
  479. package/dist/skill/patterns/detail-page.tsx +0 -206
  480. package/dist/skill/patterns/form-drawer.tsx +0 -240
  481. package/dist/skill/patterns/kanban-board.module.css +0 -28
  482. package/dist/skill/patterns/media-library.tsx +0 -637
  483. package/dist/skill/patterns/schedule.module.css +0 -22
  484. package/dist/skill/patterns/screen-states.tsx +0 -187
  485. /package/dist/skill/{patterns/dashboard.module.css → reference/components/card/examples/screen.module.css} +0 -0
  486. /package/dist/skill/{patterns/screen-states.module.css → reference/components/crossfade/examples/screen.module.css} +0 -0
  487. /package/dist/skill/{patterns/form-drawer.module.css → reference/components/drawer/examples/screen.module.css} +0 -0
  488. /package/dist/skill/{patterns/record-editor.module.css → reference/components/field-list/examples/screen.module.css} +0 -0
  489. /package/dist/skill/{patterns/settings-page.module.css → reference/components/page-content/examples/screen.module.css} +0 -0
  490. /package/dist/skill/{patterns/app-shell.module.css → reference/layout/app-shell/examples/screen.module.css} +0 -0
@@ -0,0 +1,726 @@
1
+ /** A team schedule as a work screen: the title with the people shown and a PageToolbar with search, filters, team sections and the primary action in the app header over a full-height Gantt; moves saved or refused, new work drawn and named in a Modal, details in a Drawer — `scrollHeight`, `onBarChange`, `onBarCreate`, `onSelect`. */
2
+ import {
3
+ AppHeader,
4
+ Avatar,
5
+ Badge,
6
+ Button,
7
+ ConfirmDialog,
8
+ Datepicker,
9
+ DescriptionList,
10
+ Drawer,
11
+ EmptyPage,
12
+ Fieldset,
13
+ Gantt,
14
+ type GanttBar,
15
+ type GanttDates,
16
+ Icon,
17
+ Input,
18
+ Modal,
19
+ matchesSmartFilter,
20
+ PageContent,
21
+ PageToolbar,
22
+ ProgressBar,
23
+ SegmentedControl,
24
+ Select,
25
+ SmartFilter,
26
+ type SmartFilterField,
27
+ type SmartFilterValue,
28
+ Typography,
29
+ useNotifications,
30
+ } from "prime-ui-kit";
31
+ import * as React from "react";
32
+
33
+ import styles from "./screen.module.css";
34
+
35
+ type Hue = NonNullable<GanttBar["color"]>;
36
+ type TeamId = "rollout" | "analytics" | "support";
37
+ type ClientId = "tekhnostil" | "granit" | "mostostroy" | "kvartal";
38
+ type Kind = "project" | "duty" | "absence" | "milestone";
39
+
40
+ /** A bar of the chart with the fields the page needs back in its callbacks. */
41
+ type Assignment = GanttBar & { kind: Kind; client: ClientId | null };
42
+
43
+ type Person = {
44
+ id: string;
45
+ name: string;
46
+ initials: string;
47
+ role: string;
48
+ team: TeamId;
49
+ color: Hue;
50
+ bars: Assignment[];
51
+ };
52
+
53
+ /** What the create dialog is for: a range drawn on a row, or the toolbar button. */
54
+ type Draft = { personId: string | null; dates: GanttDates | null };
55
+
56
+ type Errors = Partial<Record<"title" | "client" | "person" | "period", string>>;
57
+
58
+ const TEAMS: { value: TeamId; label: string }[] = [
59
+ { value: "rollout", label: "Внедрение" },
60
+ { value: "analytics", label: "Аналитика" },
61
+ { value: "support", label: "Поддержка" },
62
+ ];
63
+
64
+ /** One hue per client across the product: the bar label says the work, the hue repeats the client. */
65
+ const CLIENTS: Record<ClientId, { name: string; color: Hue }> = {
66
+ tekhnostil: { name: "АО «Техностиль»", color: "blue" },
67
+ granit: { name: "ООО «Гранит»", color: "teal" },
68
+ mostostroy: { name: "ООО «Мостострой»", color: "orange" },
69
+ kvartal: { name: "ООО «Зелёный квартал»", color: "pink" },
70
+ };
71
+
72
+ const KIND: Record<Kind, string> = {
73
+ project: "Работа по проекту",
74
+ duty: "Внутренняя задача",
75
+ absence: "Отсутствие",
76
+ milestone: "Веха",
77
+ };
78
+
79
+ const DAY_MS = 86_400_000;
80
+
81
+ const addDays = (date: Date, days: number) => {
82
+ const next = new Date(date);
83
+ next.setDate(next.getDate() + days);
84
+ return next;
85
+ };
86
+
87
+ const TODAY = new Date(new Date().setHours(0, 0, 0, 0));
88
+ const WEEK = addDays(TODAY, -((TODAY.getDay() + 6) % 7));
89
+ const at = (days: number) => addDays(WEEK, days);
90
+
91
+ const project = (
92
+ id: string,
93
+ label: string,
94
+ client: ClientId,
95
+ from: number,
96
+ to: number,
97
+ progress?: number,
98
+ ): Assignment => ({
99
+ id,
100
+ label,
101
+ kind: "project",
102
+ client,
103
+ color: CLIENTS[client].color,
104
+ start: at(from),
105
+ end: at(to),
106
+ progress,
107
+ });
108
+
109
+ const milestone = (id: string, label: string, client: ClientId, day: number): Assignment => ({
110
+ id,
111
+ label,
112
+ kind: "milestone",
113
+ client,
114
+ tone: "accent",
115
+ start: at(day),
116
+ end: at(day),
117
+ });
118
+
119
+ /** Absences are agreed elsewhere: the chart shows them and refuses work on top of them. */
120
+ const absence = (id: string, label: string, from: number, to: number): Assignment => ({
121
+ id,
122
+ label,
123
+ kind: "absence",
124
+ client: null,
125
+ color: "purple",
126
+ readOnly: true,
127
+ start: at(from),
128
+ end: at(to),
129
+ });
130
+
131
+ const duty = (id: string, from: number, to: number): Assignment => ({
132
+ id,
133
+ label: "Дежурство",
134
+ kind: "duty",
135
+ client: null,
136
+ tone: "neutral",
137
+ start: at(from),
138
+ end: at(to),
139
+ });
140
+
141
+ const PEOPLE: Person[] = [
142
+ {
143
+ id: "sokolov",
144
+ name: "Пётр Соколов",
145
+ initials: "ПС",
146
+ role: "Руководитель проекта",
147
+ team: "rollout",
148
+ color: "blue",
149
+ bars: [
150
+ project("s-1", "Внедрение CRM", "granit", -3, 12, 0.5),
151
+ project("s-2", "Планирование работ", "mostostroy", 8, 16),
152
+ milestone("s-3", "Запуск у «Техностиля»", "tekhnostil", 9),
153
+ ],
154
+ },
155
+ {
156
+ id: "kotova",
157
+ name: "Анна Котова",
158
+ initials: "АК",
159
+ role: "Аналитик",
160
+ team: "analytics",
161
+ color: "green",
162
+ bars: [
163
+ project("k-1", "Обследование процессов", "granit", -4, 5, 0.8),
164
+ project("k-2", "ТЗ на интеграцию", "mostostroy", 6, 15),
165
+ absence("k-3", "Отпуск", 17, 27),
166
+ ],
167
+ },
168
+ {
169
+ id: "gordeev",
170
+ name: "Илья Гордеев",
171
+ initials: "ИГ",
172
+ role: "Разработчик интеграций",
173
+ team: "rollout",
174
+ color: "orange",
175
+ bars: [
176
+ project("g-1", "Интеграция с 1С", "tekhnostil", -6, 9, 0.6),
177
+ project("g-2", "Обмен с банком", "kvartal", 4, 14),
178
+ ],
179
+ },
180
+ {
181
+ id: "belova",
182
+ name: "Ольга Белова",
183
+ initials: "ОБ",
184
+ role: "Инженер внедрения",
185
+ team: "rollout",
186
+ color: "purple",
187
+ bars: [
188
+ absence("b-1", "Отпуск", -2, 5),
189
+ project("b-2", "Настройка ролей", "tekhnostil", 5, 12),
190
+ project("b-3", "Обучение пользователей", "granit", 14, 19),
191
+ ],
192
+ },
193
+ {
194
+ id: "khasanov",
195
+ name: "Марат Хасанов",
196
+ initials: "МХ",
197
+ role: "Инженер внедрения",
198
+ team: "rollout",
199
+ color: "teal",
200
+ bars: [project("h-1", "Миграция данных", "mostostroy", 0, 18, 0.2)],
201
+ },
202
+ {
203
+ id: "zueva",
204
+ name: "Елена Зуева",
205
+ initials: "ЕЗ",
206
+ role: "Аналитик",
207
+ team: "analytics",
208
+ color: "sky",
209
+ bars: [
210
+ project("z-1", "Отчёты руководителя", "kvartal", 2, 10, 0.4),
211
+ milestone("z-2", "Приёмка отчётов", "kvartal", 10),
212
+ ],
213
+ },
214
+ {
215
+ id: "orlov",
216
+ name: "Денис Орлов",
217
+ initials: "ДО",
218
+ role: "Инженер поддержки",
219
+ team: "support",
220
+ color: "yellow",
221
+ bars: [duty("o-1", 0, 7), project("o-2", "Сопровождение запуска", "tekhnostil", 9, 23)],
222
+ },
223
+ {
224
+ id: "lebedeva",
225
+ name: "Мария Лебедева",
226
+ initials: "МЛ",
227
+ role: "Инженер поддержки",
228
+ team: "support",
229
+ color: "pink",
230
+ bars: [absence("l-1", "Больничный", -1, 2), duty("l-2", 7, 14)],
231
+ },
232
+ {
233
+ id: "morozov",
234
+ name: "Антон Морозов",
235
+ initials: "АМ",
236
+ role: "Разработчик интеграций",
237
+ team: "rollout",
238
+ color: "red",
239
+ bars: [],
240
+ },
241
+ ];
242
+
243
+ const ROLES = [...new Set(PEOPLE.map((person) => person.role))];
244
+
245
+ const FIELDS: SmartFilterField[] = [
246
+ { key: "role", label: "Роль", options: ROLES.map((role) => ({ value: role, label: role })) },
247
+ {
248
+ key: "client",
249
+ label: "Клиент",
250
+ options: (Object.keys(CLIENTS) as ClientId[]).map((value) => ({
251
+ value,
252
+ label: CLIENTS[value].name,
253
+ })),
254
+ },
255
+ ];
256
+
257
+ const DAY = new Intl.DateTimeFormat("ru-RU", { day: "numeric", month: "long" });
258
+
259
+ const formatPeriod = ({ start, end }: GanttDates) =>
260
+ start.getTime() === end.getTime()
261
+ ? DAY.format(start)
262
+ : `${DAY.format(start)} – ${DAY.format(addDays(end, -1))}`;
263
+
264
+ const lengthInDays = ({ start, end }: GanttDates) =>
265
+ Math.round((end.getTime() - start.getTime()) / DAY_MS);
266
+
267
+ const overlapsAbsence = (person: Person, dates: GanttDates, barId?: string) =>
268
+ person.bars.some(
269
+ (bar) =>
270
+ bar.kind === "absence" && bar.id !== barId && dates.start < bar.end && bar.start < dates.end,
271
+ );
272
+
273
+ /** The stand-in for the schedule service's response time. */
274
+ const wait = (ms: number) => new Promise((resolve) => window.setTimeout(resolve, ms));
275
+
276
+ /** `yyyy-MM-dd` from the Datepicker's hidden input → a local calendar day. */
277
+ const parseDay = (value: FormDataEntryValue) => new Date(`${value}T00:00`);
278
+
279
+ export default function GanttScreenExample() {
280
+ const { notify } = useNotifications();
281
+ const formId = React.useId();
282
+ const [people, setPeople] = React.useState(PEOPLE);
283
+ const [loading, setLoading] = React.useState(true);
284
+ const [team, setTeam] = React.useState("all");
285
+ const [filter, setFilter] = React.useState<SmartFilterValue>({});
286
+ const [search, setSearch] = React.useState("");
287
+ const [selected, setSelected] = React.useState<{ bar: Assignment; person: Person } | null>(null);
288
+ const [detailOpen, setDetailOpen] = React.useState(false);
289
+ const [draft, setDraft] = React.useState<Draft | null>(null);
290
+ const [errors, setErrors] = React.useState<Errors>({});
291
+ const [saving, setSaving] = React.useState(false);
292
+ // The chart keeps a busy ghost while the drawn range is being named; the dialog settles it.
293
+ const pendingCreate = React.useRef<{ resolve: () => void; reject: (reason: Error) => void }>(
294
+ null,
295
+ );
296
+
297
+ React.useEffect(() => {
298
+ const timer = window.setTimeout(() => setLoading(false), 900);
299
+ return () => window.clearTimeout(timer);
300
+ }, []);
301
+
302
+ const query = search.trim().toLowerCase();
303
+ // Absences stay visible under a client filter: whether a person is free matters for any client.
304
+ // `matchesSmartFilter(entry, null)` is true only while that filter is empty.
305
+ const rows = people
306
+ .filter(
307
+ (person) =>
308
+ (team === "all" || person.team === team) &&
309
+ matchesSmartFilter(filter.role, person.role) &&
310
+ person.name.toLowerCase().includes(query),
311
+ )
312
+ .map((person) => ({
313
+ ...person,
314
+ bars: person.bars.filter(
315
+ (bar) => bar.kind === "absence" || matchesSmartFilter(filter.client, bar.client),
316
+ ),
317
+ }))
318
+ .filter(
319
+ (person) =>
320
+ matchesSmartFilter(filter.client, null) ||
321
+ person.bars.some((bar) => bar.kind !== "absence"),
322
+ );
323
+
324
+ const findPerson = (id: string) => people.find((person) => person.id === id) as Person;
325
+
326
+ const updateBars = (personId: string, change: (bars: Assignment[]) => Assignment[]) =>
327
+ setPeople((current) =>
328
+ current.map((person) =>
329
+ person.id === personId ? { ...person, bars: change(person.bars) } : person,
330
+ ),
331
+ );
332
+
333
+ const resetFilters = () => {
334
+ setTeam("all");
335
+ setFilter({});
336
+ setSearch("");
337
+ };
338
+
339
+ // The rows change only after the service agrees, so a refused move glides back by itself.
340
+ const moveBar = async (dates: GanttDates, bar: Assignment, row: Person) => {
341
+ const person = findPerson(row.id);
342
+ await wait(700);
343
+ if (overlapsAbsence(person, dates, bar.id)) {
344
+ notify({
345
+ tone: "danger",
346
+ title: "Сроки не изменены",
347
+ description: `${person.name} в эти дни отсутствует`,
348
+ });
349
+ throw new Error("Пересекается с отсутствием");
350
+ }
351
+ updateBars(person.id, (bars) =>
352
+ bars.map((item) => (item.id === bar.id ? { ...item, ...dates } : item)),
353
+ );
354
+ };
355
+
356
+ const startCreate = (dates: GanttDates, row: Person) => {
357
+ const person = findPerson(row.id);
358
+ if (overlapsAbsence(person, dates)) {
359
+ notify({
360
+ tone: "warning",
361
+ title: "Задача не назначена",
362
+ description: `${person.name} в эти дни отсутствует`,
363
+ });
364
+ return Promise.reject(new Error("Пересекается с отсутствием"));
365
+ }
366
+ return new Promise<void>((resolve, reject) => {
367
+ pendingCreate.current = { resolve, reject };
368
+ setErrors({});
369
+ setDraft({ personId: person.id, dates });
370
+ });
371
+ };
372
+
373
+ const closeDraft = (open: boolean) => {
374
+ if (open) return;
375
+ pendingCreate.current?.reject(new Error("Отменено"));
376
+ pendingCreate.current = null;
377
+ setDraft(null);
378
+ };
379
+
380
+ const submit = async (event: React.FormEvent<HTMLFormElement>) => {
381
+ event.preventDefault();
382
+ if (!draft) return;
383
+ const data = new FormData(event.currentTarget);
384
+ const title = String(data.get("title") ?? "").trim();
385
+ const client = String(data.get("client") ?? "") as ClientId | "";
386
+ const personId = draft.personId ?? String(data.get("person") ?? "");
387
+ const [from, to] = data.getAll("period");
388
+ const dates =
389
+ draft.dates ?? (from && to ? { start: parseDay(from), end: addDays(parseDay(to), 1) } : null);
390
+
391
+ const found: Errors = {};
392
+ if (!title) found.title = "Назовите задачу";
393
+ if (!client) found.client = "Выберите клиента";
394
+ if (!personId) found.person = "Выберите сотрудника";
395
+ if (!dates) found.period = "Укажите начало и конец работ";
396
+ else if (personId && overlapsAbsence(findPerson(personId), dates))
397
+ found.period = "Сотрудник в эти дни отсутствует";
398
+ setErrors(found);
399
+ const firstInvalid = (["title", "client", "person", "period"] as const).find(
400
+ (key) => found[key],
401
+ );
402
+ if (firstInvalid || !client || !dates) {
403
+ if (firstInvalid) document.getElementById(`${formId}-${firstInvalid}`)?.focus();
404
+ return;
405
+ }
406
+
407
+ setSaving(true);
408
+ await wait(700);
409
+ const person = findPerson(personId);
410
+ updateBars(person.id, (bars) => [
411
+ ...bars,
412
+ {
413
+ id: `${person.id}-${Date.now()}`,
414
+ label: title,
415
+ kind: "project",
416
+ client,
417
+ color: CLIENTS[client].color,
418
+ ...dates,
419
+ },
420
+ ]);
421
+ pendingCreate.current?.resolve();
422
+ pendingCreate.current = null;
423
+ setSaving(false);
424
+ setDraft(null);
425
+ notify({
426
+ tone: "success",
427
+ title: "Задача назначена",
428
+ description: `${title} · ${person.name}`,
429
+ });
430
+ };
431
+
432
+ const removeSelected = async () => {
433
+ if (!selected) return;
434
+ await wait(700);
435
+ updateBars(selected.person.id, (bars) => bars.filter((bar) => bar.id !== selected.bar.id));
436
+ setDetailOpen(false);
437
+ notify({ tone: "success", title: "Задача снята", description: selected.bar.label });
438
+ };
439
+
440
+ const draftPerson = draft?.personId ? findPerson(draft.personId) : null;
441
+ const detail = selected?.bar;
442
+
443
+ return (
444
+ <>
445
+ {/* A work screen: in an app this header fills the shell's `header` slot and carries the page —
446
+ the only h1 and the page's tools; main has no PageContent.Header. */}
447
+ <AppHeader.Root>
448
+ <AppHeader.Start>
449
+ <AppHeader.Title>
450
+ Загрузка команды
451
+ <AppHeader.Description>
452
+ {loading ? "Загружаем график…" : `Сотрудников: ${rows.length} из ${people.length}`}
453
+ </AppHeader.Description>
454
+ </AppHeader.Title>
455
+ </AppHeader.Start>
456
+ <AppHeader.Tools>
457
+ <SmartFilter.Root
458
+ fields={FIELDS}
459
+ value={filter}
460
+ onValueChange={setFilter}
461
+ search={search}
462
+ onSearchChange={setSearch}
463
+ >
464
+ <PageToolbar.Root aria-label="Загрузка команды">
465
+ <PageToolbar.Tools>
466
+ <SmartFilter.Toolbar />
467
+ </PageToolbar.Tools>
468
+ <PageToolbar.Sections>
469
+ <SegmentedControl.Root
470
+ fullWidth
471
+ value={team}
472
+ onValueChange={setTeam}
473
+ aria-label="Команда"
474
+ >
475
+ <SegmentedControl.Item value="all">
476
+ Все<SegmentedControl.Count>{people.length}</SegmentedControl.Count>
477
+ </SegmentedControl.Item>
478
+ {TEAMS.map((item) => (
479
+ <SegmentedControl.Item key={item.value} value={item.value}>
480
+ {item.label}
481
+ <SegmentedControl.Count>
482
+ {people.filter((person) => person.team === item.value).length}
483
+ </SegmentedControl.Count>
484
+ </SegmentedControl.Item>
485
+ ))}
486
+ </SegmentedControl.Root>
487
+ </PageToolbar.Sections>
488
+ <PageToolbar.Actions>
489
+ <Button.Root
490
+ onClick={() => {
491
+ setErrors({});
492
+ setDraft({ personId: null, dates: null });
493
+ }}
494
+ >
495
+ <Button.Icon>
496
+ <Icon name="action.add" />
497
+ </Button.Icon>
498
+ Назначить
499
+ </Button.Root>
500
+ </PageToolbar.Actions>
501
+ <PageToolbar.Chips>
502
+ <SmartFilter.Chips />
503
+ </PageToolbar.Chips>
504
+ </PageToolbar.Root>
505
+ </SmartFilter.Root>
506
+ </AppHeader.Tools>
507
+ </AppHeader.Root>
508
+
509
+ <PageContent.Root fill>
510
+ <PageContent.Body>
511
+ <Gantt<Person, Assignment>
512
+ aria-label="Загрузка команды внедрения"
513
+ className={styles.chart}
514
+ defaultScale="day"
515
+ scrollHeight="fill"
516
+ loading={loading}
517
+ rows={loading ? [] : rows}
518
+ getRowKey={(person) => person.id}
519
+ getRowLabel={(person) => person.name}
520
+ getRowBars={(person) => person.bars}
521
+ renderRowAvatar={(person) => (
522
+ <Avatar.Root color={person.color}>
523
+ <Avatar.Fallback>{person.initials}</Avatar.Fallback>
524
+ </Avatar.Root>
525
+ )}
526
+ renderRow={(person) => (
527
+ <span className={styles.person}>
528
+ <Typography as="span" variant="body-m">
529
+ {person.name}
530
+ </Typography>
531
+ <Typography as="span" variant="caption" tone="muted">
532
+ {person.role}
533
+ </Typography>
534
+ </span>
535
+ )}
536
+ onBarChange={moveBar}
537
+ onBarCreate={startCreate}
538
+ onSelect={(bar, row) => {
539
+ setSelected({ bar, person: findPerson(row.id) });
540
+ setDetailOpen(true);
541
+ }}
542
+ renderBarTooltip={(bar) => (bar.client ? CLIENTS[bar.client].name : KIND[bar.kind])}
543
+ toolbar={
544
+ <>
545
+ {/* The legend of client hues: the bar's hue repeats its client, the tooltip names it. */}
546
+ {(Object.keys(CLIENTS) as ClientId[]).map((client) => (
547
+ <Badge.Root key={client} color={CLIENTS[client].color}>
548
+ <Badge.Dot />
549
+ {CLIENTS[client].name}
550
+ </Badge.Root>
551
+ ))}
552
+ </>
553
+ }
554
+ empty={
555
+ <EmptyPage.Root size="s" role="status" aria-labelledby="schedule-empty">
556
+ <EmptyPage.Icon>
557
+ <Icon name="action.search" />
558
+ </EmptyPage.Icon>
559
+ <EmptyPage.Title as="p" id="schedule-empty">
560
+ Никого не нашли
561
+ </EmptyPage.Title>
562
+ <EmptyPage.Description>Измените запрос или сбросьте фильтры.</EmptyPage.Description>
563
+ <EmptyPage.Actions>
564
+ <Button.Root variant="soft" tone="neutral" onClick={resetFilters}>
565
+ Сбросить фильтры
566
+ </Button.Root>
567
+ </EmptyPage.Actions>
568
+ </EmptyPage.Root>
569
+ }
570
+ />
571
+
572
+ <Modal.Root open={draft !== null} onOpenChange={closeDraft}>
573
+ <Modal.Content size="s">
574
+ <Modal.Header>
575
+ <Modal.Title>Новая задача</Modal.Title>
576
+ <Modal.Description>
577
+ {draftPerson && draft?.dates
578
+ ? `${draftPerson.name} · ${formatPeriod(draft.dates)}`
579
+ : "Сотрудник увидит задачу в своём графике."}
580
+ </Modal.Description>
581
+ </Modal.Header>
582
+ <Modal.Body>
583
+ <Modal.Guard busy={saving} />
584
+ <form id={formId} noValidate onSubmit={submit}>
585
+ <Fieldset aria-label="Задача" disabled={saving}>
586
+ <Input.Root id={`${formId}-title`} label="Задача" required error={errors.title}>
587
+ <Input.Wrapper>
588
+ <Input.Field name="title" placeholder="Обучение пользователей" autoFocus />
589
+ </Input.Wrapper>
590
+ </Input.Root>
591
+ <Select.Root
592
+ name="client"
593
+ label="Клиент"
594
+ required
595
+ placeholder="Выберите клиента"
596
+ error={errors.client}
597
+ id={`${formId}-client`}
598
+ >
599
+ <Select.Trigger>
600
+ <Select.Value />
601
+ </Select.Trigger>
602
+ <Select.Content>
603
+ {(Object.keys(CLIENTS) as ClientId[]).map((value) => (
604
+ <Select.Item key={value} value={value}>
605
+ {CLIENTS[value].name}
606
+ </Select.Item>
607
+ ))}
608
+ </Select.Content>
609
+ </Select.Root>
610
+ {draft?.personId ? null : (
611
+ <>
612
+ <Select.Root
613
+ name="person"
614
+ label="Сотрудник"
615
+ required
616
+ placeholder="Выберите сотрудника"
617
+ error={errors.person}
618
+ id={`${formId}-person`}
619
+ >
620
+ <Select.Trigger>
621
+ <Select.Value />
622
+ </Select.Trigger>
623
+ <Select.Content searchable>
624
+ {people.map((person) => (
625
+ <Select.Item key={person.id} value={person.id} keywords={person.role}>
626
+ {person.name}
627
+ </Select.Item>
628
+ ))}
629
+ </Select.Content>
630
+ </Select.Root>
631
+ <Datepicker.Root
632
+ mode="range"
633
+ name="period"
634
+ label="Период"
635
+ required
636
+ fullWidth
637
+ placeholder="Начало и конец работ"
638
+ error={errors.period}
639
+ id={`${formId}-period`}
640
+ />
641
+ </>
642
+ )}
643
+ </Fieldset>
644
+ </form>
645
+ </Modal.Body>
646
+ <Modal.Footer>
647
+ <Modal.Close>
648
+ <Button.Root variant="outline" tone="neutral">
649
+ Отмена
650
+ </Button.Root>
651
+ </Modal.Close>
652
+ <Button.Root type="submit" form={formId} loading={saving}>
653
+ Назначить
654
+ </Button.Root>
655
+ </Modal.Footer>
656
+ </Modal.Content>
657
+ </Modal.Root>
658
+
659
+ <Drawer.Root open={detailOpen} onOpenChange={setDetailOpen}>
660
+ <Drawer.Content size="s">
661
+ <Drawer.Header>
662
+ <Drawer.Title>{detail?.label}</Drawer.Title>
663
+ <Drawer.Description>{detail ? KIND[detail.kind] : null}</Drawer.Description>
664
+ </Drawer.Header>
665
+ <Drawer.Body>
666
+ {selected && detail ? (
667
+ <DescriptionList.Root orientation="vertical">
668
+ <DescriptionList.Item label="Сотрудник">
669
+ {selected.person.name} · {selected.person.role}
670
+ </DescriptionList.Item>
671
+ <DescriptionList.Item label="Клиент">
672
+ {detail.client ? (
673
+ <Badge.Root color={CLIENTS[detail.client].color}>
674
+ {CLIENTS[detail.client].name}
675
+ </Badge.Root>
676
+ ) : (
677
+ "Без клиента"
678
+ )}
679
+ </DescriptionList.Item>
680
+ <DescriptionList.Item label={detail.kind === "milestone" ? "Дата" : "Период"}>
681
+ {formatPeriod(detail)}
682
+ {detail.kind === "milestone" ? null : ` · ${lengthInDays(detail)} дн.`}
683
+ </DescriptionList.Item>
684
+ {detail.progress === undefined ? null : (
685
+ <DescriptionList.Item label="Выполнено">
686
+ <ProgressBar
687
+ value={Math.round(detail.progress * 100)}
688
+ showValue
689
+ aria-label="Выполнено"
690
+ />
691
+ </DescriptionList.Item>
692
+ )}
693
+ </DescriptionList.Root>
694
+ ) : null}
695
+ </Drawer.Body>
696
+ <Drawer.Footer>
697
+ <Drawer.Close>
698
+ <Button.Root variant="outline" tone="neutral">
699
+ Закрыть
700
+ </Button.Root>
701
+ </Drawer.Close>
702
+ {detail && !detail.readOnly ? (
703
+ <ConfirmDialog.Root
704
+ title={`Снять задачу «${detail.label}»?`}
705
+ description={`${selected?.person.name} освободится на эти дни. Задачу можно будет назначить заново.`}
706
+ confirmLabel="Снять задачу"
707
+ onConfirm={removeSelected}
708
+ >
709
+ <ConfirmDialog.Trigger>
710
+ <Button.Root variant="soft" tone="danger">
711
+ <Button.Icon>
712
+ <Icon name="action.delete" />
713
+ </Button.Icon>
714
+ Снять задачу
715
+ </Button.Root>
716
+ </ConfirmDialog.Trigger>
717
+ </ConfirmDialog.Root>
718
+ ) : null}
719
+ </Drawer.Footer>
720
+ </Drawer.Content>
721
+ </Drawer.Root>
722
+ </PageContent.Body>
723
+ </PageContent.Root>
724
+ </>
725
+ );
726
+ }