create-wakii-stack 0.0.0-stage → 0.1.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 (998) hide show
  1. package/README.md +66 -2
  2. package/dist/chunk-7XGW4UJ5.js +75 -0
  3. package/dist/chunk-AKPWS7FJ.js +76 -0
  4. package/dist/chunk-ELM6GGC5.js +50 -0
  5. package/dist/chunk-ON2WTGZH.js +77 -0
  6. package/dist/index.js +385 -0
  7. package/dist/manifest-2JVLRMDB.js +10 -0
  8. package/dist/manifest-O35TPW73.js +10 -0
  9. package/dist/manifest-TXTY67BF.js +10 -0
  10. package/dist/manifest-XWHR7Y3F.js +10 -0
  11. package/package.json +23 -4
  12. package/stack-template/.changeset/config.json +15 -0
  13. package/stack-template/.changeset/vu53-ui-redesign.md +11 -0
  14. package/stack-template/.claude/agents/be-dev.md +56 -0
  15. package/stack-template/.claude/agents/contracts-keeper.md +51 -0
  16. package/stack-template/.claude/agents/e2e-runner.md +61 -0
  17. package/stack-template/.claude/agents/fe-dev.md +53 -0
  18. package/stack-template/.claude/settings.json +77 -0
  19. package/stack-template/.claude/settings.local.json +9 -0
  20. package/stack-template/.claude/skills/add-service/SKILL.md +81 -0
  21. package/stack-template/.claude/skills/sync-contracts/SKILL.md +74 -0
  22. package/stack-template/.editorconfig +21 -0
  23. package/stack-template/.gitattributes +10 -0
  24. package/stack-template/.github/pull_request_template.md +26 -0
  25. package/stack-template/.github/workflow-templates/services-ci.yml.example +69 -0
  26. package/stack-template/.github/workflows/contracts.yml +133 -0
  27. package/stack-template/.github/workflows/convergence.yml +209 -0
  28. package/stack-template/.github/workflows/git-guard.yml +44 -0
  29. package/stack-template/.github/workflows/publish-uikit.yml +96 -0
  30. package/stack-template/.github/workflows/remotes-activity.yml +82 -0
  31. package/stack-template/.github/workflows/remotes-tasks.yml +82 -0
  32. package/stack-template/.github/workflows/services-auth-svc.yml +153 -0
  33. package/stack-template/.github/workflows/services-go-svc.yml +281 -0
  34. package/stack-template/.github/workflows/services-node-svc.yml +173 -0
  35. package/stack-template/.github/workflows/services-spring-svc.yml +132 -0
  36. package/stack-template/.github/workflows/shell.yml +78 -0
  37. package/stack-template/.github/workflows/ui-kit.yml +51 -0
  38. package/stack-template/.husky/commit-msg +14 -0
  39. package/stack-template/.husky/pre-push +51 -0
  40. package/stack-template/.spectral.yaml +17 -0
  41. package/stack-template/AGENTS.md +197 -0
  42. package/stack-template/CLAUDE.md +114 -0
  43. package/stack-template/README.md +172 -0
  44. package/stack-template/app/admin/index.html +12 -0
  45. package/stack-template/app/admin/package.json +40 -0
  46. package/stack-template/app/admin/scripts/verify-chart.mjs +49 -0
  47. package/stack-template/app/admin/src/App.test.tsx +70 -0
  48. package/stack-template/app/admin/src/App.tsx +33 -0
  49. package/stack-template/app/admin/src/data/users.ts +47 -0
  50. package/stack-template/app/admin/src/layout/AdminLayout.tsx +212 -0
  51. package/stack-template/app/admin/src/main.tsx +17 -0
  52. package/stack-template/app/admin/src/pages/ComponentsDemo.tsx +138 -0
  53. package/stack-template/app/admin/src/pages/Dashboard.tsx +182 -0
  54. package/stack-template/app/admin/src/pages/HooksDemo.tsx +153 -0
  55. package/stack-template/app/admin/src/pages/Settings.tsx +156 -0
  56. package/stack-template/app/admin/src/pages/UploadPage.tsx +34 -0
  57. package/stack-template/app/admin/src/pages/Users.test.tsx +66 -0
  58. package/stack-template/app/admin/src/pages/Users.tsx +245 -0
  59. package/stack-template/app/admin/src/styles.css +9 -0
  60. package/stack-template/app/admin/src/tests/setup.ts +15 -0
  61. package/stack-template/app/admin/tsconfig.json +18 -0
  62. package/stack-template/app/admin/vite.config.ts +11 -0
  63. package/stack-template/app/admin/vitest.config.ts +14 -0
  64. package/stack-template/app/create-wakii-app/README.md +71 -0
  65. package/stack-template/app/create-wakii-app/package.json +27 -0
  66. package/stack-template/app/create-wakii-app/scripts/publish-npm.sh +38 -0
  67. package/stack-template/app/create-wakii-app/src/index.ts +127 -0
  68. package/stack-template/app/create-wakii-app/src/options.test.ts +80 -0
  69. package/stack-template/app/create-wakii-app/src/options.ts +117 -0
  70. package/stack-template/app/create-wakii-app/src/scaffold.test.ts +143 -0
  71. package/stack-template/app/create-wakii-app/src/scaffold.ts +194 -0
  72. package/stack-template/app/create-wakii-app/tsconfig.json +17 -0
  73. package/stack-template/app/create-wakii-app/vitest.config.ts +9 -0
  74. package/stack-template/app/create-wakii-stack/package.json +27 -0
  75. package/stack-template/app/create-wakii-stack/src/manifest.ts +101 -0
  76. package/stack-template/app/create-wakii-stack/src/options.ts +106 -0
  77. package/stack-template/app/create-wakii-stack/src/scaffold.ts +315 -0
  78. package/stack-template/app/create-wakii-stack/tsconfig.json +17 -0
  79. package/stack-template/app/create-wakii-stack/vitest.config.ts +9 -0
  80. package/stack-template/app/remotes/activity/Dockerfile +36 -0
  81. package/stack-template/app/remotes/activity/README.md +59 -0
  82. package/stack-template/app/remotes/activity/index.html +11 -0
  83. package/stack-template/app/remotes/activity/nginx.conf +35 -0
  84. package/stack-template/app/remotes/activity/package.json +40 -0
  85. package/stack-template/app/remotes/activity/rspack.config.mjs +152 -0
  86. package/stack-template/app/remotes/activity/src/ActivityPage.tsx +139 -0
  87. package/stack-template/app/remotes/activity/src/api.ts +63 -0
  88. package/stack-template/app/remotes/activity/src/app.css +7 -0
  89. package/stack-template/app/remotes/activity/src/components/FeedItem.tsx +39 -0
  90. package/stack-template/app/remotes/activity/src/components/StatTile.tsx +24 -0
  91. package/stack-template/app/remotes/activity/src/components/Timeline.tsx +53 -0
  92. package/stack-template/app/remotes/activity/src/components/time.ts +30 -0
  93. package/stack-template/app/remotes/activity/src/env.d.ts +5 -0
  94. package/stack-template/app/remotes/activity/src/index.tsx +12 -0
  95. package/stack-template/app/remotes/activity/src/standalone.tsx +36 -0
  96. package/stack-template/app/remotes/activity/tests/ActivityPage.test.tsx +260 -0
  97. package/stack-template/app/remotes/activity/tests/api.test.ts +61 -0
  98. package/stack-template/app/remotes/activity/tests/setup.ts +1 -0
  99. package/stack-template/app/remotes/activity/tests/test-utils.tsx +16 -0
  100. package/stack-template/app/remotes/activity/tsconfig.json +26 -0
  101. package/stack-template/app/remotes/activity/vitest.config.ts +38 -0
  102. package/stack-template/app/remotes/tasks/Dockerfile +36 -0
  103. package/stack-template/app/remotes/tasks/README.md +59 -0
  104. package/stack-template/app/remotes/tasks/index.html +11 -0
  105. package/stack-template/app/remotes/tasks/nginx.conf +35 -0
  106. package/stack-template/app/remotes/tasks/package.json +41 -0
  107. package/stack-template/app/remotes/tasks/rspack.config.mjs +153 -0
  108. package/stack-template/app/remotes/tasks/src/TasksPage.tsx +284 -0
  109. package/stack-template/app/remotes/tasks/src/api.ts +109 -0
  110. package/stack-template/app/remotes/tasks/src/app.css +7 -0
  111. package/stack-template/app/remotes/tasks/src/components/StatusBadge.tsx +20 -0
  112. package/stack-template/app/remotes/tasks/src/components/StatusSelect.tsx +27 -0
  113. package/stack-template/app/remotes/tasks/src/components/TaskForm.tsx +74 -0
  114. package/stack-template/app/remotes/tasks/src/components/TaskItem.tsx +199 -0
  115. package/stack-template/app/remotes/tasks/src/components/TaskList.tsx +60 -0
  116. package/stack-template/app/remotes/tasks/src/components/status.ts +23 -0
  117. package/stack-template/app/remotes/tasks/src/env.d.ts +5 -0
  118. package/stack-template/app/remotes/tasks/src/index.tsx +12 -0
  119. package/stack-template/app/remotes/tasks/src/standalone.tsx +38 -0
  120. package/stack-template/app/remotes/tasks/tests/TasksPage.test.tsx +445 -0
  121. package/stack-template/app/remotes/tasks/tests/api.test.ts +159 -0
  122. package/stack-template/app/remotes/tasks/tests/setup.ts +1 -0
  123. package/stack-template/app/remotes/tasks/tests/test-utils.tsx +16 -0
  124. package/stack-template/app/remotes/tasks/tsconfig.json +26 -0
  125. package/stack-template/app/remotes/tasks/vitest.config.ts +34 -0
  126. package/stack-template/app/shared/README.md +21 -0
  127. package/stack-template/app/shared/package.json +37 -0
  128. package/stack-template/app/shared/src/api-client.ts +66 -0
  129. package/stack-template/app/shared/src/error-envelope.ts +38 -0
  130. package/stack-template/app/shared/src/index.ts +21 -0
  131. package/stack-template/app/shared/src/mf-bootstrap.ts +89 -0
  132. package/stack-template/app/shared/src/refresh-client.ts +170 -0
  133. package/stack-template/app/shared/src/token-storage.ts +57 -0
  134. package/stack-template/app/shell/Dockerfile +37 -0
  135. package/stack-template/app/shell/README.md +79 -0
  136. package/stack-template/app/shell/index.html +11 -0
  137. package/stack-template/app/shell/nginx.conf +23 -0
  138. package/stack-template/app/shell/package.json +43 -0
  139. package/stack-template/app/shell/rspack.config.mjs +147 -0
  140. package/stack-template/app/shell/src/App.tsx +57 -0
  141. package/stack-template/app/shell/src/api.ts +36 -0
  142. package/stack-template/app/shell/src/app.css +11 -0
  143. package/stack-template/app/shell/src/bootstrap.tsx +24 -0
  144. package/stack-template/app/shell/src/components/Layout.tsx +35 -0
  145. package/stack-template/app/shell/src/components/NavBar.tsx +118 -0
  146. package/stack-template/app/shell/src/components/RemoteSlot.tsx +82 -0
  147. package/stack-template/app/shell/src/components/RequireAuth.tsx +14 -0
  148. package/stack-template/app/shell/src/env.d.ts +7 -0
  149. package/stack-template/app/shell/src/index.tsx +3 -0
  150. package/stack-template/app/shell/src/pages/HomePage.tsx +70 -0
  151. package/stack-template/app/shell/src/pages/LoginPage.tsx +217 -0
  152. package/stack-template/app/shell/src/remote-urls.ts +18 -0
  153. package/stack-template/app/shell/tests/a11y.test.tsx +53 -0
  154. package/stack-template/app/shell/tests/api-timeout.test.ts +43 -0
  155. package/stack-template/app/shell/tests/integration.test.tsx +93 -0
  156. package/stack-template/app/shell/tests/login.test.tsx +227 -0
  157. package/stack-template/app/shell/tests/mf-retry.test.ts +76 -0
  158. package/stack-template/app/shell/tests/refresh-client.test.ts +301 -0
  159. package/stack-template/app/shell/tests/remote-slot.test.tsx +109 -0
  160. package/stack-template/app/shell/tests/remote-urls.test.ts +26 -0
  161. package/stack-template/app/shell/tests/setup.ts +15 -0
  162. package/stack-template/app/shell/tests/shell-chrome.test.tsx +126 -0
  163. package/stack-template/app/shell/tsconfig.json +26 -0
  164. package/stack-template/app/shell/vitest.config.ts +42 -0
  165. package/stack-template/app/spike/README.md +35 -0
  166. package/stack-template/app/spike/host/index.html +11 -0
  167. package/stack-template/app/spike/host/package.json +17 -0
  168. package/stack-template/app/spike/host/rspack.config.mjs +34 -0
  169. package/stack-template/app/spike/host/src/bootstrap.jsx +26 -0
  170. package/stack-template/app/spike/host/src/index.jsx +3 -0
  171. package/stack-template/app/spike/remote/package.json +17 -0
  172. package/stack-template/app/spike/remote/rspack.config.mjs +36 -0
  173. package/stack-template/app/spike/remote/src/Hello.jsx +7 -0
  174. package/stack-template/app/spike/remote/src/index.js +2 -0
  175. package/stack-template/app/spike/serve.mjs +31 -0
  176. package/stack-template/app/starter-templates/template-feats/extras/carousel/src/DemoCarousel.tsx +99 -0
  177. package/stack-template/app/starter-templates/template-feats/extras/chart/src/DemoChart.tsx +84 -0
  178. package/stack-template/app/starter-templates/template-feats/extras/editor/src/DemoEditor.tsx +110 -0
  179. package/stack-template/app/starter-templates/template-feats/extras/schedule/src/DemoSchedule.tsx +81 -0
  180. package/stack-template/app/starter-templates/template-feats/router/src/App.tsx +332 -0
  181. package/stack-template/app/starter-templates/template-feats/router/src/main.tsx +14 -0
  182. package/stack-template/app/starter-templates/vite-js/README.md +72 -0
  183. package/stack-template/app/starter-templates/vite-js/index.html +12 -0
  184. package/stack-template/app/starter-templates/vite-js/jsconfig.json +10 -0
  185. package/stack-template/app/starter-templates/vite-js/package.json +22 -0
  186. package/stack-template/app/starter-templates/vite-js/src/App.jsx +158 -0
  187. package/stack-template/app/starter-templates/vite-js/src/main.jsx +10 -0
  188. package/stack-template/app/starter-templates/vite-js/src/styles.css +15 -0
  189. package/stack-template/app/starter-templates/vite-js/src/theme.js +21 -0
  190. package/stack-template/app/starter-templates/vite-js/vite.config.mjs +8 -0
  191. package/stack-template/app/starter-templates/vite-ts/README.md +53 -0
  192. package/stack-template/app/starter-templates/vite-ts/index.html +12 -0
  193. package/stack-template/app/starter-templates/vite-ts/package.json +29 -0
  194. package/stack-template/app/starter-templates/vite-ts/src/App.tsx +181 -0
  195. package/stack-template/app/starter-templates/vite-ts/src/main.tsx +12 -0
  196. package/stack-template/app/starter-templates/vite-ts/src/styles.css +21 -0
  197. package/stack-template/app/starter-templates/vite-ts/src/theme.ts +16 -0
  198. package/stack-template/app/starter-templates/vite-ts/tsconfig.json +25 -0
  199. package/stack-template/app/starter-templates/vite-ts/tsconfig.node.json +11 -0
  200. package/stack-template/app/starter-templates/vite-ts/vite.config.ts +10 -0
  201. package/stack-template/app/ui-kit/.storybook/main.ts +29 -0
  202. package/stack-template/app/ui-kit/.storybook/manager-head.html +97 -0
  203. package/stack-template/app/ui-kit/.storybook/manager.ts +138 -0
  204. package/stack-template/app/ui-kit/.storybook/preview-head.html +57 -0
  205. package/stack-template/app/ui-kit/.storybook/preview.ts +62 -0
  206. package/stack-template/app/ui-kit/.storybook/tailwind-preview.css +3 -0
  207. package/stack-template/app/ui-kit/.storybook/test-runner.config.ts +20 -0
  208. package/stack-template/app/ui-kit/CHANGELOG.md +137 -0
  209. package/stack-template/app/ui-kit/LICENSE +21 -0
  210. package/stack-template/app/ui-kit/MIGRATION.md +84 -0
  211. package/stack-template/app/ui-kit/README.md +173 -0
  212. package/stack-template/app/ui-kit/RELEASE.md +77 -0
  213. package/stack-template/app/ui-kit/USAGE.md +380 -0
  214. package/stack-template/app/ui-kit/eslint.config.js +47 -0
  215. package/stack-template/app/ui-kit/package.json +166 -0
  216. package/stack-template/app/ui-kit/scripts/publish-github.sh +39 -0
  217. package/stack-template/app/ui-kit/scripts/publish-npm.sh +76 -0
  218. package/stack-template/app/ui-kit/scripts/stamp-build.cjs +13 -0
  219. package/stack-template/app/ui-kit/src/KitchenSink.stories.tsx +576 -0
  220. package/stack-template/app/ui-kit/src/Tokens.stories.tsx +193 -0
  221. package/stack-template/app/ui-kit/src/entries/carousel.ts +7 -0
  222. package/stack-template/app/ui-kit/src/entries/chart.ts +4 -0
  223. package/stack-template/app/ui-kit/src/entries/editor.ts +4 -0
  224. package/stack-template/app/ui-kit/src/entries/experience3d.ts +5 -0
  225. package/stack-template/app/ui-kit/src/entries/schedule.ts +5 -0
  226. package/stack-template/app/ui-kit/src/env.d.ts +3 -0
  227. package/stack-template/app/ui-kit/src/hooks/form.stories.tsx +80 -0
  228. package/stack-template/app/ui-kit/src/hooks/hooks-new.stories.tsx +392 -0
  229. package/stack-template/app/ui-kit/src/hooks/hooks.stories.tsx +167 -0
  230. package/stack-template/app/ui-kit/src/hooks/useClickAway.test.tsx +63 -0
  231. package/stack-template/app/ui-kit/src/hooks/useClickAway.ts +30 -0
  232. package/stack-template/app/ui-kit/src/hooks/useControllableState.test.ts +36 -0
  233. package/stack-template/app/ui-kit/src/hooks/useControllableState.ts +41 -0
  234. package/stack-template/app/ui-kit/src/hooks/useCopyToClipboard.test.ts +89 -0
  235. package/stack-template/app/ui-kit/src/hooks/useCopyToClipboard.ts +20 -0
  236. package/stack-template/app/ui-kit/src/hooks/useCountdown.test.ts +88 -0
  237. package/stack-template/app/ui-kit/src/hooks/useCountdown.ts +38 -0
  238. package/stack-template/app/ui-kit/src/hooks/useDebounce.test.ts +71 -0
  239. package/stack-template/app/ui-kit/src/hooks/useDebounce.ts +14 -0
  240. package/stack-template/app/ui-kit/src/hooks/useDefault.test.ts +37 -0
  241. package/stack-template/app/ui-kit/src/hooks/useDefault.ts +15 -0
  242. package/stack-template/app/ui-kit/src/hooks/useEventListener.test.ts +47 -0
  243. package/stack-template/app/ui-kit/src/hooks/useEventListener.ts +28 -0
  244. package/stack-template/app/ui-kit/src/hooks/useFieldArray.test.tsx +160 -0
  245. package/stack-template/app/ui-kit/src/hooks/useFieldArray.ts +85 -0
  246. package/stack-template/app/ui-kit/src/hooks/useFocusTrap.ts +67 -0
  247. package/stack-template/app/ui-kit/src/hooks/useForm.test.tsx +192 -0
  248. package/stack-template/app/ui-kit/src/hooks/useForm.ts +119 -0
  249. package/stack-template/app/ui-kit/src/hooks/useFullscreen.test.tsx +71 -0
  250. package/stack-template/app/ui-kit/src/hooks/useFullscreen.ts +68 -0
  251. package/stack-template/app/ui-kit/src/hooks/useHover.test.tsx +30 -0
  252. package/stack-template/app/ui-kit/src/hooks/useHover.ts +23 -0
  253. package/stack-template/app/ui-kit/src/hooks/useInView.test.tsx +70 -0
  254. package/stack-template/app/ui-kit/src/hooks/useInView.ts +55 -0
  255. package/stack-template/app/ui-kit/src/hooks/useInfiniteScroll.test.tsx +72 -0
  256. package/stack-template/app/ui-kit/src/hooks/useInfiniteScroll.ts +45 -0
  257. package/stack-template/app/ui-kit/src/hooks/useIsFirstRender.test.ts +30 -0
  258. package/stack-template/app/ui-kit/src/hooks/useIsFirstRender.ts +14 -0
  259. package/stack-template/app/ui-kit/src/hooks/useKeyPress.test.ts +45 -0
  260. package/stack-template/app/ui-kit/src/hooks/useKeyPress.ts +31 -0
  261. package/stack-template/app/ui-kit/src/hooks/useLocalStorage.test.ts +102 -0
  262. package/stack-template/app/ui-kit/src/hooks/useLocalStorage.ts +34 -0
  263. package/stack-template/app/ui-kit/src/hooks/useLockBodyScroll.test.ts +42 -0
  264. package/stack-template/app/ui-kit/src/hooks/useLockBodyScroll.ts +13 -0
  265. package/stack-template/app/ui-kit/src/hooks/useMeasure.test.tsx +57 -0
  266. package/stack-template/app/ui-kit/src/hooks/useMeasure.ts +32 -0
  267. package/stack-template/app/ui-kit/src/hooks/useMediaQuery.test.ts +73 -0
  268. package/stack-template/app/ui-kit/src/hooks/useMediaQuery.ts +20 -0
  269. package/stack-template/app/ui-kit/src/hooks/useMountEffect.test.ts +22 -0
  270. package/stack-template/app/ui-kit/src/hooks/useMountEffect.ts +10 -0
  271. package/stack-template/app/ui-kit/src/hooks/useOnlineStatus.test.ts +36 -0
  272. package/stack-template/app/ui-kit/src/hooks/useOnlineStatus.ts +21 -0
  273. package/stack-template/app/ui-kit/src/hooks/usePrevious.test.ts +35 -0
  274. package/stack-template/app/ui-kit/src/hooks/usePrevious.ts +13 -0
  275. package/stack-template/app/ui-kit/src/hooks/useScrollProgress.test.ts +59 -0
  276. package/stack-template/app/ui-kit/src/hooks/useScrollProgress.ts +31 -0
  277. package/stack-template/app/ui-kit/src/hooks/useSessionStorage.test.ts +80 -0
  278. package/stack-template/app/ui-kit/src/hooks/useSessionStorage.ts +34 -0
  279. package/stack-template/app/ui-kit/src/hooks/useThrottle.test.ts +61 -0
  280. package/stack-template/app/ui-kit/src/hooks/useThrottle.ts +30 -0
  281. package/stack-template/app/ui-kit/src/hooks/useTimeout.test.ts +95 -0
  282. package/stack-template/app/ui-kit/src/hooks/useTimeout.ts +38 -0
  283. package/stack-template/app/ui-kit/src/hooks/useTitle.test.ts +34 -0
  284. package/stack-template/app/ui-kit/src/hooks/useTitle.ts +24 -0
  285. package/stack-template/app/ui-kit/src/hooks/useToggle.test.ts +46 -0
  286. package/stack-template/app/ui-kit/src/hooks/useToggle.ts +16 -0
  287. package/stack-template/app/ui-kit/src/hooks/useUpdateEffect.test.ts +25 -0
  288. package/stack-template/app/ui-kit/src/hooks/useUpdateEffect.ts +18 -0
  289. package/stack-template/app/ui-kit/src/hooks/useWindowSize.test.ts +33 -0
  290. package/stack-template/app/ui-kit/src/hooks/useWindowSize.ts +23 -0
  291. package/stack-template/app/ui-kit/src/index.ts +218 -0
  292. package/stack-template/app/ui-kit/src/intro-showcase.stories.tsx +272 -0
  293. package/stack-template/app/ui-kit/src/intro.docs.mdx +247 -0
  294. package/stack-template/app/ui-kit/src/motion/motion-showcase.stories.tsx +234 -0
  295. package/stack-template/app/ui-kit/src/motion/motion.md +38 -0
  296. package/stack-template/app/ui-kit/src/provider/index.tsx +77 -0
  297. package/stack-template/app/ui-kit/src/provider/provider.stories.tsx +87 -0
  298. package/stack-template/app/ui-kit/src/provider/provider.test.tsx +121 -0
  299. package/stack-template/app/ui-kit/src/styles/base.css +171 -0
  300. package/stack-template/app/ui-kit/src/styles/dark-theme.test.ts +21 -0
  301. package/stack-template/app/ui-kit/src/styles/tailwind.css +220 -0
  302. package/stack-template/app/ui-kit/src/styles/themes/dark.css +71 -0
  303. package/stack-template/app/ui-kit/src/styles/tokens.css +133 -0
  304. package/stack-template/app/ui-kit/src/styles/ui-kit.css +10 -0
  305. package/stack-template/app/ui-kit/src/templates/auth.stories.tsx +425 -0
  306. package/stack-template/app/ui-kit/src/templates/crud.stories.tsx +416 -0
  307. package/stack-template/app/ui-kit/src/templates/dashboard.stories.tsx +504 -0
  308. package/stack-template/app/ui-kit/src/templates/experience/chapters/ch1-deep-focus.tsx +96 -0
  309. package/stack-template/app/ui-kit/src/templates/experience/chapters/ch2-living-docs.tsx +134 -0
  310. package/stack-template/app/ui-kit/src/templates/experience/chapters/ch3-team-velocity.tsx +148 -0
  311. package/stack-template/app/ui-kit/src/templates/experience/chapters/ch4-ship-your-app.tsx +100 -0
  312. package/stack-template/app/ui-kit/src/templates/experience/chapters/ch5-future.tsx +67 -0
  313. package/stack-template/app/ui-kit/src/templates/experience/chapters/hero.tsx +434 -0
  314. package/stack-template/app/ui-kit/src/templates/experience/chapters/nav-footer.tsx +85 -0
  315. package/stack-template/app/ui-kit/src/templates/experience/hero.stories.tsx +27 -0
  316. package/stack-template/app/ui-kit/src/templates/experience/hero.test.tsx +108 -0
  317. package/stack-template/app/ui-kit/src/templates/experience/index.stories.tsx +51 -0
  318. package/stack-template/app/ui-kit/src/templates/experience/lib/ChapterShell.tsx +84 -0
  319. package/stack-template/app/ui-kit/src/templates/experience/lib/SideNav.tsx +173 -0
  320. package/stack-template/app/ui-kit/src/templates/experience/lib/chapter-shell.test.tsx +266 -0
  321. package/stack-template/app/ui-kit/src/templates/experience/lib/scroll-mood.tsx +143 -0
  322. package/stack-template/app/ui-kit/src/templates/experience/lib/starfield.test.tsx +31 -0
  323. package/stack-template/app/ui-kit/src/templates/experience/lib/starfield.tsx +190 -0
  324. package/stack-template/app/ui-kit/src/templates/experience/mood-engine.stories.tsx +114 -0
  325. package/stack-template/app/ui-kit/src/templates/experience/shell-nav.stories.tsx +113 -0
  326. package/stack-template/app/ui-kit/src/templates/kanban-project.stories.tsx +352 -0
  327. package/stack-template/app/ui-kit/src/templates/landing.stories.tsx +380 -0
  328. package/stack-template/app/ui-kit/src/templates/parallax-landing.stories.tsx +633 -0
  329. package/stack-template/app/ui-kit/src/templates/settings.stories.tsx +474 -0
  330. package/stack-template/app/ui-kit/src/ui/accordion/accordion.stories.tsx +91 -0
  331. package/stack-template/app/ui-kit/src/ui/accordion/accordion.test.tsx +99 -0
  332. package/stack-template/app/ui-kit/src/ui/accordion/index.tsx +107 -0
  333. package/stack-template/app/ui-kit/src/ui/affix/affix.stories.tsx +104 -0
  334. package/stack-template/app/ui-kit/src/ui/affix/affix.test.tsx +138 -0
  335. package/stack-template/app/ui-kit/src/ui/affix/index.tsx +89 -0
  336. package/stack-template/app/ui-kit/src/ui/alert/alert.stories.tsx +149 -0
  337. package/stack-template/app/ui-kit/src/ui/alert/alert.test.tsx +85 -0
  338. package/stack-template/app/ui-kit/src/ui/alert/index.tsx +118 -0
  339. package/stack-template/app/ui-kit/src/ui/anchor/anchor.stories.tsx +96 -0
  340. package/stack-template/app/ui-kit/src/ui/anchor/anchor.test.tsx +191 -0
  341. package/stack-template/app/ui-kit/src/ui/anchor/index.tsx +144 -0
  342. package/stack-template/app/ui-kit/src/ui/auto-complete/auto-complete.stories.tsx +67 -0
  343. package/stack-template/app/ui-kit/src/ui/auto-complete/auto-complete.test.tsx +94 -0
  344. package/stack-template/app/ui-kit/src/ui/auto-complete/index.tsx +54 -0
  345. package/stack-template/app/ui-kit/src/ui/avatar/avatar.stories.tsx +132 -0
  346. package/stack-template/app/ui-kit/src/ui/avatar/avatar.test.tsx +65 -0
  347. package/stack-template/app/ui-kit/src/ui/avatar/index.tsx +48 -0
  348. package/stack-template/app/ui-kit/src/ui/avatar-group/avatar-group.stories.tsx +113 -0
  349. package/stack-template/app/ui-kit/src/ui/avatar-group/avatar-group.test.tsx +71 -0
  350. package/stack-template/app/ui-kit/src/ui/avatar-group/index.tsx +66 -0
  351. package/stack-template/app/ui-kit/src/ui/backdrop/backdrop.stories.tsx +66 -0
  352. package/stack-template/app/ui-kit/src/ui/backdrop/backdrop.test.tsx +37 -0
  353. package/stack-template/app/ui-kit/src/ui/backdrop/index.tsx +29 -0
  354. package/stack-template/app/ui-kit/src/ui/badge/badge.stories.tsx +105 -0
  355. package/stack-template/app/ui-kit/src/ui/badge/badge.test.tsx +115 -0
  356. package/stack-template/app/ui-kit/src/ui/badge/index.tsx +154 -0
  357. package/stack-template/app/ui-kit/src/ui/blockui/blockui.stories.tsx +34 -0
  358. package/stack-template/app/ui-kit/src/ui/blockui/blockui.test.tsx +29 -0
  359. package/stack-template/app/ui-kit/src/ui/blockui/index.tsx +25 -0
  360. package/stack-template/app/ui-kit/src/ui/bottom-navigation/bottom-navigation.stories.tsx +70 -0
  361. package/stack-template/app/ui-kit/src/ui/bottom-navigation/bottom-navigation.test.tsx +68 -0
  362. package/stack-template/app/ui-kit/src/ui/bottom-navigation/index.tsx +82 -0
  363. package/stack-template/app/ui-kit/src/ui/branch-fill.test.tsx +245 -0
  364. package/stack-template/app/ui-kit/src/ui/breadcrumb/breadcrumb.stories.tsx +103 -0
  365. package/stack-template/app/ui-kit/src/ui/breadcrumb/breadcrumb.test.tsx +49 -0
  366. package/stack-template/app/ui-kit/src/ui/breadcrumb/index.tsx +37 -0
  367. package/stack-template/app/ui-kit/src/ui/busy-box/busy-box.stories.tsx +30 -0
  368. package/stack-template/app/ui-kit/src/ui/busy-box/busy-box.test.tsx +29 -0
  369. package/stack-template/app/ui-kit/src/ui/busy-box/index.tsx +25 -0
  370. package/stack-template/app/ui-kit/src/ui/button/button.stories.tsx +121 -0
  371. package/stack-template/app/ui-kit/src/ui/button/button.test.tsx +207 -0
  372. package/stack-template/app/ui-kit/src/ui/button/index.tsx +128 -0
  373. package/stack-template/app/ui-kit/src/ui/calendar/calendar.stories.tsx +100 -0
  374. package/stack-template/app/ui-kit/src/ui/calendar/calendar.test.tsx +92 -0
  375. package/stack-template/app/ui-kit/src/ui/calendar/index.tsx +163 -0
  376. package/stack-template/app/ui-kit/src/ui/card/card.stories.tsx +203 -0
  377. package/stack-template/app/ui-kit/src/ui/card/card.test.tsx +135 -0
  378. package/stack-template/app/ui-kit/src/ui/card/index.tsx +84 -0
  379. package/stack-template/app/ui-kit/src/ui/carousel/carousel.stories.tsx +533 -0
  380. package/stack-template/app/ui-kit/src/ui/carousel/carousel.test.tsx +148 -0
  381. package/stack-template/app/ui-kit/src/ui/carousel/index.tsx +195 -0
  382. package/stack-template/app/ui-kit/src/ui/cascade-select/cascade-select.stories.tsx +126 -0
  383. package/stack-template/app/ui-kit/src/ui/cascade-select/cascade-select.test.tsx +132 -0
  384. package/stack-template/app/ui-kit/src/ui/cascade-select/index.tsx +87 -0
  385. package/stack-template/app/ui-kit/src/ui/chart/chart-theme.test.tsx +79 -0
  386. package/stack-template/app/ui-kit/src/ui/chart/chart.stories.tsx +278 -0
  387. package/stack-template/app/ui-kit/src/ui/chart/chart.test.tsx +103 -0
  388. package/stack-template/app/ui-kit/src/ui/chart/index.tsx +172 -0
  389. package/stack-template/app/ui-kit/src/ui/checkbox/checkbox.stories.tsx +67 -0
  390. package/stack-template/app/ui-kit/src/ui/checkbox/checkbox.test.tsx +99 -0
  391. package/stack-template/app/ui-kit/src/ui/checkbox/index.tsx +52 -0
  392. package/stack-template/app/ui-kit/src/ui/chips/chips.stories.tsx +53 -0
  393. package/stack-template/app/ui-kit/src/ui/chips/chips.test.tsx +86 -0
  394. package/stack-template/app/ui-kit/src/ui/chips/index.tsx +44 -0
  395. package/stack-template/app/ui-kit/src/ui/collapse/collapse.stories.tsx +93 -0
  396. package/stack-template/app/ui-kit/src/ui/collapse/collapse.test.tsx +128 -0
  397. package/stack-template/app/ui-kit/src/ui/collapse/index.tsx +75 -0
  398. package/stack-template/app/ui-kit/src/ui/color-picker/color-picker.stories.tsx +48 -0
  399. package/stack-template/app/ui-kit/src/ui/color-picker/color-picker.test.tsx +68 -0
  400. package/stack-template/app/ui-kit/src/ui/color-picker/index.tsx +38 -0
  401. package/stack-template/app/ui-kit/src/ui/context-menu/context-menu.stories.tsx +73 -0
  402. package/stack-template/app/ui-kit/src/ui/context-menu/context-menu.test.tsx +93 -0
  403. package/stack-template/app/ui-kit/src/ui/context-menu/index.tsx +79 -0
  404. package/stack-template/app/ui-kit/src/ui/count-up/count-up.stories.tsx +80 -0
  405. package/stack-template/app/ui-kit/src/ui/count-up/count-up.test.tsx +121 -0
  406. package/stack-template/app/ui-kit/src/ui/count-up/index.tsx +70 -0
  407. package/stack-template/app/ui-kit/src/ui/date-picker/date-picker.stories.tsx +154 -0
  408. package/stack-template/app/ui-kit/src/ui/date-picker/date-picker.test.tsx +221 -0
  409. package/stack-template/app/ui-kit/src/ui/date-picker/index.tsx +287 -0
  410. package/stack-template/app/ui-kit/src/ui/date-picker/locale-constants.ts +90 -0
  411. package/stack-template/app/ui-kit/src/ui/date-picker/locale.tsx +15 -0
  412. package/stack-template/app/ui-kit/src/ui/deferred-content/deferred-content.stories.tsx +62 -0
  413. package/stack-template/app/ui-kit/src/ui/deferred-content/deferred-content.test.tsx +94 -0
  414. package/stack-template/app/ui-kit/src/ui/deferred-content/index.tsx +61 -0
  415. package/stack-template/app/ui-kit/src/ui/descriptions/descriptions.stories.tsx +114 -0
  416. package/stack-template/app/ui-kit/src/ui/descriptions/descriptions.test.tsx +75 -0
  417. package/stack-template/app/ui-kit/src/ui/descriptions/index.tsx +48 -0
  418. package/stack-template/app/ui-kit/src/ui/divider/divider.stories.tsx +66 -0
  419. package/stack-template/app/ui-kit/src/ui/divider/divider.test.tsx +83 -0
  420. package/stack-template/app/ui-kit/src/ui/divider/index.tsx +65 -0
  421. package/stack-template/app/ui-kit/src/ui/drag-drop/drag-drop.stories.tsx +145 -0
  422. package/stack-template/app/ui-kit/src/ui/drag-drop/drag-drop.test.tsx +352 -0
  423. package/stack-template/app/ui-kit/src/ui/drag-drop/index.tsx +456 -0
  424. package/stack-template/app/ui-kit/src/ui/drawer/drawer.stories.tsx +108 -0
  425. package/stack-template/app/ui-kit/src/ui/drawer/drawer.test.tsx +132 -0
  426. package/stack-template/app/ui-kit/src/ui/drawer/index.tsx +113 -0
  427. package/stack-template/app/ui-kit/src/ui/dropdown/dropdown.stories.tsx +61 -0
  428. package/stack-template/app/ui-kit/src/ui/dropdown/dropdown.test.tsx +101 -0
  429. package/stack-template/app/ui-kit/src/ui/dropdown/index.tsx +162 -0
  430. package/stack-template/app/ui-kit/src/ui/editor/EditorInner.tsx +93 -0
  431. package/stack-template/app/ui-kit/src/ui/editor/editor.stories.tsx +37 -0
  432. package/stack-template/app/ui-kit/src/ui/editor/editor.test.tsx +84 -0
  433. package/stack-template/app/ui-kit/src/ui/editor/index.tsx +58 -0
  434. package/stack-template/app/ui-kit/src/ui/empty-state/empty-state.stories.tsx +24 -0
  435. package/stack-template/app/ui-kit/src/ui/empty-state/empty-state.test.tsx +48 -0
  436. package/stack-template/app/ui-kit/src/ui/empty-state/index.tsx +22 -0
  437. package/stack-template/app/ui-kit/src/ui/experience3d/experience3d.stories.tsx +66 -0
  438. package/stack-template/app/ui-kit/src/ui/experience3d/experience3d.test.tsx +50 -0
  439. package/stack-template/app/ui-kit/src/ui/experience3d/index.tsx +457 -0
  440. package/stack-template/app/ui-kit/src/ui/field/field-array.stories.tsx +214 -0
  441. package/stack-template/app/ui-kit/src/ui/field/field.stories.tsx +158 -0
  442. package/stack-template/app/ui-kit/src/ui/field/field.test.tsx +68 -0
  443. package/stack-template/app/ui-kit/src/ui/field/index.tsx +48 -0
  444. package/stack-template/app/ui-kit/src/ui/fieldset/fieldset.stories.tsx +49 -0
  445. package/stack-template/app/ui-kit/src/ui/fieldset/fieldset.test.tsx +88 -0
  446. package/stack-template/app/ui-kit/src/ui/fieldset/index.tsx +62 -0
  447. package/stack-template/app/ui-kit/src/ui/flex/flex.stories.tsx +96 -0
  448. package/stack-template/app/ui-kit/src/ui/flex/flex.test.tsx +79 -0
  449. package/stack-template/app/ui-kit/src/ui/flex/index.tsx +45 -0
  450. package/stack-template/app/ui-kit/src/ui/float-button/float-button.stories.tsx +82 -0
  451. package/stack-template/app/ui-kit/src/ui/float-button/float-button.test.tsx +107 -0
  452. package/stack-template/app/ui-kit/src/ui/float-button/index.tsx +124 -0
  453. package/stack-template/app/ui-kit/src/ui/galleria/galleria.stories.tsx +78 -0
  454. package/stack-template/app/ui-kit/src/ui/galleria/galleria.test.tsx +67 -0
  455. package/stack-template/app/ui-kit/src/ui/galleria/index.tsx +47 -0
  456. package/stack-template/app/ui-kit/src/ui/grid/grid.stories.tsx +179 -0
  457. package/stack-template/app/ui-kit/src/ui/grid/grid.test.tsx +286 -0
  458. package/stack-template/app/ui-kit/src/ui/grid/index.tsx +104 -0
  459. package/stack-template/app/ui-kit/src/ui/icons/icons.stories.tsx +52 -0
  460. package/stack-template/app/ui-kit/src/ui/icons/icons.test.tsx +60 -0
  461. package/stack-template/app/ui-kit/src/ui/icons/index.tsx +294 -0
  462. package/stack-template/app/ui-kit/src/ui/image/image.stories.tsx +231 -0
  463. package/stack-template/app/ui-kit/src/ui/image/image.test.tsx +79 -0
  464. package/stack-template/app/ui-kit/src/ui/image/index.tsx +76 -0
  465. package/stack-template/app/ui-kit/src/ui/inplace/index.tsx +33 -0
  466. package/stack-template/app/ui-kit/src/ui/inplace/inplace.stories.tsx +75 -0
  467. package/stack-template/app/ui-kit/src/ui/inplace/inplace.test.tsx +58 -0
  468. package/stack-template/app/ui-kit/src/ui/input/index.tsx +133 -0
  469. package/stack-template/app/ui-kit/src/ui/input/input-search.stories.tsx +112 -0
  470. package/stack-template/app/ui-kit/src/ui/input/input-search.test.tsx +116 -0
  471. package/stack-template/app/ui-kit/src/ui/input/input-search.tsx +116 -0
  472. package/stack-template/app/ui-kit/src/ui/input/input.stories.tsx +176 -0
  473. package/stack-template/app/ui-kit/src/ui/input/input.test.tsx +106 -0
  474. package/stack-template/app/ui-kit/src/ui/input-mask/index.tsx +39 -0
  475. package/stack-template/app/ui-kit/src/ui/input-mask/input-mask.stories.tsx +46 -0
  476. package/stack-template/app/ui-kit/src/ui/input-mask/input-mask.test.tsx +57 -0
  477. package/stack-template/app/ui-kit/src/ui/input-number/index.tsx +125 -0
  478. package/stack-template/app/ui-kit/src/ui/input-number/input-number.stories.tsx +190 -0
  479. package/stack-template/app/ui-kit/src/ui/input-number/input-number.test.tsx +157 -0
  480. package/stack-template/app/ui-kit/src/ui/input-otp/index.tsx +57 -0
  481. package/stack-template/app/ui-kit/src/ui/input-otp/input-otp.stories.tsx +90 -0
  482. package/stack-template/app/ui-kit/src/ui/input-otp/input-otp.test.tsx +66 -0
  483. package/stack-template/app/ui-kit/src/ui/kanban/index.tsx +277 -0
  484. package/stack-template/app/ui-kit/src/ui/kanban/kanban.stories.tsx +219 -0
  485. package/stack-template/app/ui-kit/src/ui/kanban/kanban.test.tsx +234 -0
  486. package/stack-template/app/ui-kit/src/ui/knob/index.tsx +58 -0
  487. package/stack-template/app/ui-kit/src/ui/knob/knob.stories.tsx +49 -0
  488. package/stack-template/app/ui-kit/src/ui/knob/knob.test.tsx +42 -0
  489. package/stack-template/app/ui-kit/src/ui/layout/index.tsx +145 -0
  490. package/stack-template/app/ui-kit/src/ui/layout/layout.stories.tsx +158 -0
  491. package/stack-template/app/ui-kit/src/ui/layout/layout.test.tsx +154 -0
  492. package/stack-template/app/ui-kit/src/ui/link/index.tsx +61 -0
  493. package/stack-template/app/ui-kit/src/ui/link/link.stories.tsx +68 -0
  494. package/stack-template/app/ui-kit/src/ui/link/link.test.tsx +69 -0
  495. package/stack-template/app/ui-kit/src/ui/list/index.tsx +135 -0
  496. package/stack-template/app/ui-kit/src/ui/list/list.stories.tsx +194 -0
  497. package/stack-template/app/ui-kit/src/ui/list/list.test.tsx +164 -0
  498. package/stack-template/app/ui-kit/src/ui/listbox/index.tsx +27 -0
  499. package/stack-template/app/ui-kit/src/ui/listbox/listbox.stories.tsx +57 -0
  500. package/stack-template/app/ui-kit/src/ui/listbox/listbox.test.tsx +49 -0
  501. package/stack-template/app/ui-kit/src/ui/mega-menu/index.tsx +142 -0
  502. package/stack-template/app/ui-kit/src/ui/mega-menu/mega-menu.stories.tsx +137 -0
  503. package/stack-template/app/ui-kit/src/ui/mega-menu/mega-menu.test.tsx +130 -0
  504. package/stack-template/app/ui-kit/src/ui/mention/index.tsx +76 -0
  505. package/stack-template/app/ui-kit/src/ui/mention/mention.stories.tsx +44 -0
  506. package/stack-template/app/ui-kit/src/ui/mention/mention.test.tsx +87 -0
  507. package/stack-template/app/ui-kit/src/ui/menu/index.tsx +256 -0
  508. package/stack-template/app/ui-kit/src/ui/menu/menu.stories.tsx +124 -0
  509. package/stack-template/app/ui-kit/src/ui/menu/menu.test.tsx +251 -0
  510. package/stack-template/app/ui-kit/src/ui/menubar/index.tsx +42 -0
  511. package/stack-template/app/ui-kit/src/ui/menubar/menubar.stories.tsx +66 -0
  512. package/stack-template/app/ui-kit/src/ui/menubar/menubar.test.tsx +65 -0
  513. package/stack-template/app/ui-kit/src/ui/message/index.tsx +123 -0
  514. package/stack-template/app/ui-kit/src/ui/message/message.stories.tsx +104 -0
  515. package/stack-template/app/ui-kit/src/ui/message/message.test.tsx +109 -0
  516. package/stack-template/app/ui-kit/src/ui/modal/index.tsx +142 -0
  517. package/stack-template/app/ui-kit/src/ui/modal/modal.stories.tsx +128 -0
  518. package/stack-template/app/ui-kit/src/ui/modal/modal.test.tsx +166 -0
  519. package/stack-template/app/ui-kit/src/ui/notification/index.tsx +142 -0
  520. package/stack-template/app/ui-kit/src/ui/notification/notification.stories.tsx +155 -0
  521. package/stack-template/app/ui-kit/src/ui/notification/notification.test.tsx +83 -0
  522. package/stack-template/app/ui-kit/src/ui/order-list/index.tsx +38 -0
  523. package/stack-template/app/ui-kit/src/ui/order-list/order-list.stories.tsx +38 -0
  524. package/stack-template/app/ui-kit/src/ui/order-list/order-list.test.tsx +54 -0
  525. package/stack-template/app/ui-kit/src/ui/org-chart/index.tsx +138 -0
  526. package/stack-template/app/ui-kit/src/ui/org-chart/org-chart.stories.tsx +76 -0
  527. package/stack-template/app/ui-kit/src/ui/org-chart/org-chart.test.tsx +98 -0
  528. package/stack-template/app/ui-kit/src/ui/pagination/index.tsx +102 -0
  529. package/stack-template/app/ui-kit/src/ui/pagination/pagination.stories.tsx +103 -0
  530. package/stack-template/app/ui-kit/src/ui/pagination/pagination.test.tsx +112 -0
  531. package/stack-template/app/ui-kit/src/ui/panel-menu/index.tsx +59 -0
  532. package/stack-template/app/ui-kit/src/ui/panel-menu/panel-menu.stories.tsx +96 -0
  533. package/stack-template/app/ui-kit/src/ui/panel-menu/panel-menu.test.tsx +64 -0
  534. package/stack-template/app/ui-kit/src/ui/parallax/index.tsx +62 -0
  535. package/stack-template/app/ui-kit/src/ui/parallax/parallax.stories.tsx +115 -0
  536. package/stack-template/app/ui-kit/src/ui/parallax/parallax.test.tsx +148 -0
  537. package/stack-template/app/ui-kit/src/ui/password/index.tsx +67 -0
  538. package/stack-template/app/ui-kit/src/ui/password/password.stories.tsx +43 -0
  539. package/stack-template/app/ui-kit/src/ui/password/password.test.tsx +45 -0
  540. package/stack-template/app/ui-kit/src/ui/pick-list/index.tsx +103 -0
  541. package/stack-template/app/ui-kit/src/ui/pick-list/pick-list.stories.tsx +59 -0
  542. package/stack-template/app/ui-kit/src/ui/pick-list/pick-list.test.tsx +94 -0
  543. package/stack-template/app/ui-kit/src/ui/popconfirm/index.tsx +102 -0
  544. package/stack-template/app/ui-kit/src/ui/popconfirm/popconfirm.stories.tsx +73 -0
  545. package/stack-template/app/ui-kit/src/ui/popconfirm/popconfirm.test.tsx +127 -0
  546. package/stack-template/app/ui-kit/src/ui/popover/index.tsx +136 -0
  547. package/stack-template/app/ui-kit/src/ui/popover/popover.stories.tsx +114 -0
  548. package/stack-template/app/ui-kit/src/ui/popover/popover.test.tsx +172 -0
  549. package/stack-template/app/ui-kit/src/ui/progress/index.tsx +46 -0
  550. package/stack-template/app/ui-kit/src/ui/progress/progress.stories.tsx +57 -0
  551. package/stack-template/app/ui-kit/src/ui/progress/progress.test.tsx +90 -0
  552. package/stack-template/app/ui-kit/src/ui/qr-code/index.tsx +88 -0
  553. package/stack-template/app/ui-kit/src/ui/qr-code/qr-code.stories.tsx +109 -0
  554. package/stack-template/app/ui-kit/src/ui/qr-code/qr-code.test.tsx +155 -0
  555. package/stack-template/app/ui-kit/src/ui/qr-code/qrcode-core.ts +56 -0
  556. package/stack-template/app/ui-kit/src/ui/qr-code/qrcode-generator.vendor.ts +2300 -0
  557. package/stack-template/app/ui-kit/src/ui/radio/index.tsx +62 -0
  558. package/stack-template/app/ui-kit/src/ui/radio/radio.stories.tsx +94 -0
  559. package/stack-template/app/ui-kit/src/ui/radio/radio.test.tsx +83 -0
  560. package/stack-template/app/ui-kit/src/ui/rating/index.tsx +36 -0
  561. package/stack-template/app/ui-kit/src/ui/rating/rating.stories.tsx +42 -0
  562. package/stack-template/app/ui-kit/src/ui/rating/rating.test.tsx +45 -0
  563. package/stack-template/app/ui-kit/src/ui/result/index.tsx +72 -0
  564. package/stack-template/app/ui-kit/src/ui/result/result.stories.tsx +82 -0
  565. package/stack-template/app/ui-kit/src/ui/result/result.test.tsx +57 -0
  566. package/stack-template/app/ui-kit/src/ui/reveal/index.tsx +83 -0
  567. package/stack-template/app/ui-kit/src/ui/reveal/reveal.stories.tsx +91 -0
  568. package/stack-template/app/ui-kit/src/ui/reveal/reveal.test.tsx +148 -0
  569. package/stack-template/app/ui-kit/src/ui/schedule/index.tsx +104 -0
  570. package/stack-template/app/ui-kit/src/ui/schedule/schedule.fc.test.tsx +95 -0
  571. package/stack-template/app/ui-kit/src/ui/schedule/schedule.stories.tsx +61 -0
  572. package/stack-template/app/ui-kit/src/ui/schedule/schedule.test.tsx +41 -0
  573. package/stack-template/app/ui-kit/src/ui/scroll-page/index.tsx +46 -0
  574. package/stack-template/app/ui-kit/src/ui/scroll-page/scroll-page.stories.tsx +92 -0
  575. package/stack-template/app/ui-kit/src/ui/scroll-page/scroll-page.test.tsx +51 -0
  576. package/stack-template/app/ui-kit/src/ui/scroll-panel/index.tsx +16 -0
  577. package/stack-template/app/ui-kit/src/ui/scroll-panel/scroll-panel.stories.tsx +37 -0
  578. package/stack-template/app/ui-kit/src/ui/scroll-panel/scroll-panel.test.tsx +41 -0
  579. package/stack-template/app/ui-kit/src/ui/scrolltop/index.tsx +27 -0
  580. package/stack-template/app/ui-kit/src/ui/scrolltop/scrolltop.stories.tsx +41 -0
  581. package/stack-template/app/ui-kit/src/ui/scrolltop/scrolltop.test.tsx +37 -0
  582. package/stack-template/app/ui-kit/src/ui/segmented/index.tsx +47 -0
  583. package/stack-template/app/ui-kit/src/ui/segmented/segmented.stories.tsx +126 -0
  584. package/stack-template/app/ui-kit/src/ui/segmented/segmented.test.tsx +77 -0
  585. package/stack-template/app/ui-kit/src/ui/select/index.tsx +302 -0
  586. package/stack-template/app/ui-kit/src/ui/select/select.stories.tsx +268 -0
  587. package/stack-template/app/ui-kit/src/ui/select/select.test.tsx +226 -0
  588. package/stack-template/app/ui-kit/src/ui/sidebar/index.tsx +83 -0
  589. package/stack-template/app/ui-kit/src/ui/sidebar/sidebar.stories.tsx +71 -0
  590. package/stack-template/app/ui-kit/src/ui/sidebar/sidebar.test.tsx +101 -0
  591. package/stack-template/app/ui-kit/src/ui/skeleton/index.tsx +55 -0
  592. package/stack-template/app/ui-kit/src/ui/skeleton/skeleton.stories.tsx +110 -0
  593. package/stack-template/app/ui-kit/src/ui/skeleton/skeleton.test.tsx +74 -0
  594. package/stack-template/app/ui-kit/src/ui/slide-menu/index.tsx +103 -0
  595. package/stack-template/app/ui-kit/src/ui/slide-menu/slide-menu.stories.tsx +92 -0
  596. package/stack-template/app/ui-kit/src/ui/slide-menu/slide-menu.test.tsx +120 -0
  597. package/stack-template/app/ui-kit/src/ui/slider/index.tsx +90 -0
  598. package/stack-template/app/ui-kit/src/ui/slider/slider.stories.tsx +60 -0
  599. package/stack-template/app/ui-kit/src/ui/slider/slider.test.tsx +66 -0
  600. package/stack-template/app/ui-kit/src/ui/space/index.tsx +32 -0
  601. package/stack-template/app/ui-kit/src/ui/space/space.stories.tsx +66 -0
  602. package/stack-template/app/ui-kit/src/ui/space/space.test.tsx +56 -0
  603. package/stack-template/app/ui-kit/src/ui/speed-dial/index.tsx +32 -0
  604. package/stack-template/app/ui-kit/src/ui/speed-dial/speed-dial.stories.tsx +34 -0
  605. package/stack-template/app/ui-kit/src/ui/speed-dial/speed-dial.test.tsx +34 -0
  606. package/stack-template/app/ui-kit/src/ui/spinner/index.tsx +74 -0
  607. package/stack-template/app/ui-kit/src/ui/spinner/spinner.stories.tsx +64 -0
  608. package/stack-template/app/ui-kit/src/ui/spinner/spinner.test.tsx +66 -0
  609. package/stack-template/app/ui-kit/src/ui/split-button/index.tsx +155 -0
  610. package/stack-template/app/ui-kit/src/ui/split-button/split-button.stories.tsx +137 -0
  611. package/stack-template/app/ui-kit/src/ui/split-button/split-button.test.tsx +152 -0
  612. package/stack-template/app/ui-kit/src/ui/splitter/index.tsx +49 -0
  613. package/stack-template/app/ui-kit/src/ui/splitter/splitter.stories.tsx +24 -0
  614. package/stack-template/app/ui-kit/src/ui/splitter/splitter.test.tsx +21 -0
  615. package/stack-template/app/ui-kit/src/ui/statistic/index.tsx +28 -0
  616. package/stack-template/app/ui-kit/src/ui/statistic/statistic.stories.tsx +112 -0
  617. package/stack-template/app/ui-kit/src/ui/statistic/statistic.test.tsx +39 -0
  618. package/stack-template/app/ui-kit/src/ui/steps/index.tsx +50 -0
  619. package/stack-template/app/ui-kit/src/ui/steps/steps.stories.tsx +57 -0
  620. package/stack-template/app/ui-kit/src/ui/steps/steps.test.tsx +55 -0
  621. package/stack-template/app/ui-kit/src/ui/switch/index.tsx +87 -0
  622. package/stack-template/app/ui-kit/src/ui/switch/switch.stories.tsx +123 -0
  623. package/stack-template/app/ui-kit/src/ui/switch/switch.test.tsx +98 -0
  624. package/stack-template/app/ui-kit/src/ui/tab-menu/index.tsx +33 -0
  625. package/stack-template/app/ui-kit/src/ui/tab-menu/tab-menu.stories.tsx +49 -0
  626. package/stack-template/app/ui-kit/src/ui/tab-menu/tab-menu.test.tsx +50 -0
  627. package/stack-template/app/ui-kit/src/ui/table/index.tsx +690 -0
  628. package/stack-template/app/ui-kit/src/ui/table/table-advanced.stories.tsx +91 -0
  629. package/stack-template/app/ui-kit/src/ui/table/table-advanced.test.tsx +188 -0
  630. package/stack-template/app/ui-kit/src/ui/table/table-expandable.stories.tsx +91 -0
  631. package/stack-template/app/ui-kit/src/ui/table/table-expandable.test.tsx +143 -0
  632. package/stack-template/app/ui-kit/src/ui/table/table.stories.tsx +342 -0
  633. package/stack-template/app/ui-kit/src/ui/table/table.test.tsx +465 -0
  634. package/stack-template/app/ui-kit/src/ui/table/types.ts +173 -0
  635. package/stack-template/app/ui-kit/src/ui/tabs/index.tsx +118 -0
  636. package/stack-template/app/ui-kit/src/ui/tabs/tabs.stories.tsx +113 -0
  637. package/stack-template/app/ui-kit/src/ui/tabs/tabs.test.tsx +77 -0
  638. package/stack-template/app/ui-kit/src/ui/tag/index.tsx +68 -0
  639. package/stack-template/app/ui-kit/src/ui/tag/tag.stories.tsx +88 -0
  640. package/stack-template/app/ui-kit/src/ui/tag/tag.test.tsx +126 -0
  641. package/stack-template/app/ui-kit/src/ui/team/debt-board/debt-board.stories.tsx +52 -0
  642. package/stack-template/app/ui-kit/src/ui/team/debt-board/debt-board.test.tsx +72 -0
  643. package/stack-template/app/ui-kit/src/ui/team/debt-board/index.tsx +125 -0
  644. package/stack-template/app/ui-kit/src/ui/team/inbox-thread/inbox-thread.stories.tsx +70 -0
  645. package/stack-template/app/ui-kit/src/ui/team/inbox-thread/inbox-thread.test.tsx +56 -0
  646. package/stack-template/app/ui-kit/src/ui/team/inbox-thread/index.tsx +107 -0
  647. package/stack-template/app/ui-kit/src/ui/team/ping-feed/index.tsx +102 -0
  648. package/stack-template/app/ui-kit/src/ui/team/ping-feed/ping-feed.stories.tsx +57 -0
  649. package/stack-template/app/ui-kit/src/ui/team/ping-feed/ping-feed.test.tsx +60 -0
  650. package/stack-template/app/ui-kit/src/ui/team/stage-pipeline/index.tsx +60 -0
  651. package/stack-template/app/ui-kit/src/ui/team/stage-pipeline/stage-pipeline.stories.tsx +68 -0
  652. package/stack-template/app/ui-kit/src/ui/team/stage-pipeline/stage-pipeline.test.tsx +50 -0
  653. package/stack-template/app/ui-kit/src/ui/team/team-dashboard/index.tsx +103 -0
  654. package/stack-template/app/ui-kit/src/ui/team/team-dashboard/team-dashboard.stories.tsx +72 -0
  655. package/stack-template/app/ui-kit/src/ui/team/team-dashboard/team-dashboard.test.tsx +72 -0
  656. package/stack-template/app/ui-kit/src/ui/time-picker/index.tsx +143 -0
  657. package/stack-template/app/ui-kit/src/ui/time-picker/time-picker.stories.tsx +90 -0
  658. package/stack-template/app/ui-kit/src/ui/time-picker/time-picker.test.tsx +64 -0
  659. package/stack-template/app/ui-kit/src/ui/timeline/index.tsx +91 -0
  660. package/stack-template/app/ui-kit/src/ui/timeline/timeline.stories.tsx +209 -0
  661. package/stack-template/app/ui-kit/src/ui/timeline/timeline.test.tsx +116 -0
  662. package/stack-template/app/ui-kit/src/ui/timesheet/index.tsx +277 -0
  663. package/stack-template/app/ui-kit/src/ui/timesheet/timesheet.stories.tsx +97 -0
  664. package/stack-template/app/ui-kit/src/ui/timesheet/timesheet.test.tsx +103 -0
  665. package/stack-template/app/ui-kit/src/ui/toggle-button/index.tsx +28 -0
  666. package/stack-template/app/ui-kit/src/ui/toggle-button/toggle-button-group.stories.tsx +85 -0
  667. package/stack-template/app/ui-kit/src/ui/toggle-button/toggle-button-group.test.tsx +97 -0
  668. package/stack-template/app/ui-kit/src/ui/toggle-button/toggle-button-group.tsx +81 -0
  669. package/stack-template/app/ui-kit/src/ui/toggle-button/toggle-button.stories.tsx +46 -0
  670. package/stack-template/app/ui-kit/src/ui/toggle-button/toggle-button.test.tsx +38 -0
  671. package/stack-template/app/ui-kit/src/ui/toolbar/index.tsx +18 -0
  672. package/stack-template/app/ui-kit/src/ui/toolbar/toolbar.stories.tsx +35 -0
  673. package/stack-template/app/ui-kit/src/ui/toolbar/toolbar.test.tsx +11 -0
  674. package/stack-template/app/ui-kit/src/ui/tooltip/index.tsx +61 -0
  675. package/stack-template/app/ui-kit/src/ui/tooltip/tooltip.stories.tsx +81 -0
  676. package/stack-template/app/ui-kit/src/ui/tooltip/tooltip.test.tsx +120 -0
  677. package/stack-template/app/ui-kit/src/ui/tour/index.tsx +210 -0
  678. package/stack-template/app/ui-kit/src/ui/tour/tour.stories.tsx +153 -0
  679. package/stack-template/app/ui-kit/src/ui/tour/tour.test.tsx +131 -0
  680. package/stack-template/app/ui-kit/src/ui/transition/index.tsx +377 -0
  681. package/stack-template/app/ui-kit/src/ui/transition/transition.stories.tsx +254 -0
  682. package/stack-template/app/ui-kit/src/ui/transition/transition.test.tsx +285 -0
  683. package/stack-template/app/ui-kit/src/ui/tree/index.tsx +68 -0
  684. package/stack-template/app/ui-kit/src/ui/tree/tree.stories.tsx +139 -0
  685. package/stack-template/app/ui-kit/src/ui/tree/tree.test.tsx +77 -0
  686. package/stack-template/app/ui-kit/src/ui/tree-select/index.tsx +251 -0
  687. package/stack-template/app/ui-kit/src/ui/tree-select/tree-select.stories.tsx +105 -0
  688. package/stack-template/app/ui-kit/src/ui/tree-select/tree-select.test.tsx +174 -0
  689. package/stack-template/app/ui-kit/src/ui/tree-table/index.tsx +229 -0
  690. package/stack-template/app/ui-kit/src/ui/tree-table/tree-table.stories.tsx +111 -0
  691. package/stack-template/app/ui-kit/src/ui/tree-table/tree-table.test.tsx +137 -0
  692. package/stack-template/app/ui-kit/src/ui/typography/index.tsx +124 -0
  693. package/stack-template/app/ui-kit/src/ui/typography/typography.stories.tsx +65 -0
  694. package/stack-template/app/ui-kit/src/ui/typography/typography.test.tsx +117 -0
  695. package/stack-template/app/ui-kit/src/ui/upload/index.tsx +39 -0
  696. package/stack-template/app/ui-kit/src/ui/upload/upload-core.tsx +204 -0
  697. package/stack-template/app/ui-kit/src/ui/upload/upload-dropzone.tsx +79 -0
  698. package/stack-template/app/ui-kit/src/ui/upload/upload.stories.tsx +67 -0
  699. package/stack-template/app/ui-kit/src/ui/upload/upload.test.tsx +204 -0
  700. package/stack-template/app/ui-kit/src/ui/virtual-scroll/index.tsx +86 -0
  701. package/stack-template/app/ui-kit/src/ui/virtual-scroll/virtual-scroll.stories.tsx +111 -0
  702. package/stack-template/app/ui-kit/src/ui/virtual-scroll/virtual-scroll.test.tsx +125 -0
  703. package/stack-template/app/ui-kit/src/ui/watermark/index.tsx +182 -0
  704. package/stack-template/app/ui-kit/src/ui/watermark/watermark.stories.tsx +96 -0
  705. package/stack-template/app/ui-kit/src/ui/watermark/watermark.test.tsx +120 -0
  706. package/stack-template/app/ui-kit/src/utils/array.test.ts +88 -0
  707. package/stack-template/app/ui-kit/src/utils/array.ts +51 -0
  708. package/stack-template/app/ui-kit/src/utils/dom.test.tsx +69 -0
  709. package/stack-template/app/ui-kit/src/utils/dom.ts +38 -0
  710. package/stack-template/app/ui-kit/src/utils/format.test.ts +63 -0
  711. package/stack-template/app/ui-kit/src/utils/format.ts +81 -0
  712. package/stack-template/app/ui-kit/tests/setup.ts +15 -0
  713. package/stack-template/app/ui-kit/tsconfig.json +18 -0
  714. package/stack-template/app/ui-kit/tsup.config.ts +37 -0
  715. package/stack-template/app/ui-kit/vercel.json +6 -0
  716. package/stack-template/app/ui-kit/vitest.config.ts +22 -0
  717. package/stack-template/contracts/auth/token-contract.md +119 -0
  718. package/stack-template/contracts/auth/users.json +35 -0
  719. package/stack-template/contracts/db/tasks-schema.sql +62 -0
  720. package/stack-template/contracts/events/fixtures/task.assigned.json +11 -0
  721. package/stack-template/contracts/events/fixtures/task.assignment_failed.json +11 -0
  722. package/stack-template/contracts/events/fixtures/task.created.json +12 -0
  723. package/stack-template/contracts/events/fixtures/task.deleted.json +10 -0
  724. package/stack-template/contracts/events/fixtures/task.updated.json +12 -0
  725. package/stack-template/contracts/events/task-events.md +80 -0
  726. package/stack-template/contracts/openapi/activity.yaml +196 -0
  727. package/stack-template/contracts/openapi/auth.yaml +310 -0
  728. package/stack-template/contracts/openapi/tasks.yaml +316 -0
  729. package/stack-template/contracts/openapi/users.yaml +345 -0
  730. package/stack-template/docs/adr/0001-mixed-stack.md +14 -0
  731. package/stack-template/docs/adr/0002-mf-rspack.md +14 -0
  732. package/stack-template/docs/adr/0003-contract-first.md +14 -0
  733. package/stack-template/docs/adr/0004-readonly-schema-tradeoff.md +14 -0
  734. package/stack-template/docs/adr/0005-fail-closed.md +14 -0
  735. package/stack-template/docs/adr/0006-fire-and-forget-events.md +14 -0
  736. package/stack-template/docs/adr/0007-fork-not-upgrade.md +14 -0
  737. package/stack-template/docs/adr/0008-kafka-outbox-saga.md +24 -0
  738. package/stack-template/docs/adr/0009-ui-kit-split.md +39 -0
  739. package/stack-template/docs/adr/README.md +15 -0
  740. package/stack-template/docs/ai/recipes.md +956 -0
  741. package/stack-template/docs/ai/table-Theming.md +25 -0
  742. package/stack-template/docs/ai/table-datadisplay-mz.md +20 -0
  743. package/stack-template/docs/ai/table-feedback.md +20 -0
  744. package/stack-template/docs/ai/table-forms.md +26 -0
  745. package/stack-template/docs/git-flow.md +70 -0
  746. package/stack-template/docs/parity-matrix.md +102 -0
  747. package/stack-template/docs/superpowers/contexts/auth-real/gap-answer.md +195 -0
  748. package/stack-template/docs/superpowers/contexts/auth-real/sf-1.md +21 -0
  749. package/stack-template/docs/superpowers/contexts/auth-real/sf-2.md +21 -0
  750. package/stack-template/docs/superpowers/contexts/auth-real/sf-3.md +17 -0
  751. package/stack-template/docs/superpowers/contexts/auth-real/sf-4.md +20 -0
  752. package/stack-template/docs/superpowers/contexts/auth-real/sf-5.md +21 -0
  753. package/stack-template/docs/superpowers/contexts/experience-design.md +85 -0
  754. package/stack-template/docs/superpowers/contexts/sf-1.md +39 -0
  755. package/stack-template/docs/superpowers/contexts/sf-2.md +42 -0
  756. package/stack-template/docs/superpowers/contexts/sf-3.md +42 -0
  757. package/stack-template/docs/superpowers/contexts/sf-4.md +26 -0
  758. package/stack-template/docs/superpowers/contexts/sf-5.md +36 -0
  759. package/stack-template/docs/superpowers/contexts/sf-6.md +36 -0
  760. package/stack-template/docs/superpowers/contexts/sf-7.md +35 -0
  761. package/stack-template/docs/superpowers/contexts/sf-cin-overview.md +21 -0
  762. package/stack-template/docs/superpowers/contexts/ui-redesign/sf-1.md +46 -0
  763. package/stack-template/docs/superpowers/contexts/ui-redesign/sf-2.md +44 -0
  764. package/stack-template/docs/superpowers/contexts/ui-redesign/sf-3.md +47 -0
  765. package/stack-template/docs/superpowers/contexts/ui-redesign/sf-4.md +44 -0
  766. package/stack-template/docs/superpowers/contexts/ui-redesign/sf-5.md +36 -0
  767. package/stack-template/docs/superpowers/designs/direction-approved.md +11 -0
  768. package/stack-template/docs/superpowers/designs/ui-redesign/drafts/a-roulette-soundviz.html +353 -0
  769. package/stack-template/docs/superpowers/designs/ui-redesign/drafts/b-reference.html +347 -0
  770. package/stack-template/docs/superpowers/designs/ui-redesign/drafts/c-designer.html +375 -0
  771. package/stack-template/docs/superpowers/designs/ui-redesign/ds-demo.html +140 -0
  772. package/stack-template/docs/superpowers/designs/ui-redesign-direction.md +93 -0
  773. package/stack-template/docs/superpowers/improvements-log.md +158 -0
  774. package/stack-template/docs/superpowers/mindmaps/auth-real.wakii +115 -0
  775. package/stack-template/docs/superpowers/mindmaps/kafka-outbox-saga.wakii +110 -0
  776. package/stack-template/docs/superpowers/mindmaps/micro-boilerplate.wakii +171 -0
  777. package/stack-template/docs/superpowers/mindmaps/ui-redesign.wakii +116 -0
  778. package/stack-template/docs/superpowers/mindmaps/vu53-cinematic-experience.wakii +32 -0
  779. package/stack-template/docs/superpowers/plans/2026-10-12-sf-1-contracts-platform-plan.md +44 -0
  780. package/stack-template/docs/superpowers/plans/2026-10-12-sf-3-spring-svc-impact.md +69 -0
  781. package/stack-template/docs/superpowers/plans/2026-10-12-sf-3-spring-svc-plan.md +27 -0
  782. package/stack-template/docs/superpowers/plans/2026-10-12-sf-4-go-svc-plan.md +99 -0
  783. package/stack-template/docs/superpowers/plans/2026-10-12-sf-5-fe-shell-plan.md +64 -0
  784. package/stack-template/docs/superpowers/plans/2026-10-12-sf-6-fe-remotes-plan.md +83 -0
  785. package/stack-template/docs/superpowers/plans/2026-10-12-sf-7-convergence-plan.md +59 -0
  786. package/stack-template/docs/superpowers/plans/2026-10-12-sf2-node-svc-plan.md +19 -0
  787. package/stack-template/docs/superpowers/plans/2026-10-13-sf-1-kafka-foundation-plan.md +30 -0
  788. package/stack-template/docs/superpowers/plans/2026-10-13-sf-1-ui-redesign-plan.md +28 -0
  789. package/stack-template/docs/superpowers/plans/2026-10-13-sf-2-node-outbox-plan.md +46 -0
  790. package/stack-template/docs/superpowers/plans/2026-10-13-sf-3-go-consumer-plan.md +31 -0
  791. package/stack-template/docs/superpowers/plans/2026-10-13-sf-4-spring-tx-plan.md +96 -0
  792. package/stack-template/docs/superpowers/plans/2026-10-13-sf-5-convergence-plan.md +21 -0
  793. package/stack-template/docs/superpowers/plans/2026-10-14-sf-2-ui-redesign-plan.md +32 -0
  794. package/stack-template/docs/superpowers/plans/2026-10-14-sf-3-ui-redesign-plan.md +34 -0
  795. package/stack-template/docs/superpowers/plans/2026-10-14-sf-5-ui-redesign-plan.md +35 -0
  796. package/stack-template/docs/superpowers/plans/2026-10-17-auth-real-plan.md +112 -0
  797. package/stack-template/docs/superpowers/plans/2026-10-17-ui-kit-split-plan.md +18 -0
  798. package/stack-template/docs/superpowers/reviews/2026-10-18-data-display-coverage.md +52 -0
  799. package/stack-template/docs/superpowers/reviews/code-reviewer-sf-5-r1.md +65 -0
  800. package/stack-template/docs/superpowers/reviews/security-audit-sf-5-auth-real.md +39 -0
  801. package/stack-template/docs/superpowers/specs/2026-10-12-boilerplate-microservice-mfe.md +110 -0
  802. package/stack-template/docs/superpowers/specs/2026-10-12-sf-3-spring-svc-design.md +160 -0
  803. package/stack-template/docs/superpowers/specs/2026-10-12-sf-7-convergence-spec.md +114 -0
  804. package/stack-template/docs/superpowers/specs/2026-10-12-sf2-node-svc-spec.md +42 -0
  805. package/stack-template/docs/superpowers/specs/2026-10-13-kafka-outbox-saga.md +111 -0
  806. package/stack-template/docs/superpowers/specs/2026-10-13-ui-redesign.md +173 -0
  807. package/stack-template/docs/superpowers/specs/2026-10-14-sf-3-ui-redesign.md +46 -0
  808. package/stack-template/docs/superpowers/specs/2026-10-14-sf-5-ui-redesign.md +30 -0
  809. package/stack-template/docs/superpowers/specs/2026-10-17-antd-look-tokens.md +28 -0
  810. package/stack-template/docs/superpowers/specs/2026-10-17-auth-real-design.md +124 -0
  811. package/stack-template/docs/superpowers/specs/2026-10-17-tailwind-migration.md +32 -0
  812. package/stack-template/docs/superpowers/specs/2026-10-17-ui-kit-split.md +44 -0
  813. package/stack-template/docs/superpowers/specs/2026-10-18-0.3.0-release-notes.md +77 -0
  814. package/stack-template/docs/toolchain.md +28 -0
  815. package/stack-template/docs/wakii-playbook.md +172 -0
  816. package/stack-template/docs/walkthrough.md +204 -0
  817. package/stack-template/orca.yaml +146 -0
  818. package/stack-template/package.json +19 -0
  819. package/stack-template/platform/.env.example +72 -0
  820. package/stack-template/platform/conventions/01-dockerfile-pattern.md +78 -0
  821. package/stack-template/platform/conventions/02-healthcheck.md +26 -0
  822. package/stack-template/platform/conventions/03-logging.md +41 -0
  823. package/stack-template/platform/conventions/04-error-envelope.md +41 -0
  824. package/stack-template/platform/conventions/05-ports.md +38 -0
  825. package/stack-template/platform/conventions/06-configuration.md +25 -0
  826. package/stack-template/platform/conventions/07-cors.md +23 -0
  827. package/stack-template/platform/conventions/08-gateway-and-remotes.md +33 -0
  828. package/stack-template/platform/conventions/09-async-messaging.md +42 -0
  829. package/stack-template/platform/conventions/README.md +15 -0
  830. package/stack-template/platform/docker-compose.yml +255 -0
  831. package/stack-template/platform/gateway/nginx/nginx.conf +137 -0
  832. package/stack-template/platform/postgres/init/01-init.sh +55 -0
  833. package/stack-template/pnpm-lock.yaml +15992 -0
  834. package/stack-template/pnpm-workspace.yaml +24 -0
  835. package/stack-template/scripts/scaffold/scaffold.sh +110 -0
  836. package/stack-template/scripts/smoke/e2e.sh +305 -0
  837. package/stack-template/scripts/snapshot-stack-template.sh +84 -0
  838. package/stack-template/services/auth-svc/.dockerignore +5 -0
  839. package/stack-template/services/auth-svc/Dockerfile +32 -0
  840. package/stack-template/services/auth-svc/README.md +101 -0
  841. package/stack-template/services/auth-svc/drizzle/0000_init.sql +25 -0
  842. package/stack-template/services/auth-svc/drizzle/meta/0000_snapshot.json +190 -0
  843. package/stack-template/services/auth-svc/drizzle/meta/_journal.json +13 -0
  844. package/stack-template/services/auth-svc/drizzle.config.ts +10 -0
  845. package/stack-template/services/auth-svc/package.json +35 -0
  846. package/stack-template/services/auth-svc/pnpm-lock.yaml +2425 -0
  847. package/stack-template/services/auth-svc/src/app.test.ts +403 -0
  848. package/stack-template/services/auth-svc/src/app.ts +103 -0
  849. package/stack-template/services/auth-svc/src/auth-crypto.test.ts +32 -0
  850. package/stack-template/services/auth-svc/src/auth-crypto.ts +25 -0
  851. package/stack-template/services/auth-svc/src/db/client.ts +24 -0
  852. package/stack-template/services/auth-svc/src/db/migrate.ts +72 -0
  853. package/stack-template/services/auth-svc/src/db/schema.ts +39 -0
  854. package/stack-template/services/auth-svc/src/db/seed.ts +60 -0
  855. package/stack-template/services/auth-svc/src/env.test.ts +55 -0
  856. package/stack-template/services/auth-svc/src/env.ts +57 -0
  857. package/stack-template/services/auth-svc/src/logger.ts +31 -0
  858. package/stack-template/services/auth-svc/src/plugins/auth.ts +70 -0
  859. package/stack-template/services/auth-svc/src/repo.drizzle.integration.test.ts +182 -0
  860. package/stack-template/services/auth-svc/src/repo.drizzle.ts +144 -0
  861. package/stack-template/services/auth-svc/src/repo.test-support.ts +168 -0
  862. package/stack-template/services/auth-svc/src/repo.ts +60 -0
  863. package/stack-template/services/auth-svc/src/routes/auth.ts +193 -0
  864. package/stack-template/services/auth-svc/src/routes/health.ts +40 -0
  865. package/stack-template/services/auth-svc/src/server.ts +54 -0
  866. package/stack-template/services/auth-svc/tsconfig.json +20 -0
  867. package/stack-template/services/go-svc/.dockerignore +5 -0
  868. package/stack-template/services/go-svc/Dockerfile +19 -0
  869. package/stack-template/services/go-svc/README.md +93 -0
  870. package/stack-template/services/go-svc/cmd/server/main.go +105 -0
  871. package/stack-template/services/go-svc/go.mod +12 -0
  872. package/stack-template/services/go-svc/internal/api/auth.go +83 -0
  873. package/stack-template/services/go-svc/internal/api/auth_test.go +92 -0
  874. package/stack-template/services/go-svc/internal/api/envelope.go +61 -0
  875. package/stack-template/services/go-svc/internal/api/envelope_test.go +51 -0
  876. package/stack-template/services/go-svc/internal/api/fakes_test.go +105 -0
  877. package/stack-template/services/go-svc/internal/api/feed.go +98 -0
  878. package/stack-template/services/go-svc/internal/api/feed_test.go +118 -0
  879. package/stack-template/services/go-svc/internal/api/health.go +34 -0
  880. package/stack-template/services/go-svc/internal/api/health_test.go +46 -0
  881. package/stack-template/services/go-svc/internal/api/json.go +13 -0
  882. package/stack-template/services/go-svc/internal/api/middleware.go +82 -0
  883. package/stack-template/services/go-svc/internal/api/middleware_test.go +48 -0
  884. package/stack-template/services/go-svc/internal/api/openapi/activity.yaml +196 -0
  885. package/stack-template/services/go-svc/internal/api/openapi.go +29 -0
  886. package/stack-template/services/go-svc/internal/api/openapi_test.go +38 -0
  887. package/stack-template/services/go-svc/internal/api/router.go +86 -0
  888. package/stack-template/services/go-svc/internal/api/stats.go +63 -0
  889. package/stack-template/services/go-svc/internal/api/stats_test.go +83 -0
  890. package/stack-template/services/go-svc/internal/api/types.go +32 -0
  891. package/stack-template/services/go-svc/internal/apperr/apperr.go +9 -0
  892. package/stack-template/services/go-svc/internal/config/config.go +115 -0
  893. package/stack-template/services/go-svc/internal/config/config_test.go +92 -0
  894. package/stack-template/services/go-svc/internal/consumer/envelope.go +78 -0
  895. package/stack-template/services/go-svc/internal/consumer/envelope_test.go +154 -0
  896. package/stack-template/services/go-svc/internal/consumer/handler.go +91 -0
  897. package/stack-template/services/go-svc/internal/consumer/handler_test.go +136 -0
  898. package/stack-template/services/go-svc/internal/consumer/producer.go +117 -0
  899. package/stack-template/services/go-svc/internal/consumer/runner.go +194 -0
  900. package/stack-template/services/go-svc/internal/consumer/runner_test.go +351 -0
  901. package/stack-template/services/go-svc/internal/logging/logging.go +49 -0
  902. package/stack-template/services/go-svc/internal/model/model.go +37 -0
  903. package/stack-template/services/go-svc/internal/resolve/resolver.go +134 -0
  904. package/stack-template/services/go-svc/internal/resolve/resolver_test.go +82 -0
  905. package/stack-template/services/go-svc/internal/resolve/token.go +130 -0
  906. package/stack-template/services/go-svc/internal/resolve/token_test.go +151 -0
  907. package/stack-template/services/go-svc/internal/store/events.go +71 -0
  908. package/stack-template/services/go-svc/internal/store/migrate.go +90 -0
  909. package/stack-template/services/go-svc/internal/store/migrate_test.go +34 -0
  910. package/stack-template/services/go-svc/internal/store/migrations/0001_create_events.sql +20 -0
  911. package/stack-template/services/go-svc/internal/store/migrations/0002_widen_action_cascade.sql +14 -0
  912. package/stack-template/services/go-svc/internal/store/store.go +65 -0
  913. package/stack-template/services/go-svc/internal/store/tasks.go +109 -0
  914. package/stack-template/services/go-svc/internal/swagger/assets/LICENSE +202 -0
  915. package/stack-template/services/go-svc/internal/swagger/assets/NOTICE +2 -0
  916. package/stack-template/services/go-svc/internal/swagger/assets/favicon-16x16.png +0 -0
  917. package/stack-template/services/go-svc/internal/swagger/assets/favicon-32x32.png +0 -0
  918. package/stack-template/services/go-svc/internal/swagger/assets/swagger-ui-bundle.js +2 -0
  919. package/stack-template/services/go-svc/internal/swagger/assets/swagger-ui.css +3 -0
  920. package/stack-template/services/go-svc/internal/swagger/index.html +28 -0
  921. package/stack-template/services/go-svc/internal/swagger/swagger.go +38 -0
  922. package/stack-template/services/go-svc/test/integration/integration_test.go +523 -0
  923. package/stack-template/services/node-svc/.dockerignore +5 -0
  924. package/stack-template/services/node-svc/Dockerfile +27 -0
  925. package/stack-template/services/node-svc/README.md +155 -0
  926. package/stack-template/services/node-svc/drizzle/0000_init.sql +17 -0
  927. package/stack-template/services/node-svc/drizzle/0001_status-assignment-failed.sql +2 -0
  928. package/stack-template/services/node-svc/drizzle/0002_outbox-table.sql +7 -0
  929. package/stack-template/services/node-svc/drizzle/meta/0000_snapshot.json +136 -0
  930. package/stack-template/services/node-svc/drizzle/meta/0001_snapshot.json +136 -0
  931. package/stack-template/services/node-svc/drizzle/meta/0002_snapshot.json +180 -0
  932. package/stack-template/services/node-svc/drizzle/meta/_journal.json +27 -0
  933. package/stack-template/services/node-svc/drizzle.config.ts +10 -0
  934. package/stack-template/services/node-svc/package.json +39 -0
  935. package/stack-template/services/node-svc/pnpm-lock.yaml +2706 -0
  936. package/stack-template/services/node-svc/src/app.test.ts +611 -0
  937. package/stack-template/services/node-svc/src/app.ts +126 -0
  938. package/stack-template/services/node-svc/src/clients/spring.ts +53 -0
  939. package/stack-template/services/node-svc/src/compensation-consumer.test.ts +157 -0
  940. package/stack-template/services/node-svc/src/compensation-consumer.ts +170 -0
  941. package/stack-template/services/node-svc/src/db/client.ts +23 -0
  942. package/stack-template/services/node-svc/src/db/migrate.ts +63 -0
  943. package/stack-template/services/node-svc/src/db/schema-diff.test.ts +252 -0
  944. package/stack-template/services/node-svc/src/db/schema.ts +64 -0
  945. package/stack-template/services/node-svc/src/env.ts +52 -0
  946. package/stack-template/services/node-svc/src/logger.ts +18 -0
  947. package/stack-template/services/node-svc/src/outbox-relay.test.ts +141 -0
  948. package/stack-template/services/node-svc/src/outbox-relay.ts +182 -0
  949. package/stack-template/services/node-svc/src/plugins/auth.ts +76 -0
  950. package/stack-template/services/node-svc/src/repo.drizzle.test.ts +182 -0
  951. package/stack-template/services/node-svc/src/repo.drizzle.ts +158 -0
  952. package/stack-template/services/node-svc/src/repo.ts +73 -0
  953. package/stack-template/services/node-svc/src/routes/health.ts +42 -0
  954. package/stack-template/services/node-svc/src/routes/tasks.ts +287 -0
  955. package/stack-template/services/node-svc/src/server.ts +91 -0
  956. package/stack-template/services/node-svc/src/types/api.d.ts +391 -0
  957. package/stack-template/services/node-svc/src/types/log.ts +3 -0
  958. package/stack-template/services/node-svc/tsconfig.json +20 -0
  959. package/stack-template/services/spring-svc/.dockerignore +5 -0
  960. package/stack-template/services/spring-svc/.mvn/wrapper/maven-wrapper.properties +20 -0
  961. package/stack-template/services/spring-svc/Dockerfile +22 -0
  962. package/stack-template/services/spring-svc/README.md +121 -0
  963. package/stack-template/services/spring-svc/mvnw +332 -0
  964. package/stack-template/services/spring-svc/mvnw.cmd +206 -0
  965. package/stack-template/services/spring-svc/pom.xml +144 -0
  966. package/stack-template/services/spring-svc/src/main/java/com/example/springsvc/SpringSvcApplication.java +12 -0
  967. package/stack-template/services/spring-svc/src/main/java/com/example/springsvc/domain/Team.java +64 -0
  968. package/stack-template/services/spring-svc/src/main/java/com/example/springsvc/domain/TeamRepository.java +34 -0
  969. package/stack-template/services/spring-svc/src/main/java/com/example/springsvc/domain/User.java +72 -0
  970. package/stack-template/services/spring-svc/src/main/java/com/example/springsvc/domain/UserRepository.java +10 -0
  971. package/stack-template/services/spring-svc/src/main/java/com/example/springsvc/logging/LowercaseLevelJsonProvider.java +24 -0
  972. package/stack-template/services/spring-svc/src/main/java/com/example/springsvc/security/JwtAuthFilter.java +89 -0
  973. package/stack-template/services/spring-svc/src/main/java/com/example/springsvc/security/JwtVerifier.java +48 -0
  974. package/stack-template/services/spring-svc/src/main/java/com/example/springsvc/service/TeamService.java +84 -0
  975. package/stack-template/services/spring-svc/src/main/java/com/example/springsvc/web/ApiError.java +15 -0
  976. package/stack-template/services/spring-svc/src/main/java/com/example/springsvc/web/ApiException.java +37 -0
  977. package/stack-template/services/spring-svc/src/main/java/com/example/springsvc/web/DevCorsConfig.java +41 -0
  978. package/stack-template/services/spring-svc/src/main/java/com/example/springsvc/web/ErrorEnvelope.java +27 -0
  979. package/stack-template/services/spring-svc/src/main/java/com/example/springsvc/web/GlobalExceptionHandler.java +90 -0
  980. package/stack-template/services/spring-svc/src/main/java/com/example/springsvc/web/HealthController.java +46 -0
  981. package/stack-template/services/spring-svc/src/main/java/com/example/springsvc/web/ReadinessService.java +40 -0
  982. package/stack-template/services/spring-svc/src/main/java/com/example/springsvc/web/TeamController.java +79 -0
  983. package/stack-template/services/spring-svc/src/main/java/com/example/springsvc/web/UserController.java +187 -0
  984. package/stack-template/services/spring-svc/src/main/java/db/migration/V2__SeedUsers.java +66 -0
  985. package/stack-template/services/spring-svc/src/main/resources/application.yml +42 -0
  986. package/stack-template/services/spring-svc/src/main/resources/db/migration/V1__init.sql +24 -0
  987. package/stack-template/services/spring-svc/src/main/resources/logback-spring.xml +34 -0
  988. package/stack-template/services/spring-svc/src/main/resources/openapi/users.yaml +345 -0
  989. package/stack-template/services/spring-svc/src/test/java/com/example/springsvc/logging/LogShapeTest.java +39 -0
  990. package/stack-template/services/spring-svc/src/test/java/com/example/springsvc/security/JwtAuthFilterTest.java +106 -0
  991. package/stack-template/services/spring-svc/src/test/java/com/example/springsvc/seed/SeedMatchIntegrationTest.java +68 -0
  992. package/stack-template/services/spring-svc/src/test/java/com/example/springsvc/service/TeamServiceRollbackTest.java +122 -0
  993. package/stack-template/services/spring-svc/src/test/java/com/example/springsvc/web/DevCorsTest.java +45 -0
  994. package/stack-template/services/spring-svc/src/test/java/com/example/springsvc/web/HealthCheckTest.java +56 -0
  995. package/stack-template/services/spring-svc/src/test/java/com/example/springsvc/web/TeamApiTest.java +139 -0
  996. package/stack-template/services/spring-svc/src/test/java/com/example/springsvc/web/TestPingController.java +21 -0
  997. package/stack-template/services/spring-svc/src/test/java/com/example/springsvc/web/UserApiTest.java +182 -0
  998. package/stack-template/stack-manifest.json +285 -0
