@btst/stack 2.6.2 → 2.8.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 (309) hide show
  1. package/README.md +1 -0
  2. package/dist/api/index.d.cts +2 -2
  3. package/dist/api/index.d.mts +2 -2
  4. package/dist/api/index.d.ts +2 -2
  5. package/dist/client/index.d.cts +2 -2
  6. package/dist/client/index.d.mts +2 -2
  7. package/dist/client/index.d.ts +2 -2
  8. package/dist/components/auto-form/index.d.cts +2 -2
  9. package/dist/components/auto-form/index.d.mts +2 -2
  10. package/dist/components/auto-form/index.d.ts +2 -2
  11. package/dist/components/form-builder/index.d.cts +1 -1
  12. package/dist/components/form-builder/index.d.mts +1 -1
  13. package/dist/components/form-builder/index.d.ts +1 -1
  14. package/dist/components/stepped-auto-form/index.d.cts +1 -1
  15. package/dist/components/stepped-auto-form/index.d.mts +1 -1
  16. package/dist/components/stepped-auto-form/index.d.ts +1 -1
  17. package/dist/index.d.cts +1 -1
  18. package/dist/index.d.mts +1 -1
  19. package/dist/index.d.ts +1 -1
  20. package/dist/packages/stack/src/plugins/blog/client/components/loading/post-navigation-skeleton.cjs +13 -0
  21. package/dist/packages/stack/src/plugins/blog/client/components/loading/post-navigation-skeleton.mjs +11 -0
  22. package/dist/packages/stack/src/plugins/blog/client/components/loading/recent-posts-carousel-skeleton.cjs +17 -0
  23. package/dist/packages/stack/src/plugins/blog/client/components/loading/recent-posts-carousel-skeleton.mjs +15 -0
  24. package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.internal.cjs +18 -7
  25. package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.internal.mjs +18 -7
  26. package/dist/packages/stack/src/plugins/blog/client/components/shared/post-navigation.cjs +48 -52
  27. package/dist/packages/stack/src/plugins/blog/client/components/shared/post-navigation.mjs +49 -53
  28. package/dist/packages/stack/src/plugins/blog/client/components/shared/recent-posts-carousel.cjs +34 -37
  29. package/dist/packages/stack/src/plugins/blog/client/components/shared/recent-posts-carousel.mjs +35 -38
  30. package/dist/packages/stack/src/plugins/blog/client/hooks/blog-hooks.cjs +4 -21
  31. package/dist/packages/stack/src/plugins/blog/client/hooks/blog-hooks.mjs +4 -21
  32. package/dist/packages/stack/src/plugins/comments/api/getters.cjs +284 -0
  33. package/dist/packages/stack/src/plugins/comments/api/getters.mjs +280 -0
  34. package/dist/packages/stack/src/plugins/comments/api/mutations.cjs +118 -0
  35. package/dist/packages/stack/src/plugins/comments/api/mutations.mjs +112 -0
  36. package/dist/packages/stack/src/plugins/comments/api/plugin.cjs +335 -0
  37. package/dist/packages/stack/src/plugins/comments/api/plugin.mjs +333 -0
  38. package/dist/packages/stack/src/plugins/comments/api/query-key-defs.cjs +60 -0
  39. package/dist/packages/stack/src/plugins/comments/api/query-key-defs.mjs +55 -0
  40. package/dist/packages/stack/src/plugins/comments/api/serializers.cjs +23 -0
  41. package/dist/packages/stack/src/plugins/comments/api/serializers.mjs +21 -0
  42. package/dist/packages/stack/src/plugins/comments/client/components/comment-count.cjs +46 -0
  43. package/dist/packages/stack/src/plugins/comments/client/components/comment-count.mjs +44 -0
  44. package/dist/packages/stack/src/plugins/comments/client/components/comment-form.cjs +86 -0
  45. package/dist/packages/stack/src/plugins/comments/client/components/comment-form.mjs +84 -0
  46. package/dist/packages/stack/src/plugins/comments/client/components/comment-thread.cjs +540 -0
  47. package/dist/packages/stack/src/plugins/comments/client/components/comment-thread.mjs +538 -0
  48. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.cjs +64 -0
  49. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.internal.cjs +426 -0
  50. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.internal.mjs +424 -0
  51. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.mjs +62 -0
  52. package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.cjs +66 -0
  53. package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.internal.cjs +256 -0
  54. package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.internal.mjs +254 -0
  55. package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.mjs +64 -0
  56. package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.cjs +86 -0
  57. package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.internal.cjs +191 -0
  58. package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.internal.mjs +189 -0
  59. package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.mjs +84 -0
  60. package/dist/packages/stack/src/plugins/comments/client/components/shared/page-wrapper.cjs +27 -0
  61. package/dist/packages/stack/src/plugins/comments/client/components/shared/page-wrapper.mjs +25 -0
  62. package/dist/packages/stack/src/plugins/comments/client/components/shared/pagination.cjs +37 -0
  63. package/dist/packages/stack/src/plugins/comments/client/components/shared/pagination.mjs +35 -0
  64. package/dist/packages/stack/src/plugins/comments/client/hooks/use-comments.cjs +476 -0
  65. package/dist/packages/stack/src/plugins/comments/client/hooks/use-comments.mjs +464 -0
  66. package/dist/packages/stack/src/plugins/comments/client/localization/comments-moderation.cjs +67 -0
  67. package/dist/packages/stack/src/plugins/comments/client/localization/comments-moderation.mjs +65 -0
  68. package/dist/packages/stack/src/plugins/comments/client/localization/comments-my.cjs +27 -0
  69. package/dist/packages/stack/src/plugins/comments/client/localization/comments-my.mjs +25 -0
  70. package/dist/packages/stack/src/plugins/comments/client/localization/comments-thread.cjs +30 -0
  71. package/dist/packages/stack/src/plugins/comments/client/localization/comments-thread.mjs +28 -0
  72. package/dist/packages/stack/src/plugins/comments/client/localization/index.cjs +13 -0
  73. package/dist/packages/stack/src/plugins/comments/client/localization/index.mjs +11 -0
  74. package/dist/packages/stack/src/plugins/comments/client/plugin.cjs +116 -0
  75. package/dist/packages/stack/src/plugins/comments/client/plugin.mjs +114 -0
  76. package/dist/packages/stack/src/plugins/comments/client/utils.cjs +41 -0
  77. package/dist/packages/stack/src/plugins/comments/client/utils.mjs +37 -0
  78. package/dist/packages/stack/src/plugins/comments/db.cjs +75 -0
  79. package/dist/packages/stack/src/plugins/comments/db.mjs +73 -0
  80. package/dist/packages/stack/src/plugins/comments/schemas.cjs +45 -0
  81. package/dist/packages/stack/src/plugins/comments/schemas.mjs +38 -0
  82. package/dist/packages/stack/src/plugins/kanban/api/plugin.cjs +5 -4
  83. package/dist/packages/stack/src/plugins/kanban/api/plugin.mjs +5 -4
  84. package/dist/packages/stack/src/plugins/kanban/client/components/forms/task-form.cjs +0 -1
  85. package/dist/packages/stack/src/plugins/kanban/client/components/forms/task-form.mjs +0 -1
  86. package/dist/packages/stack/src/plugins/kanban/client/components/pages/board-page.internal.cjs +39 -22
  87. package/dist/packages/stack/src/plugins/kanban/client/components/pages/board-page.internal.mjs +40 -23
  88. package/dist/packages/ui/src/components/avatar.mjs +1 -1
  89. package/dist/packages/ui/src/components/pagination-controls.cjs +64 -0
  90. package/dist/packages/ui/src/components/pagination-controls.mjs +62 -0
  91. package/dist/packages/ui/src/components/when-visible.cjs +39 -0
  92. package/dist/packages/ui/src/components/when-visible.mjs +37 -0
  93. package/dist/plugins/ai-chat/api/index.d.cts +4 -6
  94. package/dist/plugins/ai-chat/api/index.d.mts +4 -6
  95. package/dist/plugins/ai-chat/api/index.d.ts +4 -6
  96. package/dist/plugins/ai-chat/client/hooks/index.d.cts +1 -3
  97. package/dist/plugins/ai-chat/client/hooks/index.d.mts +1 -3
  98. package/dist/plugins/ai-chat/client/hooks/index.d.ts +1 -3
  99. package/dist/plugins/ai-chat/query-keys.d.cts +1 -3
  100. package/dist/plugins/ai-chat/query-keys.d.mts +1 -3
  101. package/dist/plugins/ai-chat/query-keys.d.ts +1 -3
  102. package/dist/plugins/api/index.d.cts +3 -3
  103. package/dist/plugins/api/index.d.mts +3 -3
  104. package/dist/plugins/api/index.d.ts +3 -3
  105. package/dist/plugins/blog/api/index.d.cts +3 -3
  106. package/dist/plugins/blog/api/index.d.mts +3 -3
  107. package/dist/plugins/blog/api/index.d.ts +3 -3
  108. package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
  109. package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
  110. package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
  111. package/dist/plugins/blog/client/index.d.cts +25 -3
  112. package/dist/plugins/blog/client/index.d.mts +25 -3
  113. package/dist/plugins/blog/client/index.d.ts +25 -3
  114. package/dist/plugins/blog/query-keys.d.cts +3 -3
  115. package/dist/plugins/blog/query-keys.d.mts +3 -3
  116. package/dist/plugins/blog/query-keys.d.ts +3 -3
  117. package/dist/plugins/client/index.d.cts +2 -2
  118. package/dist/plugins/client/index.d.mts +2 -2
  119. package/dist/plugins/client/index.d.ts +2 -2
  120. package/dist/plugins/cms/api/index.d.cts +1 -1
  121. package/dist/plugins/cms/api/index.d.mts +1 -1
  122. package/dist/plugins/cms/api/index.d.ts +1 -1
  123. package/dist/plugins/cms/client/index.d.cts +1 -1
  124. package/dist/plugins/cms/client/index.d.mts +1 -1
  125. package/dist/plugins/cms/client/index.d.ts +1 -1
  126. package/dist/plugins/cms/query-keys.d.cts +1 -1
  127. package/dist/plugins/cms/query-keys.d.mts +1 -1
  128. package/dist/plugins/cms/query-keys.d.ts +1 -1
  129. package/dist/plugins/comments/api/index.cjs +21 -0
  130. package/dist/plugins/comments/api/index.d.cts +126 -0
  131. package/dist/plugins/comments/api/index.d.mts +126 -0
  132. package/dist/plugins/comments/api/index.d.ts +126 -0
  133. package/dist/plugins/comments/api/index.mjs +5 -0
  134. package/dist/plugins/comments/client/components/index.cjs +15 -0
  135. package/dist/plugins/comments/client/components/index.d.cts +125 -0
  136. package/dist/plugins/comments/client/components/index.d.mts +125 -0
  137. package/dist/plugins/comments/client/components/index.d.ts +125 -0
  138. package/dist/plugins/comments/client/components/index.mjs +5 -0
  139. package/dist/plugins/comments/client/hooks/index.cjs +17 -0
  140. package/dist/plugins/comments/client/hooks/index.d.cts +200 -0
  141. package/dist/plugins/comments/client/hooks/index.d.mts +200 -0
  142. package/dist/plugins/comments/client/hooks/index.d.ts +200 -0
  143. package/dist/plugins/comments/client/hooks/index.mjs +1 -0
  144. package/dist/plugins/comments/client/index.cjs +9 -0
  145. package/dist/plugins/comments/client/index.d.cts +262 -0
  146. package/dist/plugins/comments/client/index.d.mts +262 -0
  147. package/dist/plugins/comments/client/index.d.ts +262 -0
  148. package/dist/plugins/comments/client/index.mjs +2 -0
  149. package/dist/plugins/comments/client.css +2 -0
  150. package/dist/plugins/comments/query-keys.cjs +113 -0
  151. package/dist/plugins/comments/query-keys.d.cts +71 -0
  152. package/dist/plugins/comments/query-keys.d.mts +71 -0
  153. package/dist/plugins/comments/query-keys.d.ts +71 -0
  154. package/dist/plugins/comments/query-keys.mjs +111 -0
  155. package/dist/plugins/comments/style.css +15 -0
  156. package/dist/plugins/form-builder/api/index.d.cts +2 -2
  157. package/dist/plugins/form-builder/api/index.d.mts +2 -2
  158. package/dist/plugins/form-builder/api/index.d.ts +2 -2
  159. package/dist/plugins/form-builder/client/components/index.d.cts +1 -1
  160. package/dist/plugins/form-builder/client/components/index.d.mts +1 -1
  161. package/dist/plugins/form-builder/client/components/index.d.ts +1 -1
  162. package/dist/plugins/form-builder/client/index.d.cts +1 -1
  163. package/dist/plugins/form-builder/client/index.d.mts +1 -1
  164. package/dist/plugins/form-builder/client/index.d.ts +1 -1
  165. package/dist/plugins/form-builder/query-keys.d.cts +1 -1
  166. package/dist/plugins/form-builder/query-keys.d.mts +1 -1
  167. package/dist/plugins/form-builder/query-keys.d.ts +1 -1
  168. package/dist/plugins/kanban/api/index.d.cts +2 -2
  169. package/dist/plugins/kanban/api/index.d.mts +2 -2
  170. package/dist/plugins/kanban/api/index.d.ts +2 -2
  171. package/dist/plugins/kanban/client/hooks/index.d.cts +1 -1
  172. package/dist/plugins/kanban/client/hooks/index.d.mts +1 -1
  173. package/dist/plugins/kanban/client/hooks/index.d.ts +1 -1
  174. package/dist/plugins/kanban/client/index.d.cts +1 -1
  175. package/dist/plugins/kanban/client/index.d.mts +1 -1
  176. package/dist/plugins/kanban/client/index.d.ts +1 -1
  177. package/dist/plugins/kanban/query-keys.d.cts +2 -2
  178. package/dist/plugins/kanban/query-keys.d.mts +2 -2
  179. package/dist/plugins/kanban/query-keys.d.ts +2 -2
  180. package/dist/plugins/open-api/api/index.d.cts +3 -3
  181. package/dist/plugins/open-api/api/index.d.mts +3 -3
  182. package/dist/plugins/open-api/api/index.d.ts +3 -3
  183. package/dist/plugins/route-docs/client/index.d.cts +1 -1
  184. package/dist/plugins/route-docs/client/index.d.mts +1 -1
  185. package/dist/plugins/route-docs/client/index.d.ts +1 -1
  186. package/dist/plugins/ui-builder/client/components/index.d.cts +2 -2
  187. package/dist/plugins/ui-builder/client/components/index.d.mts +2 -2
  188. package/dist/plugins/ui-builder/client/components/index.d.ts +2 -2
  189. package/dist/plugins/ui-builder/client/hooks/index.d.cts +3 -3
  190. package/dist/plugins/ui-builder/client/hooks/index.d.mts +3 -3
  191. package/dist/plugins/ui-builder/client/hooks/index.d.ts +3 -3
  192. package/dist/plugins/ui-builder/client/index.d.cts +3 -3
  193. package/dist/plugins/ui-builder/client/index.d.mts +3 -3
  194. package/dist/plugins/ui-builder/client/index.d.ts +3 -3
  195. package/dist/plugins/ui-builder/index.d.cts +3 -3
  196. package/dist/plugins/ui-builder/index.d.mts +3 -3
  197. package/dist/plugins/ui-builder/index.d.ts +3 -3
  198. package/dist/shared/{stack.B1srlBud.d.mts → stack.BFoBvGML.d.mts} +1 -1
  199. package/dist/shared/{stack.DmpPDPxA.d.cts → stack.BOCvd9HK.d.cts} +1 -1
  200. package/dist/shared/{stack.n1_i1p2B.d.cts → stack.BOokfhZD.d.cts} +170 -110
  201. package/dist/shared/{stack.DXnclTG7.d.ts → stack.BSqJrCTM.d.cts} +120 -59
  202. package/dist/shared/{stack.B58oHdqm.d.mts → stack.BX7MHi0J.d.mts} +90 -45
  203. package/dist/shared/{stack.cfCkioTe.d.mts → stack.BXxrFL9R.d.ts} +120 -59
  204. package/dist/shared/{stack.CSx98K5H.d.cts → stack.BYN8wCV6.d.cts} +87 -58
  205. package/dist/shared/{stack.FVWf2JhZ.d.mts → stack.BgQrdSlo.d.mts} +60 -45
  206. package/dist/shared/{stack.BK9Z2dcL.d.ts → stack.BmMB0LNC.d.ts} +1 -1
  207. package/dist/shared/{stack.j75TpKh2.d.ts → stack.BvCR4-9H.d.ts} +170 -110
  208. package/dist/shared/{stack.FeaWkglm.d.ts → stack.BxFl46lB.d.cts} +24 -1
  209. package/dist/shared/stack.C-b3Sn8j.d.cts +142 -0
  210. package/dist/shared/stack.C-b3Sn8j.d.mts +142 -0
  211. package/dist/shared/stack.C-b3Sn8j.d.ts +142 -0
  212. package/dist/shared/{stack.CFECM0ew.d.cts → stack.C1nXGBr6.d.cts} +1 -1
  213. package/dist/shared/{stack.C9Mg2Q46.d.cts → stack.C9zoS1TN.d.cts} +90 -45
  214. package/dist/shared/stack.CJE9sAjV.d.ts +335 -0
  215. package/dist/shared/{stack.fdi94T4S.d.mts → stack.CPsYC2-Z.d.cts} +7 -7
  216. package/dist/shared/{stack.fdi94T4S.d.ts → stack.CPsYC2-Z.d.mts} +7 -7
  217. package/dist/shared/{stack.fdi94T4S.d.cts → stack.CPsYC2-Z.d.ts} +7 -7
  218. package/dist/shared/{stack.7n9Y_u7N.d.cts → stack.CQnwAN7x.d.cts} +6 -6
  219. package/dist/shared/{stack.7n9Y_u7N.d.mts → stack.CQnwAN7x.d.mts} +6 -6
  220. package/dist/shared/{stack.7n9Y_u7N.d.ts → stack.CQnwAN7x.d.ts} +6 -6
  221. package/dist/shared/{stack.CxaFNQCV.d.mts → stack.CWxAl9K3.d.mts} +170 -110
  222. package/dist/shared/{stack.D-b5zbPm.d.cts → stack.Cbsrl06u.d.cts} +60 -45
  223. package/dist/shared/stack.CmHRdhl8.d.cts +335 -0
  224. package/dist/shared/{stack.BgTmujxW.d.mts → stack.D88yU4FT.d.mts} +87 -58
  225. package/dist/shared/{stack.DVtk5CNw.d.mts → stack.DLPa6Gzm.d.mts} +1 -1
  226. package/dist/shared/{stack.BAT540yW.d.ts → stack.DOZ1EXjM.d.mts} +9 -15
  227. package/dist/shared/{stack.FeaWkglm.d.mts → stack.DRpeDS6X.d.ts} +24 -1
  228. package/dist/shared/{stack.B8vT-Yt4.d.mts → stack.DX-tQ93o.d.cts} +9 -15
  229. package/dist/shared/stack.Dcz6636A.d.mts +335 -0
  230. package/dist/shared/{stack.ASwEoINr.d.ts → stack.DxJ-tHLt.d.ts} +1 -1
  231. package/dist/shared/{stack.DaZM10cp.d.cts → stack.DzOhpIYM.d.mts} +120 -59
  232. package/dist/shared/{stack.CTDVxbrA.d.ts → stack.Fl2Kl_bt.d.ts} +60 -45
  233. package/dist/shared/{stack.FeaWkglm.d.cts → stack.Jb0kQDJC.d.mts} +24 -1
  234. package/dist/shared/stack.Ldfkr5b2.d.cts +112 -0
  235. package/dist/shared/stack.Ldfkr5b2.d.mts +112 -0
  236. package/dist/shared/stack.Ldfkr5b2.d.ts +112 -0
  237. package/dist/shared/{stack.CLQuVdwK.d.ts → stack.RuQ9JCLo.d.ts} +87 -58
  238. package/dist/shared/{stack.BwA7trxA.d.cts → stack.VF6FhyZw.d.ts} +9 -15
  239. package/dist/shared/{stack.sO33ZDhK.d.ts → stack.fQjVhw5a.d.ts} +90 -45
  240. package/package.json +70 -5
  241. package/src/__tests__/plugins.test.tsx +5 -1
  242. package/src/__tests__/stack-api.test.ts +1 -1
  243. package/src/plugins/ai-chat/__tests__/getters.test.ts +1 -1
  244. package/src/plugins/ai-chat/api/getters.ts +1 -1
  245. package/src/plugins/ai-chat/api/plugin.ts +1 -1
  246. package/src/plugins/api/index.ts +5 -1
  247. package/src/plugins/blog/__tests__/getters.test.ts +1 -1
  248. package/src/plugins/blog/api/getters.ts +1 -1
  249. package/src/plugins/blog/api/plugin.ts +1 -1
  250. package/src/plugins/blog/client/components/loading/post-navigation-skeleton.tsx +10 -0
  251. package/src/plugins/blog/client/components/loading/recent-posts-carousel-skeleton.tsx +18 -0
  252. package/src/plugins/blog/client/components/pages/post-page.internal.tsx +23 -8
  253. package/src/plugins/blog/client/components/shared/post-navigation.tsx +0 -5
  254. package/src/plugins/blog/client/components/shared/recent-posts-carousel.tsx +1 -5
  255. package/src/plugins/blog/client/hooks/blog-hooks.tsx +8 -33
  256. package/src/plugins/blog/client/overrides.ts +26 -1
  257. package/src/plugins/cms/__tests__/getters.test.ts +1 -1
  258. package/src/plugins/cms/api/getters.ts +1 -1
  259. package/src/plugins/cms/api/mutations.ts +1 -1
  260. package/src/plugins/cms/api/plugin.ts +1 -1
  261. package/src/plugins/cms/client/components/shared/pagination.tsx +14 -42
  262. package/src/plugins/comments/api/getters.ts +444 -0
  263. package/src/plugins/comments/api/index.ts +21 -0
  264. package/src/plugins/comments/api/mutations.ts +206 -0
  265. package/src/plugins/comments/api/plugin.ts +628 -0
  266. package/src/plugins/comments/api/query-key-defs.ts +143 -0
  267. package/src/plugins/comments/api/serializers.ts +37 -0
  268. package/src/plugins/comments/client/components/comment-count.tsx +66 -0
  269. package/src/plugins/comments/client/components/comment-form.tsx +112 -0
  270. package/src/plugins/comments/client/components/comment-thread.tsx +799 -0
  271. package/src/plugins/comments/client/components/index.tsx +11 -0
  272. package/src/plugins/comments/client/components/pages/moderation-page.internal.tsx +550 -0
  273. package/src/plugins/comments/client/components/pages/moderation-page.tsx +70 -0
  274. package/src/plugins/comments/client/components/pages/my-comments-page.internal.tsx +367 -0
  275. package/src/plugins/comments/client/components/pages/my-comments-page.tsx +72 -0
  276. package/src/plugins/comments/client/components/pages/resource-comments-page.internal.tsx +225 -0
  277. package/src/plugins/comments/client/components/pages/resource-comments-page.tsx +97 -0
  278. package/src/plugins/comments/client/components/shared/page-wrapper.tsx +32 -0
  279. package/src/plugins/comments/client/components/shared/pagination.tsx +44 -0
  280. package/src/plugins/comments/client/hooks/index.tsx +13 -0
  281. package/src/plugins/comments/client/hooks/use-comments.tsx +717 -0
  282. package/src/plugins/comments/client/index.ts +14 -0
  283. package/src/plugins/comments/client/localization/comments-moderation.ts +75 -0
  284. package/src/plugins/comments/client/localization/comments-my.ts +32 -0
  285. package/src/plugins/comments/client/localization/comments-thread.ts +32 -0
  286. package/src/plugins/comments/client/localization/index.ts +11 -0
  287. package/src/plugins/comments/client/overrides.ts +164 -0
  288. package/src/plugins/comments/client/plugin.tsx +195 -0
  289. package/src/plugins/comments/client/utils.ts +67 -0
  290. package/src/plugins/comments/client.css +2 -0
  291. package/src/plugins/comments/db.ts +77 -0
  292. package/src/plugins/comments/query-keys.ts +189 -0
  293. package/src/plugins/comments/schemas.ts +72 -0
  294. package/src/plugins/comments/style.css +15 -0
  295. package/src/plugins/comments/types.ts +73 -0
  296. package/src/plugins/form-builder/__tests__/getters.test.ts +1 -1
  297. package/src/plugins/form-builder/api/getters.ts +1 -1
  298. package/src/plugins/form-builder/api/plugin.ts +1 -1
  299. package/src/plugins/kanban/__tests__/getters.test.ts +1 -1
  300. package/src/plugins/kanban/api/getters.ts +1 -1
  301. package/src/plugins/kanban/api/mutations.ts +1 -1
  302. package/src/plugins/kanban/api/plugin.ts +6 -5
  303. package/src/plugins/kanban/client/components/forms/task-form.tsx +0 -1
  304. package/src/plugins/kanban/client/components/pages/board-page.internal.tsx +46 -27
  305. package/src/plugins/kanban/client/overrides.ts +27 -1
  306. package/src/types.ts +5 -1
  307. package/dist/shared/{stack.BQmuNl5p.d.mts → stack.BWp0hcm9.d.cts} +3 -3
  308. package/dist/shared/{stack.BQmuNl5p.d.ts → stack.BWp0hcm9.d.mts} +3 -3
  309. package/dist/shared/{stack.BQmuNl5p.d.cts → stack.BWp0hcm9.d.ts} +3 -3
@@ -0,0 +1,11 @@
1
+ export {
2
+ CommentThread,
3
+ type CommentThreadProps,
4
+ type CommentComponents,
5
+ type CommentInputProps,
6
+ type CommentRendererProps,
7
+ } from "./comment-thread";
8
+ export { CommentCount, type CommentCountProps } from "./comment-count";
9
+ export { CommentForm, type CommentFormProps } from "./comment-form";
10
+ export { ModerationPageComponent } from "./pages/moderation-page";
11
+ export { ResourceCommentsPageComponent } from "./pages/resource-comments-page";
@@ -0,0 +1,550 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import {
5
+ Table,
6
+ TableBody,
7
+ TableCell,
8
+ TableHead,
9
+ TableHeader,
10
+ TableRow,
11
+ } from "@workspace/ui/components/table";
12
+ import {
13
+ Dialog,
14
+ DialogContent,
15
+ DialogHeader,
16
+ DialogTitle,
17
+ } from "@workspace/ui/components/dialog";
18
+ import {
19
+ AlertDialog,
20
+ AlertDialogAction,
21
+ AlertDialogCancel,
22
+ AlertDialogContent,
23
+ AlertDialogDescription,
24
+ AlertDialogFooter,
25
+ AlertDialogHeader,
26
+ AlertDialogTitle,
27
+ } from "@workspace/ui/components/alert-dialog";
28
+ import { Button } from "@workspace/ui/components/button";
29
+ import { Badge } from "@workspace/ui/components/badge";
30
+ import { Tabs, TabsList, TabsTrigger } from "@workspace/ui/components/tabs";
31
+ import { Checkbox } from "@workspace/ui/components/checkbox";
32
+ import {
33
+ Avatar,
34
+ AvatarFallback,
35
+ AvatarImage,
36
+ } from "@workspace/ui/components/avatar";
37
+ import { CheckCircle, ShieldOff, Trash2, Eye } from "lucide-react";
38
+ import { toast } from "sonner";
39
+ import { formatDistanceToNow } from "date-fns";
40
+ import { useRegisterPageAIContext } from "@btst/stack/plugins/ai-chat/client/context";
41
+ import type { SerializedComment, CommentStatus } from "../../../types";
42
+ import {
43
+ useSuspenseModerationComments,
44
+ useUpdateCommentStatus,
45
+ useDeleteComment,
46
+ } from "../../hooks/use-comments";
47
+ import {
48
+ COMMENTS_LOCALIZATION,
49
+ type CommentsLocalization,
50
+ } from "../../localization";
51
+ import { getInitials } from "../../utils";
52
+ import { Pagination } from "../shared/pagination";
53
+
54
+ interface ModerationPageProps {
55
+ apiBaseURL: string;
56
+ apiBasePath: string;
57
+ headers?: HeadersInit;
58
+ localization?: CommentsLocalization;
59
+ }
60
+
61
+ function StatusBadge({ status }: { status: CommentStatus }) {
62
+ const variants: Record<
63
+ CommentStatus,
64
+ "secondary" | "default" | "destructive"
65
+ > = {
66
+ pending: "secondary",
67
+ approved: "default",
68
+ spam: "destructive",
69
+ };
70
+ return <Badge variant={variants[status]}>{status}</Badge>;
71
+ }
72
+
73
+ export function ModerationPage({
74
+ apiBaseURL,
75
+ apiBasePath,
76
+ headers,
77
+ localization: localizationProp,
78
+ }: ModerationPageProps) {
79
+ const loc = { ...COMMENTS_LOCALIZATION, ...localizationProp };
80
+ const [activeTab, setActiveTab] = useState<CommentStatus>("pending");
81
+ const [currentPage, setCurrentPage] = useState(1);
82
+ const [selected, setSelected] = useState<Set<string>>(new Set());
83
+ const [viewComment, setViewComment] = useState<SerializedComment | null>(
84
+ null,
85
+ );
86
+ const [deleteIds, setDeleteIds] = useState<string[]>([]);
87
+
88
+ const config = { apiBaseURL, apiBasePath, headers };
89
+
90
+ const { comments, total, limit, offset, totalPages, refetch } =
91
+ useSuspenseModerationComments(config, {
92
+ status: activeTab,
93
+ page: currentPage,
94
+ });
95
+
96
+ const updateStatus = useUpdateCommentStatus(config);
97
+ const deleteMutation = useDeleteComment(config);
98
+
99
+ // Register AI context with pending comment previews
100
+ useRegisterPageAIContext({
101
+ routeName: "comments-moderation",
102
+ pageDescription: `${total} ${activeTab} comments in the moderation queue.\n\nTop ${activeTab} comments:\n${comments
103
+ .slice(0, 5)
104
+ .map(
105
+ (c) =>
106
+ `- "${c.body.slice(0, 80)}${c.body.length > 80 ? "…" : ""}" by ${c.resolvedAuthorName} on ${c.resourceType}/${c.resourceId}`,
107
+ )
108
+ .join("\n")}`,
109
+ suggestions: [
110
+ "Approve all safe-looking comments",
111
+ "Flag spam comments",
112
+ "Summarize today's discussion",
113
+ ],
114
+ });
115
+
116
+ const toggleSelect = (id: string) => {
117
+ setSelected((prev) => {
118
+ const next = new Set(prev);
119
+ next.has(id) ? next.delete(id) : next.add(id);
120
+ return next;
121
+ });
122
+ };
123
+
124
+ const toggleSelectAll = () => {
125
+ if (selected.size === comments.length) {
126
+ setSelected(new Set());
127
+ } else {
128
+ setSelected(new Set(comments.map((c) => c.id)));
129
+ }
130
+ };
131
+
132
+ const handleApprove = async (id: string) => {
133
+ try {
134
+ await updateStatus.mutateAsync({ id, status: "approved" });
135
+ toast.success(loc.COMMENTS_MODERATION_TOAST_APPROVED);
136
+ await refetch();
137
+ } catch {
138
+ toast.error(loc.COMMENTS_MODERATION_TOAST_APPROVE_ERROR);
139
+ }
140
+ };
141
+
142
+ const handleSpam = async (id: string) => {
143
+ try {
144
+ await updateStatus.mutateAsync({ id, status: "spam" });
145
+ toast.success(loc.COMMENTS_MODERATION_TOAST_SPAM);
146
+ await refetch();
147
+ } catch {
148
+ toast.error(loc.COMMENTS_MODERATION_TOAST_SPAM_ERROR);
149
+ }
150
+ };
151
+
152
+ const handleDelete = async (ids: string[]) => {
153
+ try {
154
+ await Promise.all(ids.map((id) => deleteMutation.mutateAsync(id)));
155
+ toast.success(
156
+ ids.length === 1
157
+ ? loc.COMMENTS_MODERATION_TOAST_DELETED
158
+ : loc.COMMENTS_MODERATION_TOAST_DELETED_PLURAL.replace(
159
+ "{n}",
160
+ String(ids.length),
161
+ ),
162
+ );
163
+ setSelected(new Set());
164
+ setDeleteIds([]);
165
+ await refetch();
166
+ } catch {
167
+ toast.error(loc.COMMENTS_MODERATION_TOAST_DELETE_ERROR);
168
+ }
169
+ };
170
+
171
+ const handleBulkApprove = async () => {
172
+ const ids = [...selected];
173
+ try {
174
+ await Promise.all(
175
+ ids.map((id) => updateStatus.mutateAsync({ id, status: "approved" })),
176
+ );
177
+ toast.success(
178
+ loc.COMMENTS_MODERATION_TOAST_BULK_APPROVED.replace(
179
+ "{n}",
180
+ String(ids.length),
181
+ ),
182
+ );
183
+ setSelected(new Set());
184
+ await refetch();
185
+ } catch {
186
+ toast.error(loc.COMMENTS_MODERATION_TOAST_BULK_APPROVE_ERROR);
187
+ }
188
+ };
189
+
190
+ return (
191
+ <div className="w-full max-w-5xl space-y-6" data-testid="moderation-page">
192
+ <div>
193
+ <h1 className="text-2xl font-bold">{loc.COMMENTS_MODERATION_TITLE}</h1>
194
+ <p className="text-muted-foreground text-sm mt-1">
195
+ {loc.COMMENTS_MODERATION_DESCRIPTION}
196
+ </p>
197
+ </div>
198
+
199
+ <Tabs
200
+ value={activeTab}
201
+ onValueChange={(v) => {
202
+ setActiveTab(v as CommentStatus);
203
+ setCurrentPage(1);
204
+ setSelected(new Set());
205
+ }}
206
+ >
207
+ <TabsList>
208
+ <TabsTrigger value="pending" data-testid="tab-pending">
209
+ {loc.COMMENTS_MODERATION_TAB_PENDING}
210
+ </TabsTrigger>
211
+ <TabsTrigger value="approved" data-testid="tab-approved">
212
+ {loc.COMMENTS_MODERATION_TAB_APPROVED}
213
+ </TabsTrigger>
214
+ <TabsTrigger value="spam" data-testid="tab-spam">
215
+ {loc.COMMENTS_MODERATION_TAB_SPAM}
216
+ </TabsTrigger>
217
+ </TabsList>
218
+ </Tabs>
219
+
220
+ {/* Bulk actions toolbar */}
221
+ {selected.size > 0 && (
222
+ <div className="flex items-center gap-2 p-3 bg-muted rounded-lg">
223
+ <span className="text-sm text-muted-foreground">
224
+ {loc.COMMENTS_MODERATION_SELECTED.replace(
225
+ "{n}",
226
+ String(selected.size),
227
+ )}
228
+ </span>
229
+ {activeTab !== "approved" && (
230
+ <Button
231
+ size="sm"
232
+ variant="outline"
233
+ onClick={handleBulkApprove}
234
+ disabled={updateStatus.isPending}
235
+ >
236
+ <CheckCircle className="h-4 w-4 mr-1" />
237
+ {loc.COMMENTS_MODERATION_APPROVE_SELECTED}
238
+ </Button>
239
+ )}
240
+ <Button
241
+ size="sm"
242
+ variant="outline"
243
+ className="text-destructive border-destructive hover:bg-destructive hover:text-destructive-foreground"
244
+ onClick={() => setDeleteIds([...selected])}
245
+ >
246
+ <Trash2 className="h-4 w-4 mr-1" />
247
+ {loc.COMMENTS_MODERATION_DELETE_SELECTED}
248
+ </Button>
249
+ </div>
250
+ )}
251
+
252
+ {comments.length === 0 ? (
253
+ <div className="flex flex-col items-center gap-2 py-16 text-muted-foreground">
254
+ <CheckCircle className="h-8 w-8" />
255
+ <p className="text-sm">
256
+ {loc.COMMENTS_MODERATION_EMPTY.replace("{status}", activeTab)}
257
+ </p>
258
+ </div>
259
+ ) : (
260
+ <>
261
+ <div className="rounded-lg border">
262
+ <Table>
263
+ <TableHeader>
264
+ <TableRow>
265
+ <TableHead className="w-10">
266
+ <Checkbox
267
+ checked={
268
+ selected.size === comments.length && comments.length > 0
269
+ }
270
+ onCheckedChange={toggleSelectAll}
271
+ aria-label={loc.COMMENTS_MODERATION_SELECT_ALL}
272
+ />
273
+ </TableHead>
274
+ <TableHead>{loc.COMMENTS_MODERATION_COL_AUTHOR}</TableHead>
275
+ <TableHead>{loc.COMMENTS_MODERATION_COL_COMMENT}</TableHead>
276
+ <TableHead>{loc.COMMENTS_MODERATION_COL_RESOURCE}</TableHead>
277
+ <TableHead>{loc.COMMENTS_MODERATION_COL_DATE}</TableHead>
278
+ <TableHead className="w-36">
279
+ {loc.COMMENTS_MODERATION_COL_ACTIONS}
280
+ </TableHead>
281
+ </TableRow>
282
+ </TableHeader>
283
+ <TableBody>
284
+ {comments.map((comment) => (
285
+ <TableRow
286
+ key={comment.id}
287
+ data-testid="moderation-row"
288
+ data-comment-id={comment.id}
289
+ >
290
+ <TableCell>
291
+ <Checkbox
292
+ checked={selected.has(comment.id)}
293
+ onCheckedChange={() => toggleSelect(comment.id)}
294
+ aria-label={loc.COMMENTS_MODERATION_SELECT_ONE}
295
+ />
296
+ </TableCell>
297
+ <TableCell>
298
+ <div className="flex items-center gap-2">
299
+ <Avatar className="h-7 w-7">
300
+ {comment.resolvedAvatarUrl && (
301
+ <AvatarImage src={comment.resolvedAvatarUrl} />
302
+ )}
303
+ <AvatarFallback className="text-xs">
304
+ {getInitials(comment.resolvedAuthorName)}
305
+ </AvatarFallback>
306
+ </Avatar>
307
+ <span className="text-sm font-medium truncate max-w-[100px]">
308
+ {comment.resolvedAuthorName}
309
+ </span>
310
+ </div>
311
+ </TableCell>
312
+ <TableCell>
313
+ <p className="text-sm text-muted-foreground max-w-xs truncate">
314
+ {comment.body}
315
+ </p>
316
+ </TableCell>
317
+ <TableCell>
318
+ <span className="text-xs text-muted-foreground">
319
+ {comment.resourceType}/{comment.resourceId}
320
+ </span>
321
+ </TableCell>
322
+ <TableCell className="text-xs text-muted-foreground whitespace-nowrap">
323
+ {formatDistanceToNow(new Date(comment.createdAt), {
324
+ addSuffix: true,
325
+ })}
326
+ </TableCell>
327
+ <TableCell>
328
+ <div className="flex items-center gap-1">
329
+ <Button
330
+ variant="ghost"
331
+ size="icon"
332
+ className="h-7 w-7"
333
+ title={loc.COMMENTS_MODERATION_ACTION_VIEW}
334
+ onClick={() => setViewComment(comment)}
335
+ data-testid="view-button"
336
+ >
337
+ <Eye className="h-4 w-4" />
338
+ </Button>
339
+ {activeTab !== "approved" && (
340
+ <Button
341
+ variant="ghost"
342
+ size="icon"
343
+ className="h-7 w-7 text-green-600 hover:text-green-700"
344
+ title={loc.COMMENTS_MODERATION_ACTION_APPROVE}
345
+ onClick={() => handleApprove(comment.id)}
346
+ disabled={updateStatus.isPending}
347
+ data-testid="approve-button"
348
+ >
349
+ <CheckCircle className="h-4 w-4" />
350
+ </Button>
351
+ )}
352
+ {activeTab !== "spam" && (
353
+ <Button
354
+ variant="ghost"
355
+ size="icon"
356
+ className="h-7 w-7 text-orange-500 hover:text-orange-600"
357
+ title={loc.COMMENTS_MODERATION_ACTION_SPAM}
358
+ onClick={() => handleSpam(comment.id)}
359
+ disabled={updateStatus.isPending}
360
+ data-testid="spam-button"
361
+ >
362
+ <ShieldOff className="h-4 w-4" />
363
+ </Button>
364
+ )}
365
+ <Button
366
+ variant="ghost"
367
+ size="icon"
368
+ className="h-7 w-7 text-destructive hover:text-destructive"
369
+ title={loc.COMMENTS_MODERATION_ACTION_DELETE}
370
+ onClick={() => setDeleteIds([comment.id])}
371
+ data-testid="delete-button"
372
+ >
373
+ <Trash2 className="h-4 w-4" />
374
+ </Button>
375
+ </div>
376
+ </TableCell>
377
+ </TableRow>
378
+ ))}
379
+ </TableBody>
380
+ </Table>
381
+ </div>
382
+ <Pagination
383
+ currentPage={currentPage}
384
+ totalPages={totalPages}
385
+ onPageChange={setCurrentPage}
386
+ total={total}
387
+ limit={limit}
388
+ offset={offset}
389
+ />
390
+ </>
391
+ )}
392
+
393
+ {/* View comment dialog */}
394
+ <Dialog open={!!viewComment} onOpenChange={() => setViewComment(null)}>
395
+ <DialogContent className="max-w-2xl">
396
+ <DialogHeader>
397
+ <DialogTitle>{loc.COMMENTS_MODERATION_DIALOG_TITLE}</DialogTitle>
398
+ </DialogHeader>
399
+ {viewComment && (
400
+ <div className="space-y-4">
401
+ <div className="flex items-center gap-3">
402
+ <Avatar className="h-10 w-10">
403
+ {viewComment.resolvedAvatarUrl && (
404
+ <AvatarImage src={viewComment.resolvedAvatarUrl} />
405
+ )}
406
+ <AvatarFallback>
407
+ {getInitials(viewComment.resolvedAuthorName)}
408
+ </AvatarFallback>
409
+ </Avatar>
410
+ <div>
411
+ <p className="font-medium text-sm">
412
+ {viewComment.resolvedAuthorName}
413
+ </p>
414
+ <p className="text-xs text-muted-foreground">
415
+ {new Date(viewComment.createdAt).toLocaleString()}
416
+ </p>
417
+ </div>
418
+ <StatusBadge status={viewComment.status} />
419
+ </div>
420
+
421
+ <div className="grid grid-cols-2 gap-3 text-sm">
422
+ <div>
423
+ <p className="text-muted-foreground text-xs">
424
+ {loc.COMMENTS_MODERATION_DIALOG_RESOURCE}
425
+ </p>
426
+ <p className="font-mono text-xs">
427
+ {viewComment.resourceType}/{viewComment.resourceId}
428
+ </p>
429
+ </div>
430
+ <div>
431
+ <p className="text-muted-foreground text-xs">
432
+ {loc.COMMENTS_MODERATION_DIALOG_LIKES}
433
+ </p>
434
+ <p>{viewComment.likes}</p>
435
+ </div>
436
+ {viewComment.parentId && (
437
+ <div>
438
+ <p className="text-muted-foreground text-xs">
439
+ {loc.COMMENTS_MODERATION_DIALOG_REPLY_TO}
440
+ </p>
441
+ <p className="font-mono text-xs">{viewComment.parentId}</p>
442
+ </div>
443
+ )}
444
+ {viewComment.editedAt && (
445
+ <div>
446
+ <p className="text-muted-foreground text-xs">
447
+ {loc.COMMENTS_MODERATION_DIALOG_EDITED}
448
+ </p>
449
+ <p className="text-xs">
450
+ {new Date(viewComment.editedAt).toLocaleString()}
451
+ </p>
452
+ </div>
453
+ )}
454
+ </div>
455
+
456
+ <div>
457
+ <p className="text-muted-foreground text-xs mb-1">
458
+ {loc.COMMENTS_MODERATION_DIALOG_BODY}
459
+ </p>
460
+ <div className="p-3 bg-muted rounded-lg text-sm whitespace-pre-wrap break-words">
461
+ {viewComment.body}
462
+ </div>
463
+ </div>
464
+
465
+ <div className="flex justify-end gap-2">
466
+ {viewComment.status !== "approved" && (
467
+ <Button
468
+ size="sm"
469
+ onClick={async () => {
470
+ await handleApprove(viewComment.id);
471
+ setViewComment(null);
472
+ }}
473
+ disabled={updateStatus.isPending}
474
+ data-testid="dialog-approve-button"
475
+ >
476
+ <CheckCircle className="h-4 w-4 mr-1" />
477
+ {loc.COMMENTS_MODERATION_DIALOG_APPROVE}
478
+ </Button>
479
+ )}
480
+ {viewComment.status !== "spam" && (
481
+ <Button
482
+ size="sm"
483
+ variant="outline"
484
+ onClick={async () => {
485
+ await handleSpam(viewComment.id);
486
+ setViewComment(null);
487
+ }}
488
+ disabled={updateStatus.isPending}
489
+ >
490
+ <ShieldOff className="h-4 w-4 mr-1" />
491
+ {loc.COMMENTS_MODERATION_DIALOG_MARK_SPAM}
492
+ </Button>
493
+ )}
494
+ <Button
495
+ size="sm"
496
+ variant="destructive"
497
+ onClick={() => {
498
+ setDeleteIds([viewComment.id]);
499
+ setViewComment(null);
500
+ }}
501
+ >
502
+ <Trash2 className="h-4 w-4 mr-1" />
503
+ {loc.COMMENTS_MODERATION_DIALOG_DELETE}
504
+ </Button>
505
+ </div>
506
+ </div>
507
+ )}
508
+ </DialogContent>
509
+ </Dialog>
510
+
511
+ {/* Delete confirmation dialog */}
512
+ <AlertDialog
513
+ open={deleteIds.length > 0}
514
+ onOpenChange={(open) => !open && setDeleteIds([])}
515
+ >
516
+ <AlertDialogContent>
517
+ <AlertDialogHeader>
518
+ <AlertDialogTitle>
519
+ {deleteIds.length === 1
520
+ ? loc.COMMENTS_MODERATION_DELETE_TITLE_SINGULAR
521
+ : loc.COMMENTS_MODERATION_DELETE_TITLE_PLURAL.replace(
522
+ "{n}",
523
+ String(deleteIds.length),
524
+ )}
525
+ </AlertDialogTitle>
526
+ <AlertDialogDescription>
527
+ {deleteIds.length === 1
528
+ ? loc.COMMENTS_MODERATION_DELETE_DESCRIPTION_SINGULAR
529
+ : loc.COMMENTS_MODERATION_DELETE_DESCRIPTION_PLURAL}
530
+ </AlertDialogDescription>
531
+ </AlertDialogHeader>
532
+ <AlertDialogFooter>
533
+ <AlertDialogCancel>
534
+ {loc.COMMENTS_MODERATION_DELETE_CANCEL}
535
+ </AlertDialogCancel>
536
+ <AlertDialogAction
537
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
538
+ onClick={() => handleDelete(deleteIds)}
539
+ data-testid="confirm-delete-button"
540
+ >
541
+ {deleteMutation.isPending
542
+ ? loc.COMMENTS_MODERATION_DELETE_DELETING
543
+ : loc.COMMENTS_MODERATION_DELETE_CONFIRM}
544
+ </AlertDialogAction>
545
+ </AlertDialogFooter>
546
+ </AlertDialogContent>
547
+ </AlertDialog>
548
+ </div>
549
+ );
550
+ }
@@ -0,0 +1,70 @@
1
+ "use client";
2
+
3
+ import { lazy } from "react";
4
+ import { ComposedRoute } from "@btst/stack/client/components";
5
+ import { usePluginOverrides } from "@btst/stack/context";
6
+ import type { CommentsPluginOverrides } from "../../overrides";
7
+ import { COMMENTS_LOCALIZATION } from "../../localization";
8
+ import { useRouteLifecycle } from "@workspace/ui/hooks/use-route-lifecycle";
9
+ import { PageWrapper } from "../shared/page-wrapper";
10
+
11
+ const ModerationPageInternal = lazy(() =>
12
+ import("./moderation-page.internal").then((m) => ({
13
+ default: m.ModerationPage,
14
+ })),
15
+ );
16
+
17
+ function ModerationPageSkeleton() {
18
+ return (
19
+ <div className="w-full max-w-5xl mx-auto space-y-4 animate-pulse">
20
+ <div className="h-8 w-64 rounded bg-muted" />
21
+ <div className="h-4 w-48 rounded bg-muted" />
22
+ <div className="h-10 w-72 rounded bg-muted" />
23
+ <div className="rounded-lg border h-64 bg-muted" />
24
+ </div>
25
+ );
26
+ }
27
+
28
+ export function ModerationPageComponent() {
29
+ return (
30
+ <ComposedRoute
31
+ path="/comments/moderation"
32
+ PageComponent={ModerationPageWrapper}
33
+ LoadingComponent={ModerationPageSkeleton}
34
+ onError={(error) =>
35
+ console.error("[btst/comments] Moderation error:", error)
36
+ }
37
+ />
38
+ );
39
+ }
40
+
41
+ function ModerationPageWrapper() {
42
+ const overrides = usePluginOverrides<CommentsPluginOverrides>("comments");
43
+ const loc = { ...COMMENTS_LOCALIZATION, ...overrides.localization };
44
+
45
+ useRouteLifecycle({
46
+ routeName: "moderation",
47
+ context: {
48
+ path: "/comments/moderation",
49
+ isSSR: typeof window === "undefined",
50
+ },
51
+ overrides,
52
+ beforeRenderHook: (o, context) => {
53
+ if (o.onBeforeModerationPageRendered) {
54
+ return o.onBeforeModerationPageRendered(context);
55
+ }
56
+ return true;
57
+ },
58
+ });
59
+
60
+ return (
61
+ <PageWrapper>
62
+ <ModerationPageInternal
63
+ apiBaseURL={overrides.apiBaseURL}
64
+ apiBasePath={overrides.apiBasePath}
65
+ headers={overrides.headers as HeadersInit | undefined}
66
+ localization={loc}
67
+ />
68
+ </PageWrapper>
69
+ );
70
+ }