@@ -0,0 +1,956 @@
1
+ # AI Recipes — 19 công thức UI dùng `@wakii/ui-kit`
2
+
3
+ Code dưới dùng **API thật đã verify theo source** (audit 2026-10-18). Import gốc:
4
+
5
+ ```tsx
6
+ import { /* components */ } from '@wakii/ui-kit';
7
+ import { z } from 'zod'; // peer-dep cho useForm
8
+ ```
9
+
10
+ Quy tắc chung xem [AGENTS.md](../../AGENTS.md). Mỗi recipe tự chứa đủ imports riêng để copy-chạy.
11
+
12
+ ---
13
+
14
+ ## 1. LoginForm — useForm + zod + Field + Input + Button
15
+
16
+ ```tsx
17
+ import { useState } from 'react';
18
+ import { z } from 'zod';
19
+ import { Button, Field, Input, useForm } from '@wakii/ui-kit';
20
+
21
+ const schema = z.object({
22
+ email: z.string().min(1, 'Email bắt buộc').email('Email không hợp lệ'),
23
+ password: z.string().min(8, 'Mật khẩu cần 8–72 ký tự.').max(72, 'Mật khẩu cần 8–72 ký tự.'),
24
+ });
25
+
26
+ export function LoginForm({ onLogin }: { onLogin: (d: { email: string; password: string }) => Promise<void> }) {
27
+ const form = useForm({ schema, initialValues: { email: '', password: '' }, mode: 'onSubmit' });
28
+ const [serverError, setServerError] = useState<string | null>(null);
29
+ const [submitting, setSubmitting] = useState(false);
30
+
31
+ return (
32
+ <form
33
+ className="flex w-80 flex-col gap-3"
34
+ onSubmit={form.handleSubmit(async (data) => {
35
+ setSubmitting(true);
36
+ try {
37
+ await onLogin(data); // sai → throw Error('Email hoặc mật khẩu không đúng.')
38
+ } catch (e) {
39
+ setServerError((e as Error).message);
40
+ } finally {
41
+ setSubmitting(false);
42
+ }
43
+ })}
44
+ >
45
+ <Field label="Email" htmlFor="email" error={form.errors.email}>
46
+ <Input id="email" name="email" type="email" autoComplete="username" placeholder="you@demo.local"
47
+ value={form.values.email} invalid={!!form.errors.email}
48
+ onChange={form.handleChange('email')} onBlur={form.handleBlur('email')} />
49
+ </Field>
50
+ <Field label="Mật khẩu" htmlFor="password" error={form.errors.password}>
51
+ <Input id="password" name="password" type="password" autoComplete="current-password"
52
+ value={form.values.password} invalid={!!form.errors.password}
53
+ onChange={form.handleChange('password')} />
54
+ </Field>
55
+ {serverError && <p role="alert" className="text-meta text-st-fail">{serverError}</p>}
56
+ <Button htmlType="submit" loading={submitting} disabled={submitting} block>Đăng nhập</Button>
57
+ </form>
58
+ );
59
+ }
60
+ ```
61
+
62
+ Ghi chú: sai schema → `handleSubmit` tự set `errors` + focus field đầu (`[name=…]`); `mode` đổi thành `'onBlur' | 'onChange'` nếu muốn validate sớm.
63
+
64
+ ## 2. CrudUserPage — Table + Select filter + Pagination + Modal + message
65
+
66
+ ```tsx
67
+ import { useMemo, useState } from 'react';
68
+ import { Button, Field, Input, message, Modal, Pagination, Select, Table } from '@wakii/ui-kit';
69
+
70
+ type User = { id: number; name: string; role: 'admin' | 'user' };
71
+ const USERS: User[] = [
72
+ { id: 1, name: 'Alice', role: 'admin' },
73
+ { id: 2, name: 'Bob', role: 'user' },
74
+ { id: 3, name: 'Carol', role: 'user' },
75
+ ];
76
+
77
+ export function CrudUserPage() {
78
+ const [users, setUsers] = useState(USERS);
79
+ const [roleFilter, setRoleFilter] = useState<string>('all');
80
+ const [page, setPage] = useState(1);
81
+ const [editing, setEditing] = useState<User | null>(null);
82
+
83
+ const filtered = useMemo(
84
+ () => users.filter((u) => roleFilter === 'all' || u.role === roleFilter),
85
+ [users, roleFilter],
86
+ );
87
+ const pageSize = 2;
88
+ const paged = filtered.slice((page - 1) * pageSize, page * pageSize);
89
+
90
+ return (
91
+ <div className="flex flex-col gap-4">
92
+ {/* FilterBar */}
93
+ <div className="flex items-center gap-2">
94
+ <Select
95
+ ariaLabel="Lọc theo vai trò"
96
+ className="w-44"
97
+ value={roleFilter}
98
+ onChange={(v) => { setRoleFilter(v); setPage(1); }}
99
+ options={[
100
+ { label: 'Tất cả', value: 'all' },
101
+ { label: 'Admin', value: 'admin' },
102
+ { label: 'User', value: 'user' },
103
+ ]}
104
+ />
105
+ </div>
106
+
107
+ <Table
108
+ columns={[
109
+ { key: 'name', title: 'Tên', sortable: true },
110
+ { key: 'role', title: 'Vai trò' },
111
+ {
112
+ key: 'act', title: '',
113
+ render: (row: User) => <Button size="small" onClick={() => setEditing(row)}>Sửa</Button>,
114
+ },
115
+ ]}
116
+ dataSource={paged}
117
+ rowKey="id"
118
+ emptyMessage={<p className="p-4 text-body text-ink3">Không có user phù hợp.</p>}
119
+ />
120
+ <Pagination current={page} pageSize={pageSize}
121
+ total={filtered.length} onChange={setPage} />
122
+
123
+ <Modal open={!!editing} title={`Sửa: ${editing?.name ?? ''}`}
124
+ onOk={() => { message.success('Đã lưu'); setEditing(null); }}
125
+ onCancel={() => setEditing(null)}>
126
+ <Field label="Tên" htmlFor="u-name">
127
+ <Input id="u-name" value={editing?.name ?? ''} onChange={(e) =>
128
+ setEditing(editing ? { ...editing, name: e.target.value } : editing)} />
129
+ </Field>
130
+ </Modal>
131
+ </div>
132
+ );
133
+ }
134
+ ```
135
+
136
+ ## 3. FilterBar — Select + Input + Button
137
+
138
+ ```tsx
139
+ import { Button, Icon, Input, Select } from '@wakii/ui-kit';
140
+
141
+ export function FilterBar({ q, setQ, status, setStatus, onReset }: {
142
+ q: string; setQ: (v: string) => void;
143
+ status: string; setStatus: (v: string) => void;
144
+ onReset: () => void;
145
+ }) {
146
+ return (
147
+ <div className="flex flex-wrap items-center gap-2">
148
+ <Input className="w-56" placeholder="Tìm theo tên…" allowClear prefix={<Icon name="search" size={14} />}
149
+ value={q} onChange={setQ} />
150
+ <Select className="w-40" ariaLabel="Trạng thái" value={status} onChange={setStatus}
151
+ options={[{ label: 'Tất cả', value: 'all' }, { label: 'Đang làm', value: 'doing' }, { label: 'Xong', value: 'done' }]} />
152
+ <Button type="text" icon={<Icon name="refresh" size={14} />} onClick={onReset}>Đặt lại</Button>
153
+ </div>
154
+ );
155
+ }
156
+ ```
157
+
158
+ ## 4. Wizard — Steps
159
+
160
+ ```tsx
161
+ import { useState } from 'react';
162
+ import { Button, Steps } from '@wakii/ui-kit';
163
+
164
+ const STEPS = [
165
+ { title: 'Chọn loại', description: 'Task hay bug' },
166
+ { title: 'Điền thông tin', description: 'Tiêu đề + assignee' },
167
+ { title: 'Xác nhận', description: 'Review rồi tạo' },
168
+ ];
169
+
170
+ export function Wizard() {
171
+ const [current, setCurrent] = useState(0);
172
+ return (
173
+ <div className="flex max-w-xl flex-col gap-6">
174
+ <Steps items={STEPS} current={current} />
175
+ <div className="min-h-24 rounded-xl border border-line bg-s2 p-4 text-body text-ink2">
176
+ Nội dung bước {current + 1} — {STEPS[current]?.description}
177
+ </div>
178
+ <div className="flex gap-2">
179
+ <Button disabled={current === 0} onClick={() => setCurrent((c) => c - 1)}>Quay lại</Button>
180
+ {current < STEPS.length - 1
181
+ ? <Button type="primary" onClick={() => setCurrent((c) => c + 1)}>Tiếp</Button>
182
+ : <Button type="primary" onClick={() => setCurrent(0)}>Hoàn tất</Button>}
183
+ </div>
184
+ </div>
185
+ );
186
+ }
187
+ ```
188
+
189
+ ## 5. Upload kèm preview — UploadDropzone + Progress
190
+
191
+ ```tsx
192
+ import { useRef, useState } from 'react';
193
+ import { UploadDropzone } from '@wakii/ui-kit';
194
+
195
+ export function UploadKemPreview({ onChange }: { onChange?: (files: File[]) => void }) {
196
+ const [previews, setPreviews] = useState<{ name: string; url: string }[]>([]);
197
+
198
+ return (
199
+ <div className="flex flex-col gap-3">
200
+ <UploadDropzone
201
+ accept="image/*"
202
+ multiple
203
+ maxCount={5}
204
+ onChange={(files) => {
205
+ onChange?.(files);
206
+ // component tự render danh sách file chuẩn (tên + size + × remove);
207
+ // preview ảnh tự dựng từ File[]
208
+ setPreviews(files.map((f) => ({ name: f.name, url: URL.createObjectURL(f) })));
209
+ }}
210
+ />
211
+ {previews.length > 0 && (
212
+ <div className="flex gap-2">
213
+ {previews.map((u) => (
214
+ <img key={u.url} src={u.url} alt={u.name} className="size-16 rounded-lg object-cover" />
215
+ ))}
216
+ </div>
217
+ )}
218
+ </div>
219
+ );
220
+ }
221
+ ```
222
+
223
+ *(Danh sách `UploadItem` (tên + size + Progress) do `UploadDropzone` tự render; `onChange` trả `File[]` để consumer dựng preview.)*
224
+
225
+ ## 6. DashboardLayout — stat cards + Chart + Timeline
226
+
227
+ ```tsx
228
+ import { Card, Chart, EmptyState, Icon, Tag, Timeline, type ChartConfig } from '@wakii/ui-kit';
229
+
230
+ // ⚠️ Shape NATIVE chart.js v4 — data lồng trong data:{labels,datasets}
231
+ const config = {
232
+ type: 'line' as const,
233
+ data: {
234
+ labels: ['T2', 'T3', 'T4', 'T5', 'T6', 'T7', 'CN'],
235
+ datasets: [{ label: 'Tasks', data: [12, 19, 8, 22, 30, 25, 18], borderColor: 'var(--accent)', tension: 0.35, fill: true }],
236
+ },
237
+ options: { responsive: true, maintainAspectRatio: false, animation: false },
238
+ } as unknown as ChartConfig;
239
+
240
+ const STATS = [
241
+ { title: 'Người dùng', value: '1.284', tag: '+12%', icon: 'users' as const },
242
+ { title: 'Tasks mở', value: '316', tag: '+5%', icon: 'check-circle' as const },
243
+ ];
244
+
245
+ export function DashboardLayout() {
246
+ return (
247
+ <div className="flex flex-col gap-6">
248
+ <div className="grid grid-cols-1 gap-6 sm:grid-cols-2 xl:grid-cols-4">
249
+ {STATS.map((s) => (
250
+ <Card key={s.title} hoverable>
251
+ <div className="flex items-center gap-3">
252
+ <span aria-hidden="true" className="grid size-10 shrink-0 place-items-center rounded-lg bg-accent/10 text-accent">
253
+ <Icon name={s.icon} size={18} />
254
+ </span>
255
+ <div className="min-w-0 flex-1">
256
+ <div className="text-body text-ink2">{s.title}</div>
257
+ <div className="mt-0.5 flex items-center gap-2">
258
+ <span className="text-2xl font-semibold leading-tight text-ink">{s.value}</span>
259
+ <span className="rounded-pill bg-st-done/10 px-1.5 py-0.5 text-[10px] font-medium text-st-done">{s.tag}</span>
260
+ </div>
261
+ </div>
262
+ </div>
263
+ </Card>
264
+ ))}
265
+ </div>
266
+ <div className="grid grid-cols-1 gap-6 xl:grid-cols-5">
267
+ <Card title="Hoạt động 7 ngày" extra={<Tag>Tuần này</Tag>} className="xl:col-span-3">
268
+ <div className="h-64"><Chart config={config} height={256} /></div>
269
+ </Card>
270
+ <Card title="Timeline" extra={<Tag>real-time</Tag>} className="xl:col-span-2">
271
+ <Timeline items={[
272
+ { key: 't1', children: 'Alice tạo task', label: '10 phút trước' },
273
+ { key: 't2', color: 'var(--st-done)', children: 'Bot sync Kafka', label: '1 giờ trước' },
274
+ ]} />
275
+ </Card>
276
+ </div>
277
+ </div>
278
+ );
279
+ }
280
+ ```
281
+
282
+ ## 7. MasterDetailDrawer — List + Avatar + Drawer
283
+
284
+ ```tsx
285
+ import { useState } from 'react';
286
+ import { Avatar, Button, Drawer, List } from '@wakii/ui-kit';
287
+
288
+ const PEOPLE = [
289
+ { id: 1, name: 'Alice', role: 'Admin' },
290
+ { id: 2, name: 'Bob', role: 'User' },
291
+ ];
292
+
293
+ export function MasterDetailDrawer() {
294
+ const [open, setOpen] = useState(false);
295
+ const [active, setActive] = useState(PEOPLE[0]);
296
+
297
+ return (
298
+ <>
299
+ <List
300
+ items={PEOPLE}
301
+ renderItem={(p) => (
302
+ <button type="button" className="flex w-full items-center gap-2 rounded-lg px-2 py-2 hover:bg-s3"
303
+ onClick={() => { setActive(p); setOpen(true); }}>
304
+ <Avatar size={28}>{p.name.charAt(0)}</Avatar>
305
+ <span className="text-body text-ink">{p.name}</span>
306
+ <span className="ml-auto text-meta text-ink3">{p.role}</span>
307
+ </button>
308
+ )}
309
+ />
310
+ <Drawer open={open} title={active.name} width={360} onClose={() => setOpen(false)}
311
+ footer={<Button type="primary" onClick={() => setOpen(false)}>Đóng</Button>}>
312
+ <p className="text-body text-ink2">Chi tiết của {active.name} ({active.role}).</p>
313
+ </Drawer>
314
+ </>
315
+ );
316
+ }
317
+ ```
318
+
319
+ ## 8. NotificationFlow — message + Badge bell
320
+
321
+ ```tsx
322
+ import { Badge, Button, Icon, message, notification } from '@wakii/ui-kit';
323
+
324
+ export function NotificationFlow() {
325
+ const notify = () => {
326
+ message.success('Đã lưu task');
327
+ notification.info('Kafka event', 'task.updated — feed activity đã cập nhật.');
328
+ };
329
+ return (
330
+ <div className="flex items-center gap-3">
331
+ <button type="button" aria-label="Thông báo (2 mới)"
332
+ className="relative cursor-pointer rounded-2 border-none bg-transparent p-1.5 text-ink2 hover:bg-s3">
333
+ <Icon name="bell" size={18} />
334
+ <span className="absolute -right-1 -top-1"><Badge variant="failed">2</Badge></span>
335
+ </button>
336
+ <Button type="primary" onClick={notify}>Kích hoạt sự kiện</Button>
337
+ </div>
338
+ );
339
+ }
340
+ ```
341
+
342
+ ## 9. FormSettings — Tabs + Switch + Field
343
+
344
+ ```tsx
345
+ import { useState } from 'react';
346
+ import { Field, Input, Switch, Tabs } from '@wakii/ui-kit';
347
+
348
+ export function FormSettings() {
349
+ const [dark, setDark] = useState(false);
350
+ const [name, setName] = useState('Alice');
351
+ return (
352
+ <Tabs
353
+ items={[
354
+ {
355
+ key: 'profile', label: 'Hồ sơ',
356
+ children: (
357
+ <div className="flex max-w-sm flex-col gap-3 pt-4">
358
+ <Field label="Tên hiển thị" htmlFor="s-name">
359
+ <Input id="s-name" value={name} onChange={setName} />
360
+ </Field>
361
+ </div>
362
+ ),
363
+ },
364
+ {
365
+ key: 'appearance', label: 'Giao diện',
366
+ children: (
367
+ <div className="flex items-center gap-3 pt-4">
368
+ <Switch checked={dark} onChange={setDark} aria-label="Dark mode" />
369
+ <span className="text-body text-ink2">Chủ đề tối</span>
370
+ </div>
371
+ ),
372
+ },
373
+ ]}
374
+ onChange={(k) => console.log('tab:', k)}
375
+ />
376
+ );
377
+ }
378
+ ```
379
+
380
+ ## 10. HeroLanding — Button + Tag + Typography + Divider
381
+
382
+ ```tsx
383
+ import { Button, Divider, Icon, Tag } from '@wakii/ui-kit';
384
+
385
+ export function HeroLanding() {
386
+ return (
387
+ <section className="flex min-h-[420px] flex-col items-center justify-center gap-4 text-center">
388
+ <Tag icon={<Icon name="check-circle" size={12} />}>v1.0 — production ready</Tag>
389
+ <h1 className="m-0 text-h1 font-semibold tracking-tight text-ink">
390
+ Bộ quản lý công việc cho team của bạn
391
+ </h1>
392
+ <p className="max-w-md text-body text-ink2">
393
+ Quản lý công việc, upload, hoạt động real-time — một bộ design system nhất quán.
394
+ </p>
395
+ <div className="flex gap-2">
396
+ <Button type="primary" size="large">Bắt đầu</Button>
397
+ <Button size="large">Xem demo</Button>
398
+ </div>
399
+ <Divider dashed>hoặc</Divider>
400
+ <span className="text-meta text-ink3">alice@demo.local / demo-password</span>
401
+ </section>
402
+ );
403
+ }
404
+ ```
405
+
406
+ ---
407
+
408
+ ## 11. DashboardGrid — Row/Col + Card + Statistic
409
+
410
+ ```tsx
411
+ import { Card, Col, Row, Statistic, type StatisticProps } from '@wakii/ui-kit';
412
+
413
+ const STATS: { title: string; value: string; delta: string; tone: StatisticProps['valueStyle'] }[] = [
414
+ { title: 'Người dùng', value: '1,284', delta: '+12%', tone: { color: 'var(--st-done)' } },
415
+ { title: 'Tasks mở', value: '316', delta: '+5%', tone: { color: 'var(--st-doing)' } },
416
+ { title: 'Quá hạn', value: '18', delta: '-3', tone: { color: 'var(--st-fail)' } },
417
+ { title: 'Uptime', value: '99.98%', delta: '30 ngày', tone: { color: 'var(--ink)' } },
418
+ ];
419
+
420
+ export function DashboardGrid() {
421
+ return (
422
+ <Row gutter={[16, 16]}>
423
+ {STATS.map((s) => (
424
+ <Col key={s.title} xs={24} md={12} xl={6}>
425
+ <Card>
426
+ <Statistic title={s.title} value={s.value} valueStyle={s.tone} suffix={s.delta} />
427
+ </Card>
428
+ </Col>
429
+ ))}
430
+ <Col span={24}>
431
+ <Card title="Hoạt động tuần này">…chart/list body…</Card>
432
+ </Col>
433
+ </Row>
434
+ );
435
+ }
436
+ ```
437
+
438
+ Ghi chú: `gutter={[ngang, dọc]}` px chảy qua negative-margin + col padding (mechanic antd — không cần tự tính margin). Span **mobile-first**: `xs={24} md={12} xl={6}` = 1 cột mobile / 2 cột tablet / 4 cột desktop; bậc breakpoint không truyền thì kế thừa bậc dưới. Layout 1 trục không cần cột → dùng `Flex` (`gap` thang `small|middle|large`) cho gọn.
439
+
440
+ ## 12. Popover hay Tooltip — chọn overlay nào
441
+
442
+ ```tsx
443
+ import { Button, Popover, Tooltip } from '@wakii/ui-kit';
444
+
445
+ // Tooltip — hint TEXT thuần, hover/focus là đủ.
446
+ <Tooltip title="Đồng bộ qua gateway, retry 3 lần rồi vào DLQ">
447
+ <Button icon>?</Button>
448
+ </Tooltip>
449
+
450
+ // Popover — nội dung GIÀU: title + ReactNode (list, link, action), trigger click.
451
+ <Popover
452
+ trigger="click"
453
+ title="Người thực hiện"
454
+ content={
455
+ <div className="flex w-56 flex-col gap-2">
456
+ <span>Alice — admin</span>
457
+ <span>Bob — member</span>
458
+ <Button size="small" type="primary">+ Mời</Button>
459
+ </div>
460
+ }
461
+ >
462
+ <Button>Quản lý (3)</Button>
463
+ </Popover>
464
+ ```
465
+
466
+ Ghi chú: **Tooltip** = 1 câu hint ≤250px, đóng theo hover/focus, không tương tác được (`pointer-events-none`). **Popover** = panel `role="dialog"` có `title` divider + body ReactNode tự do; mở bằng `trigger` `hover|click|focus` (click dùng cho panel có action — hover làm Panel khó bấm); đóng bằng ESC + click ngoài + (controlled) `onOpenChange`. Popconfirm vẫn là lựa chọn riêng cho xác nhận OK/Huỷ — đừng dựng confirm bằng Popover.
467
+
468
+ ---
469
+
470
+ ## 13. CrudTableExpandable — Table expandable + Input.Search filter
471
+
472
+ Bảng CRUD có row chi tiết mở được (master-detail) + search filter dùng `Input.Search`.
473
+ Cột expander **tự inject** khi truyền `expandable` — không cần khai cột `expander` thủ công.
474
+
475
+ ```tsx
476
+ import { useMemo, useState } from 'react';
477
+ import { Input, Table, Tag } from '@wakii/ui-kit';
478
+
479
+ type Task = { id: string; title: string; status: 'todo' | 'doing' | 'done'; priority: number; note: string };
480
+ const TASKS: Task[] = [
481
+ { id: '1', title: 'Setup repo', status: 'done', priority: 1, note: 'Scaffold + CI xanh.' },
482
+ { id: '2', title: 'Thêm endpoint tasks', status: 'doing', priority: 3, note: 'Drizzle + outbox pattern, chưa có retry.' },
483
+ { id: '3', title: 'E2E saga demo', status: 'todo', priority: 2, note: 'Chạy qua gateway :8080, assert DLQ.' },
484
+ ];
485
+
486
+ export function CrudTableExpandable() {
487
+ const [q, setQ] = useState('');
488
+
489
+ const filtered = useMemo(
490
+ () => TASKS.filter((t) => t.title.toLowerCase().includes(q.trim().toLowerCase())),
491
+ [q],
492
+ );
493
+
494
+ return (
495
+ <div className="flex flex-col gap-3">
496
+ {/* Search filter — onSearch fire khi Enter HOẶC click nút (antd parity) */}
497
+ <div className="w-72">
498
+ <Input.Search
499
+ aria-label="Tìm task"
500
+ placeholder="Tìm task…"
501
+ enterButton // true → nút primary icon search; hoặc enterButton="Tìm"
502
+ onSearch={setQ}
503
+ />
504
+ </div>
505
+
506
+ <Table
507
+ columns={[
508
+ { key: 'title', title: 'Task', sortable: true },
509
+ { key: 'status', title: 'Trạng thái', render: (r: Task) => <Tag>{r.status}</Tag> },
510
+ { key: 'priority', title: 'Ưu tiên', dataType: 'number', align: 'right' },
511
+ ]}
512
+ dataSource={filtered}
513
+ rowKey="id"
514
+ emptyMessage={<p className="p-4 text-body text-ink3">Không có task phù hợp.</p>}
515
+ expandable={{
516
+ // Row con chiếm full colspan, nền bg-s3 — render mọi ReactNode
517
+ expandedRowRender: (r) => (
518
+ <div className="text-label text-ink2">
519
+ <p className="font-medium text-ink">{r.title}</p>
520
+ <p className="mt-1">{r.note}</p>
521
+ </div>
522
+ ),
523
+ defaultExpandedRowKeys: ['2'], // mở sẵn lúc mount (uncontrolled)
524
+ rowExpandable: (r) => r.status !== 'done', // done không có chi tiết → không render nút chevron
525
+ }}
526
+ />
527
+ </div>
528
+ );
529
+ }
530
+ ```
531
+
532
+ Ghi chú: `onSearch` là **trigger-style** (Enter/click) — muốn lọc live theo từng phím dùng `onChange` của input (hoặc `useDebounce` + `onChange` cho query đắt). Chevron xoay 90° khi mở, nút mang `aria-expanded`; row `rowExpandable: false` không render nút. `expandable` kết hợp được với `paginator` + `selectionMode="checkbox"` (state mở giữ qua trang). Filter-bar cần nổi khi scroll dài → bọc trong `Affix` (`offsetTop={0}`) ngay phía trên `Table`.
533
+
534
+ ---
535
+
536
+ ## 14. KanbanTaskBoard — KanbanBoard + Tag + Avatar + WIP-limit
537
+
538
+ ```tsx
539
+ import { useState } from 'react';
540
+ import { Avatar, KanbanBoard, type KanbanColumnData } from '@wakii/ui-kit';
541
+
542
+ const INITIAL: KanbanColumnData[] = [
543
+ {
544
+ key: 'todo',
545
+ title: 'Cần làm',
546
+ cards: [
547
+ { key: 't1', title: 'Sửa token contrast', description: 'Dark mode lệch 1 chỗ', tag: 'design' },
548
+ { key: 't2', title: 'Outbox retry', left: <Avatar size={24}>B</Avatar> },
549
+ ],
550
+ },
551
+ { key: 'doing', title: 'Đang làm', wipLimit: 2, cards: [{ key: 't3', title: 'Rate-limit gateway', tag: 'infra' }] },
552
+ { key: 'done', title: 'Xong', cards: [] },
553
+ ];
554
+
555
+ export function KanbanTaskBoard() {
556
+ const [columns, setColumns] = useState(INITIAL);
557
+ return (
558
+ <KanbanBoard
559
+ columns={columns}
560
+ onCardMove={(cardKey, from, to, toIndex) => {
561
+ setColumns((prev) => {
562
+ const card = prev.flatMap((c) => c.cards).find((c) => c.key === cardKey);
563
+ if (!card) return prev;
564
+ return prev.map((col) => {
565
+ const cards = col.cards.filter((c) => c.key !== cardKey);
566
+ if (col.key === from) return { ...col, cards };
567
+ if (col.key === to) {
568
+ const next = [...cards];
569
+ next.splice(toIndex, 0, card); // toIndex = vị trí SAU khi card rời nguồn
570
+ return { ...col, cards: next };
571
+ }
572
+ return col;
573
+ });
574
+ });
575
+ }}
576
+ />
577
+ );
578
+ }
579
+ ```
580
+
581
+ Ghi chú: **controlled** — board không giữ state, mọi move (kéo chuột HOẶC keyboard) chỉ bắn `onCardMove(cardKey, fromColKey, toColKey, toIndex)`; `toIndex` là vị trí **cuối** trong cột đích sau khi card rời nguồn → dùng đúng công thức remove-then-splice như trên. `tag` tự render `Tag`, slot `left` cho `Avatar` assignee; card hoàn toàn khác → `renderCard(card, columnKey)`. Keyboard đầy đủ: Tab tới card → Space nhặt → ↑↓ đổi vị trí, ←→ đổi cột → Enter thả, Esc hủy — aria-live tự thông báo "Đã chuyển X tới cột Y, vị trí Z". Cột `wipLimit` đầy **từ chối** drop từ cột khác (viền `border-st-fail` + aria-live) nhưng vẫn cho đổi thứ tự trong chính cột đó (count không đổi). ⚠️ Native HTML5 DnD **không hỗ trợ touch** (mobile) — hướng nâng cấp tương lai là peer-dep dnd-kit, đừng tự viết pointer engine.
582
+
583
+ ---
584
+
585
+ ## 15. WeeklyTimesheet — Timesheet log giờ tuần + Statistic tổng
586
+
587
+ ```tsx
588
+ import { useMemo, useState } from 'react';
589
+ import { Card, Statistic, Timesheet, type TimesheetRow } from '@wakii/ui-kit';
590
+
591
+ // values ĐÚNG 7 phần tử theo cột Thứ 2 → Chủ nhật; null = ô trống
592
+ const INITIAL_ROWS: TimesheetRow[] = [
593
+ { key: 't1', label: 'Task #101', meta: 'Design system', values: [8, 8, 8, 8, 8, null, null] },
594
+ { key: 't2', label: 'Task #102', meta: 'Gateway', values: [1, 2, 0.5, null, null, null, null] },
595
+ { key: 't3', label: 'Task #103', meta: 'e2e', values: [null, null, null, 4, 6, 3, null] },
596
+ ];
597
+
598
+ export function WeeklyTimesheetPage() {
599
+ const [rows, setRows] = useState(INITIAL_ROWS);
600
+ const [week, setWeek] = useState(new Date()); // bất kỳ ngày nào — component tự chuẩn về Thứ 2
601
+
602
+ const total = useMemo(
603
+ () => rows.reduce((sum, r) => sum + r.values.reduce((a, v) => a + (v ?? 0), 0), 0),
604
+ [rows],
605
+ );
606
+
607
+ return (
608
+ <div className="flex flex-col gap-4">
609
+ <Card>
610
+ <Statistic title="Tổng giờ tuần này" value={total} suffix="h" />
611
+ </Card>
612
+ <Timesheet
613
+ rows={rows}
614
+ week={week}
615
+ editable
616
+ locale="vi"
617
+ onWeekChange={setWeek}
618
+ onCellChange={(rowKey, dayIndex, value) =>
619
+ setRows((prev) =>
620
+ prev.map((r) =>
621
+ r.key === rowKey
622
+ ? { ...r, values: r.values.map((v, i) => (i === dayIndex ? value : v)) }
623
+ : r,
624
+ ),
625
+ )
626
+ }
627
+ />
628
+ </div>
629
+ );
630
+ }
631
+ ```
632
+
633
+ Ghi chú: tuần **luôn bắt đầu Thứ 2** cả vi/en — `week` nhận bất kỳ Date nào trong tuần; nav prev/next bắn `onWeekChange` (Date = Thứ 2 tuần mới) và grid tự replay fade-in. Ô nhập clamp **0–24, step 0.5**; xoá ô → `onCellChange(…, null)` — state `rows` là nguồn sự thật, footer tổng cột + grand tự tính lại (bỏ `editable` → readonly text). Cột T7/CN đã nền nhẹ sẵn; `locale="en"` đổi headers + nhãn tuần sang `Oct 19–25, 2026`. Đang fetch → `loading` thay thân bảng bằng skeleton (header + nhãn tuần giữ nguyên). Tổng tuần trên `Statistic` tính consumer-side từ `rows` (tabular-nums built in).
634
+
635
+ ## 16. LandingAlive — Reveal + Parallax hero + CountUp KPI (Statistic + Grid)
636
+
637
+ ```tsx
638
+ import { CountUp, Parallax, Reveal, Col, Row, Statistic } from '@wakii/ui-kit';
639
+
640
+ type Kpi = { title: string; value: number; decimals?: number; suffix: string };
641
+
642
+ const KPIS: Kpi[] = [
643
+ { title: 'Tasks completed', value: 1284, suffix: 'tasks' },
644
+ { title: 'On-time rate', value: 96.4, decimals: 1, suffix: '%' },
645
+ { title: 'Active teams', value: 327, suffix: 'teams' },
646
+ ];
647
+
648
+ export function LandingAlive() {
649
+ return (
650
+ <div className="flex flex-col gap-10">
651
+ {/* Hero — ảnh trôi chậm hơn trang (speed 0.3 + scale nhẹ); tự tắt khi reduced-motion */}
652
+ <Parallax speed={0.3} scale className="overflow-hidden rounded-xl">
653
+ <img src="/hero.jpg" alt="Product hero" className="aspect-video w-full object-cover" />
654
+ </Parallax>
655
+
656
+ {/* KPI row — card trôi vào lệch pha, số đếm ease-out trong Statistic */}
657
+ <Row gutter={[24, 24]}>
658
+ {KPIS.map((kpi, i) => (
659
+ <Col key={kpi.title} xs={24} md={8}>
660
+ <Reveal direction="up" delay={i * 150}>
661
+ <div className="h-full rounded-xl border border-line bg-s1 p-5">
662
+ <Statistic
663
+ title={kpi.title}
664
+ value={<CountUp value={kpi.value} decimals={kpi.decimals} />}
665
+ suffix={kpi.suffix}
666
+ />
667
+ </div>
668
+ </Reveal>
669
+ </Col>
670
+ ))}
671
+ </Row>
672
+
673
+ {/* Feature pair — vào lần lượt từ 2 bên */}
674
+ <div className="grid gap-6 md:grid-cols-2">
675
+ <Reveal direction="left">
676
+ <div className="rounded-xl border border-line bg-s1 p-5 text-body text-ink2">
677
+ Real-time qua Kafka — activity feed cập nhật tức thì.
678
+ </div>
679
+ </Reveal>
680
+ <Reveal direction="right">
681
+ <div className="rounded-xl border border-line bg-s1 p-5 text-body text-ink2">
682
+ Module Federation — remote độc lập deploy, shell ghép runtime.
683
+ </div>
684
+ </Reveal>
685
+ </div>
686
+ </div>
687
+ );
688
+ }
689
+ ```
690
+
691
+ Ghi chú: **an toàn a11y có sẵn** — `Parallax` tự tắt khi `prefers-reduced-motion` (qua `useMediaQuery`), animation Reveal/CountUp rơi về tức thì nhờ block global; không cần nhánh UI riêng. `Reveal once` mặc định chỉ chạy 1 lần (IO disconnect sau khi vào) — thêm `once={false}` nếu muốn replay khi cuộn lại. `CountUp` đổi `value` giữa chừng sẽ animate tiếp **từ số đang hiển thị** (hợp poll/refresh số liệu); `decimals`/`format` định dạng hiển thị (`format={(n) => n.toLocaleString('vi-VN')}` cho grouping). `Parallax` chỉ track cửa sổ — đặt ngoài container `overflow: hidden` cuộn riêng. Tất cả 0 deps, chỉ animate `transform`/`opacity` (GPU).
692
+
693
+ ## 17. DynamicEmailsForm — useForm + useFieldArray (mảng động + zod validate từng row)
694
+
695
+ Form động kiểu antd `Form.List`: danh sách email thêm/xoá/đảo, zod validate **từng row** qua
696
+ path lồng `emails.<i>.email`. `nameAt` đặt name đúng path → focus-first-invalid của
697
+ `form.handleSubmit` tự vào đúng ô sai.
698
+
699
+ ```tsx
700
+ import { useState } from 'react';
701
+ import { z } from 'zod';
702
+ import { Button, Field, Input, useForm, useFieldArray } from '@wakii/ui-kit';
703
+
704
+ const schema = z.object({
705
+ emails: z
706
+ .array(z.object({ email: z.string().email('Email không hợp lệ') }))
707
+ .min(1, 'Cần ít nhất 1 email'),
708
+ });
709
+ type InviteForm = z.infer<typeof schema>;
710
+ type EmailRow = InviteForm['emails'][number];
711
+
712
+ export function DynamicEmailsForm({ onInvite }: { onInvite: (emails: string[]) => Promise<void> }) {
713
+ const form = useForm<InviteForm>({ schema, mode: 'onChange', initialValues: { emails: [{ email: '' }] } });
714
+ const arr = useFieldArray<InviteForm, 'emails', EmailRow>(form, 'emails');
715
+ const [sending, setSending] = useState(false);
716
+
717
+ return (
718
+ <form
719
+ className="flex w-96 flex-col gap-2"
720
+ onSubmit={form.handleSubmit(async (d) => {
721
+ setSending(true);
722
+ try {
723
+ await onInvite(d.emails.map((r) => r.email));
724
+ } finally {
725
+ setSending(false);
726
+ }
727
+ })}
728
+ >
729
+ {arr.fields.map((row, i) => (
730
+ <Field key={i} label={i === 0 ? 'Email người nhận' : undefined} htmlFor={`inv-${i}`}
731
+ error={form.errors[`emails.${i}.email`]}>
732
+ <div className="flex gap-2">
733
+ <Input id={`inv-${i}`} name={arr.nameAt(i, 'email')} placeholder="name@company.vn"
734
+ value={row.email} invalid={!!form.errors[`emails.${i}.email`]}
735
+ onChange={(e) => arr.update(i, { email: e.target.value })} />
736
+ <Button type="text" aria-label={`Xoá email ${i + 1}`} onClick={() => arr.remove(i)}>×</Button>
737
+ </div>
738
+ </Field>
739
+ ))}
740
+ <div className="flex items-center gap-2">
741
+ <Button type="default" onClick={() => arr.append({ email: '' })}>+ Thêm email</Button>
742
+ <Button type="text" disabled={arr.count < 2} onClick={() => arr.swap(0, arr.count - 1)}>Đảo đầu–cuối</Button>
743
+ <Button htmlType="submit" type="primary" loading={sending}>Gửi mời</Button>
744
+ </div>
745
+ </form>
746
+ );
747
+ }
748
+ ```
749
+
750
+ Ghi chú: **path lồng** — zod issues tự map thành `errors['emails.<i>.email']` (useForm join
751
+ `'.'`), `Field error=` đọc đúng key đó. **Lỗi đi theo row**: mỗi op cấu trúc (`append`/
752
+ `remove`/`move`/`swap`/`update`) rebuild errors từ snapshot mới — xoá row giữa thì lỗi dồn
753
+ index đúng. **`mode: 'onChange'`** để validate từng row khi gõ (lỗi tự tắt khi sửa đúng);
754
+ `nameAt(i, key)` đặt name attr → submit sai tự focus ô đầu tiên. Mỗi event chỉ gọi **1 op**
755
+ (hook thuần closure — 2 op liên tiếp trong 1 handler thì op sau thấy state cũ). Reorder
756
+ nhiều row: `move(from, to)` là insert-at kiểu RHF; `swap` đổi chỗ 2 row.
757
+
758
+ ---
759
+
760
+ ## 18. NavAppĐaCấp — Menubar vs MegaMenu vs SlideMenu: khi nào cái nào
761
+
762
+ Ba mức nav ngang — chọn theo ĐỘ SÂU + ĐỘ RỘNG nội dung:
763
+
764
+ | | `Menubar` | `MegaMenu` | `SlideMenu` |
765
+ |---|---|---|---|
766
+ | Cấu trúc | dropdown **1 cấp** gọn | panel **multi-column** (group = cột → leaf) | các **cấp trượt ngang**, đệ quy không giới hạn |
767
+ | Khi nào | app header ít mục, mỗi mục vài action | landing/app header nhiều NHÓM chức năng (sản phẩm, tài nguyên, quản trị) | panel hẹp / mobile / hamburger / cây phân loại sâu |
768
+ | Mở/đóng | hover | hover HOẶC click · ESC/click-ngoài/leave | click vào cấp · back bar / ESC |
769
+ | Không dùng khi | cần panel nhóm nhiều cột → MegaMenu | không gian hẹp (mobile) → SlideMenu | top bar desktop nhiều cột song song → MegaMenu |
770
+
771
+ ```tsx
772
+ import { useState } from 'react';
773
+ import { Card, MegaMenu, SlideMenu, type MegaMenuItem, type SlideMenuItem } from '@wakii/ui-kit';
774
+
775
+ const MEGA: MegaMenuItem[] = [
776
+ {
777
+ key: 'product',
778
+ label: 'Sản phẩm',
779
+ children: [
780
+ {
781
+ key: 'work',
782
+ label: 'Workspace',
783
+ items: [
784
+ { key: 'board', label: 'Board' },
785
+ { key: 'list', label: 'List view' },
786
+ ],
787
+ },
788
+ {
789
+ key: 'insight',
790
+ label: 'Insights',
791
+ items: [
792
+ { key: 'charts', label: 'Charts' },
793
+ { key: 'reports', label: 'Reports' },
794
+ ],
795
+ },
796
+ ],
797
+ },
798
+ { key: 'pricing', label: 'Giá', onClick: () => console.log('pricing') },
799
+ ];
800
+
801
+ const SLIDE: SlideMenuItem[] = [
802
+ {
803
+ key: 'projects',
804
+ label: 'Dự án',
805
+ children: [
806
+ {
807
+ key: 'active',
808
+ label: 'Đang chạy',
809
+ children: [{ key: 'vu53', label: 'VU-53 redesign' }],
810
+ },
811
+ { key: 'archived', label: 'Lưu trữ' },
812
+ ],
813
+ },
814
+ { key: 'settings', label: 'Cài đặt' },
815
+ ];
816
+
817
+ export function NavAppDaCap({ isMobile }: { isMobile: boolean }) {
818
+ const [lastPick, setLastPick] = useState('—');
819
+ return isMobile ? (
820
+ // Mobile/panel hẹp: SlideMenu — cấp sâu trong footer drawer
821
+ <Card title="Menu">
822
+ <SlideMenu items={SLIDE} ariaLabel="Menu chính" className="w-full" />
823
+ </Card>
824
+ ) : (
825
+ // Desktop: MegaMenu làm top bar — nhóm chức năng duyệt theo cột
826
+ <Card title="Sản phẩm & more">
827
+ <MegaMenu
828
+ items={MEGA.map((m) =>
829
+ m.key === 'pricing'
830
+ ? { ...m, onClick: () => setLastPick('pricing') }
831
+ : m,
832
+ )}
833
+ className="rounded-lg"
834
+ />
835
+ <p className="m-0 mt-3 text-meta text-ink3">Đã chọn: {lastPick}</p>
836
+ </Card>
837
+ );
838
+ }
839
+ ```
840
+
841
+ Ghi chú: cả 3 cùng ngôn ngữ nav — item `key`/`label`/`onClick`, `danger` đỏ, đóng bằng ESC.
842
+ MegaMenu panel là `role=menu` + `role=menuitem`, trigger có `aria-expanded`; SlideMenu trang ẩn
843
+ `inert` + `aria-hidden` (không phá tab order), back bar kèm nhãn cấp cha. Menubar/MegaMenu mở
844
+ theo hover (desktop) — với touch cứ click. Cần nav đa cấp DỌC trong sidebar → dùng `Menu`
845
+ (đa cấp) hoặc `Sidebar` + `MenuCompactContext`, không nhét SlideMenu vào sidebar.
846
+
847
+ ## 19. MotionComposable — Transition + Reveal + Parallax cộng hưởng trên landing
848
+
849
+ ```tsx
850
+ import { useState } from 'react';
851
+ import { Button, CollapseTransition, CountUp, Parallax, Reveal, Row, Col, Statistic } from '@wakii/ui-kit';
852
+
853
+ // Mỗi motion primitiveOWN đúng 1 trục, không giành transform trên CÙNG element:
854
+ // Parallax = scroll · Reveal = viewport-enter · Transition = state in/out.
855
+ // Kết hợp bằng CÁCH BỌC NESTED (mỗi primitive một element riêng).
856
+
857
+ export function LandingMotionComposable() {
858
+ const [openFeature, setOpenFeature] = useState(false);
859
+
860
+ return (
861
+ <div className="flex flex-col gap-12">
862
+ {/* 1) Hero — Parallax trôi chậm khi scroll (self-tắt khi reduced-motion) */}
863
+ <Parallax speed={0.3} scale className="overflow-hidden rounded-xl">
864
+ <img src="/hero.jpg" alt="Product hero" className="aspect-video w-full object-cover" />
865
+ </Parallax>
866
+
867
+ {/* 2) KPI — Reveal stagger khi cuộn tới + CountUp đếm ease-out */}
868
+ <Row gutter={[24, 24]}>
869
+ {[
870
+ { title: 'Tasks shipped', value: 1284, suffix: '' },
871
+ { title: 'Uptime', value: 99.95, decimals: 2, suffix: '%' },
872
+ { title: 'Teams onboard', value: 327, suffix: '+' },
873
+ ].map((kpi, i) => (
874
+ <Col key={kpi.title} xs={24} md={8}>
875
+ <Reveal direction="up" delay={i * 150}>
876
+ <div className="h-full rounded-xl border border-line bg-s1 p-5">
877
+ <Statistic
878
+ title={kpi.title}
879
+ value={<CountUp value={kpi.value} decimals={kpi.decimals} />}
880
+ suffix={kpi.suffix}
881
+ />
882
+ </div>
883
+ </Reveal>
884
+ </Col>
885
+ ))}
886
+ </Row>
887
+
888
+ {/* 3) Feature card — Reveal đưa card vào, CollapseTransition mở details (state do user) */}
889
+ <Reveal direction="left" className="max-w-xl">
890
+ <div className="rounded-xl border border-line bg-s1 p-5">
891
+ <h3 className="m-0 text-h3 font-semibold text-ink">Zero-config motion</h3>
892
+ <p className="mt-1 text-body text-ink2">
893
+ Scroll-reveal từ Reveal, chiều cao đàn hồi từ CollapseTransition — hai primitive,
894
+ hai element, không xung đột transform.
895
+ </p>
896
+ <Button size="small" onClick={() => setOpenFeature((o) => !o)}>
897
+ {openFeature ? 'Hide details' : 'Show details'}
898
+ </Button>
899
+ {/* padding nằm trong children — tầng overflow-hidden phải clip sạch */}
900
+ <CollapseTransition in={openFeature}>
901
+ <p className="m-0 pt-3 text-body text-ink2">
902
+ CollapseTransition unmount sau khi exit animation chạy xong — DOM sạch,
903
+ không banner ẩn chiếm chỗ.
904
+ </p>
905
+ </CollapseTransition>
906
+ </div>
907
+ </Reveal>
908
+ </div>
909
+ );
910
+ }
911
+ ```
912
+
913
+ Ghi chú: quy tắc **composable** — (1) mỗi primitive bọc một element riêng, KHÔNG gộp 2 hiệu ứng lên cùng div (transform/opacity ghi đè nhau); (2) `Reveal` quản entrance, `Transition`/`CollapseTransition` quản state in/out do user, `Parallax` quản scroll — trục nào về nó; (3) reduced-motion tắt cả ba nhờ block global (`Parallax` còn tự ngắt listener qua `useMediaQuery`). `CollapseTransition` là tên barrel của `Collapse` trong `ui/transition` (trùng tên `Collapse` — xem AGENTS.md nhóm Theming & Motion).
914
+
915
+ ---
916
+
917
+
918
+ ## Bảng "khi nào dùng" — nhóm Primitives + Navigation
919
+
920
+ | Component | Khi nào dùng | Đừng dùng khi |
921
+ |---|---|---|
922
+ | `Alert` | Thông báo ngữ cảnh nằm trong trang (filled mặc định) | toast tạm → dùng `message` |
923
+ | `Avatar` / `AvatarGroup` | Đại diện user; nhóm chồng + max "+N" | — |
924
+ | `Badge` | Số đếm/ trạng thái nhỏ trên icon, pill delta | nội dung dài → Tag |
925
+ | `BusyBox` | Bọc khối đang loading (thay Skeleton thủ công) | loading cả trang → Skeleton page |
926
+ | `Button` | Mọi action; `htmlType` mới là type HTML; `loading` chặn double-submit | link điều hướng → `<Link>`/nav |
927
+ | `Breadcrumb` | Đường phân cấp trang chi tiết | nav chính → Menu/Tabs |
928
+ | `Card` | Khối nội dung có viền/đề (anchor: rounded + title/extra) | layout vô hình → div |
929
+ | `ContextMenu` | Menu chuột phải | menu thường → Dropdown/Menu |
930
+ | `Divider` | Phân cách nội dung (ngang/dọc, text, dashed) | — |
931
+ | `Dropdown` | Menu gắn trigger bất kỳ; `---` làm separator | menu đa cấp sidebar → Menu |
932
+ | `EmptyState` | Danh sách/region rỗng (title + action) | lỗi → Result |
933
+ | `Icon` | Mọi glyph (48 tên primeicons) | — |
934
+ | `Image` | Ảnh có preview overlay | avatar → Avatar |
935
+ | `Input` / `Textarea` | Ô nhập; `invalid` + `Field` = form | số → InputNumber |
936
+ | `List` | Danh sách item + loading/empty/pagination | bảng dữ liệu → Table |
937
+ | `Menu` | Nav đa cấp sidebar (vertical) / thanh (horizontal) | action đơn → Dropdown |
938
+ | `Menubar` | Thanh menu desktop hover | trong trang → Tabs |
939
+ | `Modal` | Xác nhận/form chặn tương tác | phi-chặn → Drawer/Popconfirm |
940
+ | `Pagination` | Phân trang client-side | infinite scroll |
941
+ | `Popconfirm` | Xác nhận nhanh trước action nguy hiểm | form dài → Modal |
942
+ | `Segmented` | Đổi view cùng ngữ cảnh | nav routing → Tabs/TabMenu |
943
+ | `Sidebar` | Khung app chính, collapse icon-only | landing → nav thường |
944
+ | `Skeleton` | Placeholder trước khi có data | — |
945
+ | `Space` | Gap flex nhanh | layout phức tạp → grid |
946
+ | `Spinner` | Loading điểm lẻ | khối → Skeleton/BusyBox |
947
+ | `SplitButton` | Action chính + biến thể phụ | 2 action ngang hàng → 2 Button |
948
+ | `Steps` | Tiến trình nhiều bước | trạng thái đơn → Tag/Progress |
949
+ | `TabMenu` | Nav dạng tab (đổi view) | nội dung trong tab → Tabs |
950
+ | `Tabs` | Chia nội dung cùng trang | nav routing → TabMenu |
951
+ | `Tag` | Nhãn trạng thái/phân loại | số đếm → Badge |
952
+ | `Timeline` | Chuỗi sự kiện theo thời gian | — |
953
+ | `Toolbar` | Cụm công cụ trên cùng hàng | — |
954
+ | `Tooltip` | Hint khi hover/focus icon | nội dung dài → Popover (backlog) |
955
+
956
+ *Nhóm Forms / Data Display / Feedback — worker khác bổ sung sau.*