@prenta/admin 2.6.1 → 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 (864) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/design-system/tokens.css +196 -85
  3. package/design-system/tokens.json +310 -85
  4. package/design-system/tokens.scss +163 -44
  5. package/design-system/tokens.tailwind.cjs +64 -1
  6. package/dist/AdminRoot.d.ts.map +1 -1
  7. package/dist/AdminRoot.js +43 -21
  8. package/dist/AdminRoot.js.map +1 -1
  9. package/dist/LoginRoot.d.ts.map +1 -1
  10. package/dist/LoginRoot.js +2 -1
  11. package/dist/LoginRoot.js.map +1 -1
  12. package/dist/__tests__/components/author-avatar.test.js +3 -17
  13. package/dist/__tests__/components/author-avatar.test.js.map +1 -1
  14. package/dist/__tests__/components/notification-bell.test.js +86 -15
  15. package/dist/__tests__/components/notification-bell.test.js.map +1 -1
  16. package/dist/__tests__/components/responsive-tiers.render.test.d.ts +2 -0
  17. package/dist/__tests__/components/responsive-tiers.render.test.d.ts.map +1 -0
  18. package/dist/__tests__/components/responsive-tiers.render.test.js +313 -0
  19. package/dist/__tests__/components/responsive-tiers.render.test.js.map +1 -0
  20. package/dist/__tests__/components/serp-preview.render.test.js +6 -3
  21. package/dist/__tests__/components/serp-preview.render.test.js.map +1 -1
  22. package/dist/__tests__/components/shell-primitives.render.test.d.ts +2 -0
  23. package/dist/__tests__/components/shell-primitives.render.test.d.ts.map +1 -0
  24. package/dist/__tests__/components/shell-primitives.render.test.js +716 -0
  25. package/dist/__tests__/components/shell-primitives.render.test.js.map +1 -0
  26. package/dist/__tests__/components/theme-provider-accent.test.js +49 -7
  27. package/dist/__tests__/components/theme-provider-accent.test.js.map +1 -1
  28. package/dist/__tests__/components/toggle.test.js +10 -3
  29. package/dist/__tests__/components/toggle.test.js.map +1 -1
  30. package/dist/__tests__/components/ui-primitives.test.js +3 -7
  31. package/dist/__tests__/components/ui-primitives.test.js.map +1 -1
  32. package/dist/__tests__/layout/build-nav-items.test.js +1 -1
  33. package/dist/__tests__/layout/build-nav-items.test.js.map +1 -1
  34. package/dist/__tests__/layout/shell-nav.render.test.d.ts +2 -0
  35. package/dist/__tests__/layout/shell-nav.render.test.d.ts.map +1 -0
  36. package/dist/__tests__/layout/shell-nav.render.test.js +736 -0
  37. package/dist/__tests__/layout/shell-nav.render.test.js.map +1 -0
  38. package/dist/__tests__/layout/sidebar-forms-submenu.render.test.js +8 -4
  39. package/dist/__tests__/layout/sidebar-forms-submenu.render.test.js.map +1 -1
  40. package/dist/__tests__/layout/sidebar-groups.render.test.d.ts +2 -0
  41. package/dist/__tests__/layout/sidebar-groups.render.test.d.ts.map +1 -0
  42. package/dist/__tests__/layout/sidebar-groups.render.test.js +56 -0
  43. package/dist/__tests__/layout/sidebar-groups.render.test.js.map +1 -0
  44. package/dist/__tests__/layout/sidebar-menus.render.test.d.ts +2 -0
  45. package/dist/__tests__/layout/sidebar-menus.render.test.d.ts.map +1 -0
  46. package/dist/__tests__/layout/sidebar-menus.render.test.js +82 -0
  47. package/dist/__tests__/layout/sidebar-menus.render.test.js.map +1 -0
  48. package/dist/__tests__/layout/sidebar-plugin-nav.test.js +1 -1
  49. package/dist/__tests__/layout/sidebar-plugin-nav.test.js.map +1 -1
  50. package/dist/__tests__/layout/sidebar-posts.test.js +48 -38
  51. package/dist/__tests__/layout/sidebar-posts.test.js.map +1 -1
  52. package/dist/__tests__/layout/sidebar-submenu.render.test.js +26 -10
  53. package/dist/__tests__/layout/sidebar-submenu.render.test.js.map +1 -1
  54. package/dist/__tests__/layout/topbar.render.test.d.ts +2 -0
  55. package/dist/__tests__/layout/topbar.render.test.d.ts.map +1 -0
  56. package/dist/__tests__/layout/topbar.render.test.js +406 -0
  57. package/dist/__tests__/layout/topbar.render.test.js.map +1 -0
  58. package/dist/__tests__/lib/api.test.js +2 -0
  59. package/dist/__tests__/lib/api.test.js.map +1 -1
  60. package/dist/__tests__/lib/batch-wizard.test.d.ts +2 -0
  61. package/dist/__tests__/lib/batch-wizard.test.d.ts.map +1 -0
  62. package/dist/__tests__/lib/batch-wizard.test.js +144 -0
  63. package/dist/__tests__/lib/batch-wizard.test.js.map +1 -0
  64. package/dist/__tests__/lib/job-drawer.test.d.ts +2 -0
  65. package/dist/__tests__/lib/job-drawer.test.d.ts.map +1 -0
  66. package/dist/__tests__/lib/job-drawer.test.js +395 -0
  67. package/dist/__tests__/lib/job-drawer.test.js.map +1 -0
  68. package/dist/__tests__/lib/job-view.test.d.ts +2 -0
  69. package/dist/__tests__/lib/job-view.test.d.ts.map +1 -0
  70. package/dist/__tests__/lib/job-view.test.js +484 -0
  71. package/dist/__tests__/lib/job-view.test.js.map +1 -0
  72. package/dist/__tests__/lib/jobs-service.test.d.ts +2 -0
  73. package/dist/__tests__/lib/jobs-service.test.d.ts.map +1 -0
  74. package/dist/__tests__/lib/jobs-service.test.js +142 -0
  75. package/dist/__tests__/lib/jobs-service.test.js.map +1 -0
  76. package/dist/__tests__/router/admin-route-order.test.d.ts +2 -0
  77. package/dist/__tests__/router/admin-route-order.test.d.ts.map +1 -0
  78. package/dist/__tests__/router/admin-route-order.test.js +36 -0
  79. package/dist/__tests__/router/admin-route-order.test.js.map +1 -0
  80. package/dist/__tests__/styles/ai-gradient-contrast.test.d.ts +2 -0
  81. package/dist/__tests__/styles/ai-gradient-contrast.test.d.ts.map +1 -0
  82. package/dist/__tests__/styles/ai-gradient-contrast.test.js +110 -0
  83. package/dist/__tests__/styles/ai-gradient-contrast.test.js.map +1 -0
  84. package/dist/__tests__/styles/breakpoint-order.test.d.ts +2 -0
  85. package/dist/__tests__/styles/breakpoint-order.test.d.ts.map +1 -0
  86. package/dist/__tests__/styles/breakpoint-order.test.js +52 -0
  87. package/dist/__tests__/styles/breakpoint-order.test.js.map +1 -0
  88. package/dist/__tests__/styles/responsive-layer.test.js +303 -12
  89. package/dist/__tests__/styles/responsive-layer.test.js.map +1 -1
  90. package/dist/__tests__/styles/tailwind-preset.test.d.ts +2 -0
  91. package/dist/__tests__/styles/tailwind-preset.test.d.ts.map +1 -0
  92. package/dist/__tests__/styles/tailwind-preset.test.js +147 -0
  93. package/dist/__tests__/styles/tailwind-preset.test.js.map +1 -0
  94. package/dist/__tests__/styles/theme-token-scope.test.js +9 -3
  95. package/dist/__tests__/styles/theme-token-scope.test.js.map +1 -1
  96. package/dist/__tests__/styles/tiers.test.d.ts +2 -0
  97. package/dist/__tests__/styles/tiers.test.d.ts.map +1 -0
  98. package/dist/__tests__/styles/tiers.test.js +121 -0
  99. package/dist/__tests__/styles/tiers.test.js.map +1 -0
  100. package/dist/__tests__/styles/tokens-drift.test.js +57 -6
  101. package/dist/__tests__/styles/tokens-drift.test.js.map +1 -1
  102. package/dist/__tests__/views/admin-lazy-route.render.test.js +27 -1
  103. package/dist/__tests__/views/admin-lazy-route.render.test.js.map +1 -1
  104. package/dist/__tests__/views/ai-settings.render.test.js +4 -4
  105. package/dist/__tests__/views/appearance-chrome.render.test.d.ts +2 -0
  106. package/dist/__tests__/views/appearance-chrome.render.test.d.ts.map +1 -0
  107. package/dist/__tests__/views/appearance-chrome.render.test.js +688 -0
  108. package/dist/__tests__/views/appearance-chrome.render.test.js.map +1 -0
  109. package/dist/__tests__/views/appearance-menus-route.render.test.d.ts +2 -0
  110. package/dist/__tests__/views/appearance-menus-route.render.test.d.ts.map +1 -0
  111. package/dist/__tests__/views/appearance-menus-route.render.test.js +50 -0
  112. package/dist/__tests__/views/appearance-menus-route.render.test.js.map +1 -0
  113. package/dist/__tests__/views/appearance-settings.render.test.js +8 -6
  114. package/dist/__tests__/views/appearance-settings.render.test.js.map +1 -1
  115. package/dist/__tests__/views/appearance-theme-provider.render.test.d.ts +2 -0
  116. package/dist/__tests__/views/appearance-theme-provider.render.test.d.ts.map +1 -0
  117. package/dist/__tests__/views/appearance-theme-provider.render.test.js +286 -0
  118. package/dist/__tests__/views/appearance-theme-provider.render.test.js.map +1 -0
  119. package/dist/__tests__/views/dashboard.test.js +37 -0
  120. package/dist/__tests__/views/dashboard.test.js.map +1 -1
  121. package/dist/__tests__/views/document-edit-navigation.render.test.d.ts +2 -0
  122. package/dist/__tests__/views/document-edit-navigation.render.test.d.ts.map +1 -0
  123. package/dist/__tests__/views/document-edit-navigation.render.test.js +101 -0
  124. package/dist/__tests__/views/document-edit-navigation.render.test.js.map +1 -0
  125. package/dist/__tests__/views/document-edit-routes.render.test.d.ts +2 -0
  126. package/dist/__tests__/views/document-edit-routes.render.test.d.ts.map +1 -0
  127. package/dist/__tests__/views/document-edit-routes.render.test.js +113 -0
  128. package/dist/__tests__/views/document-edit-routes.render.test.js.map +1 -0
  129. package/dist/__tests__/views/email-settings.render.test.js +2 -2
  130. package/dist/__tests__/views/form-entries.render.test.js +15 -0
  131. package/dist/__tests__/views/form-entries.render.test.js.map +1 -1
  132. package/dist/__tests__/views/form-submissions.render.test.js +18 -0
  133. package/dist/__tests__/views/form-submissions.render.test.js.map +1 -1
  134. package/dist/__tests__/views/forms-list.render.test.js +12 -0
  135. package/dist/__tests__/views/forms-list.render.test.js.map +1 -1
  136. package/dist/__tests__/views/general-settings.render.test.js +26 -10
  137. package/dist/__tests__/views/general-settings.render.test.js.map +1 -1
  138. package/dist/__tests__/views/header-footer-form.test.d.ts +2 -0
  139. package/dist/__tests__/views/header-footer-form.test.d.ts.map +1 -0
  140. package/dist/__tests__/views/header-footer-form.test.js +266 -0
  141. package/dist/__tests__/views/header-footer-form.test.js.map +1 -0
  142. package/dist/__tests__/views/jobs-drawer.render.test.d.ts +2 -0
  143. package/dist/__tests__/views/jobs-drawer.render.test.d.ts.map +1 -0
  144. package/dist/__tests__/views/jobs-drawer.render.test.js +1348 -0
  145. package/dist/__tests__/views/jobs-drawer.render.test.js.map +1 -0
  146. package/dist/__tests__/views/jobs-list.render.test.d.ts +2 -0
  147. package/dist/__tests__/views/jobs-list.render.test.d.ts.map +1 -0
  148. package/dist/__tests__/views/jobs-list.render.test.js +697 -0
  149. package/dist/__tests__/views/jobs-list.render.test.js.map +1 -0
  150. package/dist/__tests__/views/jobs-wizard.render.test.d.ts +2 -0
  151. package/dist/__tests__/views/jobs-wizard.render.test.d.ts.map +1 -0
  152. package/dist/__tests__/views/jobs-wizard.render.test.js +588 -0
  153. package/dist/__tests__/views/jobs-wizard.render.test.js.map +1 -0
  154. package/dist/__tests__/views/kpi-grid.test.d.ts +2 -0
  155. package/dist/__tests__/views/kpi-grid.test.d.ts.map +1 -0
  156. package/dist/__tests__/views/kpi-grid.test.js +26 -0
  157. package/dist/__tests__/views/kpi-grid.test.js.map +1 -0
  158. package/dist/__tests__/views/post-section-editor.test.js +3 -1
  159. package/dist/__tests__/views/post-section-editor.test.js.map +1 -1
  160. package/dist/__tests__/views/script-tags.render.test.js +25 -0
  161. package/dist/__tests__/views/script-tags.render.test.js.map +1 -1
  162. package/dist/__tests__/views/security-settings.render.test.js +9 -8
  163. package/dist/__tests__/views/security-settings.render.test.js.map +1 -1
  164. package/dist/__tests__/views/seo-settings.render.test.js +7 -7
  165. package/dist/__tests__/views/seo-settings.render.test.js.map +1 -1
  166. package/dist/__tests__/views/settings-save-bar.render.test.d.ts +2 -0
  167. package/dist/__tests__/views/settings-save-bar.render.test.d.ts.map +1 -0
  168. package/dist/__tests__/views/settings-save-bar.render.test.js +68 -0
  169. package/dist/__tests__/views/settings-save-bar.render.test.js.map +1 -0
  170. package/dist/__tests__/views/settings-save-footer.render.test.js +23 -15
  171. package/dist/__tests__/views/settings-save-footer.render.test.js.map +1 -1
  172. package/dist/__tests__/views/settings-tabs.render.test.d.ts +2 -0
  173. package/dist/__tests__/views/settings-tabs.render.test.d.ts.map +1 -0
  174. package/dist/__tests__/views/settings-tabs.render.test.js +319 -0
  175. package/dist/__tests__/views/settings-tabs.render.test.js.map +1 -0
  176. package/dist/__tests__/views/updates-tab.render.test.js +4 -3
  177. package/dist/__tests__/views/updates-tab.render.test.js.map +1 -1
  178. package/dist/components/Breadcrumbs.d.ts +8 -1
  179. package/dist/components/Breadcrumbs.d.ts.map +1 -1
  180. package/dist/components/Breadcrumbs.js +6 -0
  181. package/dist/components/Breadcrumbs.js.map +1 -1
  182. package/dist/components/CommandPalette.d.ts.map +1 -1
  183. package/dist/components/CommandPalette.js +40 -4
  184. package/dist/components/CommandPalette.js.map +1 -1
  185. package/dist/components/LocaleSwitcher.d.ts.map +1 -1
  186. package/dist/components/LocaleSwitcher.js +1 -1
  187. package/dist/components/LocaleSwitcher.js.map +1 -1
  188. package/dist/components/NotificationBell.d.ts.map +1 -1
  189. package/dist/components/NotificationBell.js +44 -3
  190. package/dist/components/NotificationBell.js.map +1 -1
  191. package/dist/components/OfflineStatus.js +1 -1
  192. package/dist/components/OfflineStatus.js.map +1 -1
  193. package/dist/components/PresenceIndicator.d.ts.map +1 -1
  194. package/dist/components/PresenceIndicator.js +2 -1
  195. package/dist/components/PresenceIndicator.js.map +1 -1
  196. package/dist/components/ReflowMeter.js +2 -2
  197. package/dist/components/ReflowMeter.js.map +1 -1
  198. package/dist/components/SEOPanel.js +2 -2
  199. package/dist/components/SEOPanel.js.map +1 -1
  200. package/dist/components/ThemeProvider.d.ts +24 -4
  201. package/dist/components/ThemeProvider.d.ts.map +1 -1
  202. package/dist/components/ThemeProvider.js +80 -22
  203. package/dist/components/ThemeProvider.js.map +1 -1
  204. package/dist/components/TipTapEditor.d.ts.map +1 -1
  205. package/dist/components/TipTapEditor.js +3 -4
  206. package/dist/components/TipTapEditor.js.map +1 -1
  207. package/dist/components/forms/EmbedPanel.js +1 -1
  208. package/dist/components/forms/EmbedPanel.js.map +1 -1
  209. package/dist/components/forms/FormEntryDetailPane.js +6 -1
  210. package/dist/components/forms/FormEntryDetailPane.js.map +1 -1
  211. package/dist/components/forms/ShortcodeGlossary.d.ts.map +1 -1
  212. package/dist/components/forms/ShortcodeGlossary.js +6 -1
  213. package/dist/components/forms/ShortcodeGlossary.js.map +1 -1
  214. package/dist/components/forms/primitives.d.ts +15 -5
  215. package/dist/components/forms/primitives.d.ts.map +1 -1
  216. package/dist/components/forms/primitives.js +11 -11
  217. package/dist/components/forms/primitives.js.map +1 -1
  218. package/dist/components/pages/tag-badges.d.ts +2 -1
  219. package/dist/components/pages/tag-badges.d.ts.map +1 -1
  220. package/dist/components/pages/tag-badges.js +4 -3
  221. package/dist/components/pages/tag-badges.js.map +1 -1
  222. package/dist/components/seo/PageAuditPanel.js +2 -2
  223. package/dist/components/seo/PageAuditPanel.js.map +1 -1
  224. package/dist/components/seo/SerpPreview.d.ts +4 -4
  225. package/dist/components/seo/SerpPreview.js +1 -1
  226. package/dist/components/seo/SerpPreview.js.map +1 -1
  227. package/dist/components/seo/primitives.js +2 -2
  228. package/dist/components/seo/primitives.js.map +1 -1
  229. package/dist/components/ui/AuthorAvatar.d.ts +4 -4
  230. package/dist/components/ui/AuthorAvatar.d.ts.map +1 -1
  231. package/dist/components/ui/AuthorAvatar.js +11 -10
  232. package/dist/components/ui/AuthorAvatar.js.map +1 -1
  233. package/dist/components/ui/Badge.d.ts +1 -1
  234. package/dist/components/ui/Badge.js +1 -1
  235. package/dist/components/ui/Badge.js.map +1 -1
  236. package/dist/components/ui/Breadcrumb.d.ts +27 -0
  237. package/dist/components/ui/Breadcrumb.d.ts.map +1 -0
  238. package/dist/components/ui/Breadcrumb.js +41 -0
  239. package/dist/components/ui/Breadcrumb.js.map +1 -0
  240. package/dist/components/ui/Button.d.ts +2 -2
  241. package/dist/components/ui/Button.d.ts.map +1 -1
  242. package/dist/components/ui/Button.js +31 -16
  243. package/dist/components/ui/Button.js.map +1 -1
  244. package/dist/components/ui/Card.d.ts +8 -4
  245. package/dist/components/ui/Card.d.ts.map +1 -1
  246. package/dist/components/ui/Card.js +21 -13
  247. package/dist/components/ui/Card.js.map +1 -1
  248. package/dist/components/ui/CardListControls.d.ts +34 -0
  249. package/dist/components/ui/CardListControls.d.ts.map +1 -0
  250. package/dist/components/ui/CardListControls.js +25 -0
  251. package/dist/components/ui/CardListControls.js.map +1 -0
  252. package/dist/components/ui/CountChip.d.ts +20 -0
  253. package/dist/components/ui/CountChip.d.ts.map +1 -0
  254. package/dist/components/ui/CountChip.js +48 -0
  255. package/dist/components/ui/CountChip.js.map +1 -0
  256. package/dist/components/ui/FilterPill.d.ts +2 -1
  257. package/dist/components/ui/FilterPill.d.ts.map +1 -1
  258. package/dist/components/ui/FilterPill.js +3 -2
  259. package/dist/components/ui/FilterPill.js.map +1 -1
  260. package/dist/components/ui/Input.d.ts +1 -1
  261. package/dist/components/ui/InspectorPane.d.ts +1 -1
  262. package/dist/components/ui/InspectorPane.d.ts.map +1 -1
  263. package/dist/components/ui/InspectorPane.js +15 -4
  264. package/dist/components/ui/InspectorPane.js.map +1 -1
  265. package/dist/components/ui/Modal.d.ts.map +1 -1
  266. package/dist/components/ui/Modal.js +10 -2
  267. package/dist/components/ui/Modal.js.map +1 -1
  268. package/dist/components/ui/PageHeader.d.ts +28 -0
  269. package/dist/components/ui/PageHeader.d.ts.map +1 -0
  270. package/dist/components/ui/PageHeader.js +18 -0
  271. package/dist/components/ui/PageHeader.js.map +1 -0
  272. package/dist/components/ui/RowActions.d.ts +5 -0
  273. package/dist/components/ui/RowActions.d.ts.map +1 -1
  274. package/dist/components/ui/RowActions.js +7 -2
  275. package/dist/components/ui/RowActions.js.map +1 -1
  276. package/dist/components/ui/Segmented.d.ts +21 -0
  277. package/dist/components/ui/Segmented.d.ts.map +1 -0
  278. package/dist/components/ui/Segmented.js +41 -0
  279. package/dist/components/ui/Segmented.js.map +1 -0
  280. package/dist/components/ui/Toaster.d.ts +12 -0
  281. package/dist/components/ui/Toaster.d.ts.map +1 -0
  282. package/dist/components/ui/Toaster.js +103 -0
  283. package/dist/components/ui/Toaster.js.map +1 -0
  284. package/dist/components/ui/Toggle.d.ts +4 -3
  285. package/dist/components/ui/Toggle.d.ts.map +1 -1
  286. package/dist/components/ui/Toggle.js +16 -12
  287. package/dist/components/ui/Toggle.js.map +1 -1
  288. package/dist/components/ui/UnderlineTabs.d.ts +56 -0
  289. package/dist/components/ui/UnderlineTabs.d.ts.map +1 -0
  290. package/dist/components/ui/UnderlineTabs.js +205 -0
  291. package/dist/components/ui/UnderlineTabs.js.map +1 -0
  292. package/dist/components/ui/UnsavedBar.d.ts +25 -0
  293. package/dist/components/ui/UnsavedBar.d.ts.map +1 -0
  294. package/dist/components/ui/UnsavedBar.js +22 -0
  295. package/dist/components/ui/UnsavedBar.js.map +1 -0
  296. package/dist/components/ui/admin-links.d.ts +11 -0
  297. package/dist/components/ui/admin-links.d.ts.map +1 -0
  298. package/dist/components/ui/admin-links.js +10 -0
  299. package/dist/components/ui/admin-links.js.map +1 -0
  300. package/dist/components/ui/card-table.d.ts +43 -0
  301. package/dist/components/ui/card-table.d.ts.map +1 -0
  302. package/dist/components/ui/card-table.js +31 -0
  303. package/dist/components/ui/card-table.js.map +1 -0
  304. package/dist/components/virtual-window.d.ts +7 -0
  305. package/dist/components/virtual-window.d.ts.map +1 -1
  306. package/dist/components/virtual-window.js +5 -2
  307. package/dist/components/virtual-window.js.map +1 -1
  308. package/dist/fields/RelationshipField.js +1 -1
  309. package/dist/fields/RelationshipField.js.map +1 -1
  310. package/dist/files/dm-mono-latin-400-normal.woff +0 -0
  311. package/dist/files/dm-mono-latin-400-normal.woff2 +0 -0
  312. package/dist/files/dm-mono-latin-500-normal.woff +0 -0
  313. package/dist/files/dm-mono-latin-500-normal.woff2 +0 -0
  314. package/dist/files/dm-mono-latin-ext-400-normal.woff +0 -0
  315. package/dist/files/dm-mono-latin-ext-400-normal.woff2 +0 -0
  316. package/dist/files/dm-mono-latin-ext-500-normal.woff +0 -0
  317. package/dist/files/dm-mono-latin-ext-500-normal.woff2 +0 -0
  318. package/dist/files/dm-sans-latin-400-normal.woff +0 -0
  319. package/dist/files/dm-sans-latin-400-normal.woff2 +0 -0
  320. package/dist/files/dm-sans-latin-500-normal.woff +0 -0
  321. package/dist/files/dm-sans-latin-500-normal.woff2 +0 -0
  322. package/dist/files/dm-sans-latin-600-normal.woff +0 -0
  323. package/dist/files/dm-sans-latin-600-normal.woff2 +0 -0
  324. package/dist/files/dm-sans-latin-700-normal.woff +0 -0
  325. package/dist/files/dm-sans-latin-700-normal.woff2 +0 -0
  326. package/dist/files/dm-sans-latin-ext-400-normal.woff +0 -0
  327. package/dist/files/dm-sans-latin-ext-400-normal.woff2 +0 -0
  328. package/dist/files/dm-sans-latin-ext-500-normal.woff +0 -0
  329. package/dist/files/dm-sans-latin-ext-500-normal.woff2 +0 -0
  330. package/dist/files/dm-sans-latin-ext-600-normal.woff +0 -0
  331. package/dist/files/dm-sans-latin-ext-600-normal.woff2 +0 -0
  332. package/dist/files/dm-sans-latin-ext-700-normal.woff +0 -0
  333. package/dist/files/dm-sans-latin-ext-700-normal.woff2 +0 -0
  334. package/dist/hooks/useAppearanceBootstrap.d.ts.map +1 -1
  335. package/dist/hooks/useAppearanceBootstrap.js +9 -3
  336. package/dist/hooks/useAppearanceBootstrap.js.map +1 -1
  337. package/dist/index.d.ts +15 -0
  338. package/dist/index.d.ts.map +1 -1
  339. package/dist/index.js +8 -0
  340. package/dist/index.js.map +1 -1
  341. package/dist/layout/Header.d.ts +14 -5
  342. package/dist/layout/Header.d.ts.map +1 -1
  343. package/dist/layout/Header.js +78 -13
  344. package/dist/layout/Header.js.map +1 -1
  345. package/dist/layout/Layout.d.ts +5 -3
  346. package/dist/layout/Layout.d.ts.map +1 -1
  347. package/dist/layout/Layout.js +45 -54
  348. package/dist/layout/Layout.js.map +1 -1
  349. package/dist/layout/MobileTabBar.d.ts +20 -0
  350. package/dist/layout/MobileTabBar.d.ts.map +1 -0
  351. package/dist/layout/MobileTabBar.js +29 -0
  352. package/dist/layout/MobileTabBar.js.map +1 -0
  353. package/dist/layout/Sidebar.d.ts +8 -26
  354. package/dist/layout/Sidebar.d.ts.map +1 -1
  355. package/dist/layout/Sidebar.js +309 -345
  356. package/dist/layout/Sidebar.js.map +1 -1
  357. package/dist/layout/nav-config.d.ts +101 -0
  358. package/dist/layout/nav-config.d.ts.map +1 -0
  359. package/dist/layout/nav-config.js +297 -0
  360. package/dist/layout/nav-config.js.map +1 -0
  361. package/dist/layout/primitives/AdminShell.d.ts +10 -2
  362. package/dist/layout/primitives/AdminShell.d.ts.map +1 -1
  363. package/dist/layout/primitives/AdminShell.js +114 -6
  364. package/dist/layout/primitives/AdminShell.js.map +1 -1
  365. package/dist/layout/topbar-classes.d.ts +8 -0
  366. package/dist/layout/topbar-classes.d.ts.map +1 -0
  367. package/dist/layout/topbar-classes.js +8 -0
  368. package/dist/layout/topbar-classes.js.map +1 -0
  369. package/dist/layout/use-nav-items.d.ts +10 -0
  370. package/dist/layout/use-nav-items.d.ts.map +1 -0
  371. package/dist/layout/use-nav-items.js +39 -0
  372. package/dist/layout/use-nav-items.js.map +1 -0
  373. package/dist/lib/api.d.ts +8 -0
  374. package/dist/lib/api.d.ts.map +1 -1
  375. package/dist/lib/api.js +8 -1
  376. package/dist/lib/api.js.map +1 -1
  377. package/dist/lib/appearance-api.d.ts +3 -1
  378. package/dist/lib/appearance-api.d.ts.map +1 -1
  379. package/dist/lib/appearance-api.js +1 -0
  380. package/dist/lib/appearance-api.js.map +1 -1
  381. package/dist/lib/batch-wizard.d.ts +84 -0
  382. package/dist/lib/batch-wizard.d.ts.map +1 -0
  383. package/dist/lib/batch-wizard.js +122 -0
  384. package/dist/lib/batch-wizard.js.map +1 -0
  385. package/dist/lib/chrome-service.d.ts +69 -0
  386. package/dist/lib/chrome-service.d.ts.map +1 -0
  387. package/dist/lib/chrome-service.js +108 -0
  388. package/dist/lib/chrome-service.js.map +1 -0
  389. package/dist/lib/job-drawer.d.ts +128 -0
  390. package/dist/lib/job-drawer.d.ts.map +1 -0
  391. package/dist/lib/job-drawer.js +351 -0
  392. package/dist/lib/job-drawer.js.map +1 -0
  393. package/dist/lib/job-view.d.ts +230 -0
  394. package/dist/lib/job-view.d.ts.map +1 -0
  395. package/dist/lib/job-view.js +419 -0
  396. package/dist/lib/job-view.js.map +1 -0
  397. package/dist/lib/jobs-service.d.ts +152 -0
  398. package/dist/lib/jobs-service.d.ts.map +1 -0
  399. package/dist/lib/jobs-service.js +220 -0
  400. package/dist/lib/jobs-service.js.map +1 -0
  401. package/dist/lib/sheet-focus.d.ts +24 -0
  402. package/dist/lib/sheet-focus.d.ts.map +1 -0
  403. package/dist/lib/sheet-focus.js +61 -0
  404. package/dist/lib/sheet-focus.js.map +1 -0
  405. package/dist/lib/tiers.d.ts +30 -0
  406. package/dist/lib/tiers.d.ts.map +1 -0
  407. package/dist/lib/tiers.js +43 -0
  408. package/dist/lib/tiers.js.map +1 -0
  409. package/dist/prenta-admin.css +1 -1
  410. package/dist/router/admin-routes.d.ts.map +1 -1
  411. package/dist/router/admin-routes.js +8 -0
  412. package/dist/router/admin-routes.js.map +1 -1
  413. package/dist/router/index.d.ts +5 -1
  414. package/dist/router/index.d.ts.map +1 -1
  415. package/dist/router/index.js +7 -0
  416. package/dist/router/index.js.map +1 -1
  417. package/dist/views/ApiKeys.js +2 -2
  418. package/dist/views/ApiKeys.js.map +1 -1
  419. package/dist/views/AppearanceChrome.d.ts +24 -0
  420. package/dist/views/AppearanceChrome.d.ts.map +1 -0
  421. package/dist/views/AppearanceChrome.js +461 -0
  422. package/dist/views/AppearanceChrome.js.map +1 -0
  423. package/dist/views/CollectionList.d.ts.map +1 -1
  424. package/dist/views/CollectionList.js +73 -53
  425. package/dist/views/CollectionList.js.map +1 -1
  426. package/dist/views/Dashboard.d.ts.map +1 -1
  427. package/dist/views/Dashboard.js +14 -8
  428. package/dist/views/Dashboard.js.map +1 -1
  429. package/dist/views/DocumentEdit.d.ts +6 -1
  430. package/dist/views/DocumentEdit.d.ts.map +1 -1
  431. package/dist/views/DocumentEdit.js +44 -8
  432. package/dist/views/DocumentEdit.js.map +1 -1
  433. package/dist/views/FormDeliveries.d.ts.map +1 -1
  434. package/dist/views/FormDeliveries.js +7 -1
  435. package/dist/views/FormDeliveries.js.map +1 -1
  436. package/dist/views/FormEntries.d.ts.map +1 -1
  437. package/dist/views/FormEntries.js +11 -2
  438. package/dist/views/FormEntries.js.map +1 -1
  439. package/dist/views/FormSubmissions.d.ts +1 -1
  440. package/dist/views/FormSubmissions.d.ts.map +1 -1
  441. package/dist/views/FormSubmissions.js +15 -4
  442. package/dist/views/FormSubmissions.js.map +1 -1
  443. package/dist/views/Forms.d.ts.map +1 -1
  444. package/dist/views/Forms.js +6 -4
  445. package/dist/views/Forms.js.map +1 -1
  446. package/dist/views/FormsSettings.d.ts +2 -0
  447. package/dist/views/FormsSettings.d.ts.map +1 -0
  448. package/dist/views/FormsSettings.js +37 -0
  449. package/dist/views/FormsSettings.js.map +1 -0
  450. package/dist/views/Jobs/BusyButton.d.ts +15 -0
  451. package/dist/views/Jobs/BusyButton.d.ts.map +1 -0
  452. package/dist/views/Jobs/BusyButton.js +26 -0
  453. package/dist/views/Jobs/BusyButton.js.map +1 -0
  454. package/dist/views/Jobs/CancelJobDialog.d.ts +27 -0
  455. package/dist/views/Jobs/CancelJobDialog.d.ts.map +1 -0
  456. package/dist/views/Jobs/CancelJobDialog.js +76 -0
  457. package/dist/views/Jobs/CancelJobDialog.js.map +1 -0
  458. package/dist/views/Jobs/DrawerActivity.d.ts +16 -0
  459. package/dist/views/Jobs/DrawerActivity.d.ts.map +1 -0
  460. package/dist/views/Jobs/DrawerActivity.js +59 -0
  461. package/dist/views/Jobs/DrawerActivity.js.map +1 -0
  462. package/dist/views/Jobs/DrawerOverview.d.ts +28 -0
  463. package/dist/views/Jobs/DrawerOverview.d.ts.map +1 -0
  464. package/dist/views/Jobs/DrawerOverview.js +32 -0
  465. package/dist/views/Jobs/DrawerOverview.js.map +1 -0
  466. package/dist/views/Jobs/DrawerResults.d.ts +25 -0
  467. package/dist/views/Jobs/DrawerResults.d.ts.map +1 -0
  468. package/dist/views/Jobs/DrawerResults.js +50 -0
  469. package/dist/views/Jobs/DrawerResults.js.map +1 -0
  470. package/dist/views/Jobs/DrawerReview.d.ts +51 -0
  471. package/dist/views/Jobs/DrawerReview.d.ts.map +1 -0
  472. package/dist/views/Jobs/DrawerReview.js +110 -0
  473. package/dist/views/Jobs/DrawerReview.js.map +1 -0
  474. package/dist/views/Jobs/JobDrawer.d.ts +51 -0
  475. package/dist/views/Jobs/JobDrawer.d.ts.map +1 -0
  476. package/dist/views/Jobs/JobDrawer.js +378 -0
  477. package/dist/views/Jobs/JobDrawer.js.map +1 -0
  478. package/dist/views/Jobs/JobsTable.d.ts +21 -0
  479. package/dist/views/Jobs/JobsTable.d.ts.map +1 -0
  480. package/dist/views/Jobs/JobsTable.js +81 -0
  481. package/dist/views/Jobs/JobsTable.js.map +1 -0
  482. package/dist/views/Jobs/NewBatchJob.d.ts +34 -0
  483. package/dist/views/Jobs/NewBatchJob.d.ts.map +1 -0
  484. package/dist/views/Jobs/NewBatchJob.js +282 -0
  485. package/dist/views/Jobs/NewBatchJob.js.map +1 -0
  486. package/dist/views/Jobs/WorkerStrip.d.ts +24 -0
  487. package/dist/views/Jobs/WorkerStrip.d.ts.map +1 -0
  488. package/dist/views/Jobs/WorkerStrip.js +63 -0
  489. package/dist/views/Jobs/WorkerStrip.js.map +1 -0
  490. package/dist/views/Jobs/atoms.d.ts +19 -0
  491. package/dist/views/Jobs/atoms.d.ts.map +1 -0
  492. package/dist/views/Jobs/atoms.js +52 -0
  493. package/dist/views/Jobs/atoms.js.map +1 -0
  494. package/dist/views/Jobs/focus.d.ts +29 -0
  495. package/dist/views/Jobs/focus.d.ts.map +1 -0
  496. package/dist/views/Jobs/focus.js +64 -0
  497. package/dist/views/Jobs/focus.js.map +1 -0
  498. package/dist/views/Jobs/icons.d.ts +22 -0
  499. package/dist/views/Jobs/icons.d.ts.map +1 -0
  500. package/dist/views/Jobs/icons.js +23 -0
  501. package/dist/views/Jobs/icons.js.map +1 -0
  502. package/dist/views/Jobs/useJobDetail.d.ts +21 -0
  503. package/dist/views/Jobs/useJobDetail.d.ts.map +1 -0
  504. package/dist/views/Jobs/useJobDetail.js +39 -0
  505. package/dist/views/Jobs/useJobDetail.js.map +1 -0
  506. package/dist/views/Jobs/useJobsFeed.d.ts +35 -0
  507. package/dist/views/Jobs/useJobsFeed.d.ts.map +1 -0
  508. package/dist/views/Jobs/useJobsFeed.js +231 -0
  509. package/dist/views/Jobs/useJobsFeed.js.map +1 -0
  510. package/dist/views/Jobs/useReviewItems.d.ts +39 -0
  511. package/dist/views/Jobs/useReviewItems.d.ts.map +1 -0
  512. package/dist/views/Jobs/useReviewItems.js +140 -0
  513. package/dist/views/Jobs/useReviewItems.js.map +1 -0
  514. package/dist/views/Jobs.d.ts +19 -1
  515. package/dist/views/Jobs.d.ts.map +1 -1
  516. package/dist/views/Jobs.js +204 -35
  517. package/dist/views/Jobs.js.map +1 -1
  518. package/dist/views/MediaBrowser.d.ts.map +1 -1
  519. package/dist/views/MediaBrowser.js +37 -9
  520. package/dist/views/MediaBrowser.js.map +1 -1
  521. package/dist/views/Pages/PagesListView.d.ts +1 -1
  522. package/dist/views/Pages/PagesListView.d.ts.map +1 -1
  523. package/dist/views/Pages/PagesListView.js +34 -5
  524. package/dist/views/Pages/PagesListView.js.map +1 -1
  525. package/dist/views/Posts/NewPostTypeInstructions.d.ts.map +1 -1
  526. package/dist/views/Posts/NewPostTypeInstructions.js +12 -2
  527. package/dist/views/Posts/NewPostTypeInstructions.js.map +1 -1
  528. package/dist/views/Posts/PostTypesView.d.ts.map +1 -1
  529. package/dist/views/Posts/PostTypesView.js +10 -1
  530. package/dist/views/Posts/PostTypesView.js.map +1 -1
  531. package/dist/views/Posts/PostsListView.d.ts +1 -2
  532. package/dist/views/Posts/PostsListView.d.ts.map +1 -1
  533. package/dist/views/Posts/PostsListView.js +31 -3
  534. package/dist/views/Posts/PostsListView.js.map +1 -1
  535. package/dist/views/SEO.d.ts.map +1 -1
  536. package/dist/views/SEO.js +3 -3
  537. package/dist/views/SEO.js.map +1 -1
  538. package/dist/views/ScriptTags.d.ts.map +1 -1
  539. package/dist/views/ScriptTags.js +20 -15
  540. package/dist/views/ScriptTags.js.map +1 -1
  541. package/dist/views/Settings.d.ts.map +1 -1
  542. package/dist/views/Settings.js +115 -51
  543. package/dist/views/Settings.js.map +1 -1
  544. package/dist/views/SpamFolder.d.ts +2 -0
  545. package/dist/views/SpamFolder.d.ts.map +1 -0
  546. package/dist/views/SpamFolder.js +14 -0
  547. package/dist/views/SpamFolder.js.map +1 -0
  548. package/dist/views/Trash.d.ts +6 -1
  549. package/dist/views/Trash.d.ts.map +1 -1
  550. package/dist/views/Trash.js +7 -2
  551. package/dist/views/Trash.js.map +1 -1
  552. package/dist/views/Users.js +2 -2
  553. package/dist/views/Users.js.map +1 -1
  554. package/dist/views/WidgetsPlaceholder.d.ts +2 -0
  555. package/dist/views/WidgetsPlaceholder.d.ts.map +1 -0
  556. package/dist/views/WidgetsPlaceholder.js +15 -0
  557. package/dist/views/WidgetsPlaceholder.js.map +1 -0
  558. package/dist/views/dashboard/kpi-grid.d.ts +18 -0
  559. package/dist/views/dashboard/kpi-grid.d.ts.map +1 -0
  560. package/dist/views/dashboard/kpi-grid.js +36 -0
  561. package/dist/views/dashboard/kpi-grid.js.map +1 -0
  562. package/dist/views/header-footer/AnnouncementTab.d.ts +15 -0
  563. package/dist/views/header-footer/AnnouncementTab.d.ts.map +1 -0
  564. package/dist/views/header-footer/AnnouncementTab.js +43 -0
  565. package/dist/views/header-footer/AnnouncementTab.js.map +1 -0
  566. package/dist/views/header-footer/ChromeField.d.ts +25 -0
  567. package/dist/views/header-footer/ChromeField.d.ts.map +1 -0
  568. package/dist/views/header-footer/ChromeField.js +99 -0
  569. package/dist/views/header-footer/ChromeField.js.map +1 -0
  570. package/dist/views/header-footer/ChromePreview.d.ts +31 -0
  571. package/dist/views/header-footer/ChromePreview.d.ts.map +1 -0
  572. package/dist/views/header-footer/ChromePreview.js +195 -0
  573. package/dist/views/header-footer/ChromePreview.js.map +1 -0
  574. package/dist/views/header-footer/DeveloperTab.d.ts +17 -0
  575. package/dist/views/header-footer/DeveloperTab.d.ts.map +1 -0
  576. package/dist/views/header-footer/DeveloperTab.js +98 -0
  577. package/dist/views/header-footer/DeveloperTab.js.map +1 -0
  578. package/dist/views/header-footer/RegionTab.d.ts +27 -0
  579. package/dist/views/header-footer/RegionTab.d.ts.map +1 -0
  580. package/dist/views/header-footer/RegionTab.js +80 -0
  581. package/dist/views/header-footer/RegionTab.js.map +1 -0
  582. package/dist/views/header-footer/chrome-form.d.ts +137 -0
  583. package/dist/views/header-footer/chrome-form.d.ts.map +1 -0
  584. package/dist/views/header-footer/chrome-form.js +518 -0
  585. package/dist/views/header-footer/chrome-form.js.map +1 -0
  586. package/dist/views/header-footer/code-samples.d.ts +30 -0
  587. package/dist/views/header-footer/code-samples.d.ts.map +1 -0
  588. package/dist/views/header-footer/code-samples.js +127 -0
  589. package/dist/views/header-footer/code-samples.js.map +1 -0
  590. package/dist/views/header-footer/controls.d.ts +57 -0
  591. package/dist/views/header-footer/controls.d.ts.map +1 -0
  592. package/dist/views/header-footer/controls.js +43 -0
  593. package/dist/views/header-footer/controls.js.map +1 -0
  594. package/dist/views/lazy-views.d.ts +4 -0
  595. package/dist/views/lazy-views.d.ts.map +1 -1
  596. package/dist/views/lazy-views.js +4 -0
  597. package/dist/views/lazy-views.js.map +1 -1
  598. package/dist/views/page-editor/PageTab.js +1 -1
  599. package/dist/views/page-editor/PageTab.js.map +1 -1
  600. package/dist/views/page-editor/ReviewBar.js +1 -1
  601. package/dist/views/page-editor/ReviewBar.js.map +1 -1
  602. package/dist/views/page-editor/VersionHistoryPanel.js +1 -1
  603. package/dist/views/page-editor/VersionHistoryPanel.js.map +1 -1
  604. package/dist/views/seo/AuditTab.js +1 -1
  605. package/dist/views/seo/AuditTab.js.map +1 -1
  606. package/dist/views/seo/OverviewTab.d.ts.map +1 -1
  607. package/dist/views/seo/OverviewTab.js +2 -2
  608. package/dist/views/seo/OverviewTab.js.map +1 -1
  609. package/dist/views/seo/ProposalsTab.js +1 -1
  610. package/dist/views/seo/ProposalsTab.js.map +1 -1
  611. package/dist/views/settings/AISettingsTab.d.ts.map +1 -1
  612. package/dist/views/settings/AISettingsTab.js +4 -2
  613. package/dist/views/settings/AISettingsTab.js.map +1 -1
  614. package/dist/views/settings/AccessProtectionCard.js +1 -1
  615. package/dist/views/settings/AccessProtectionCard.js.map +1 -1
  616. package/dist/views/settings/AdminThemeCard.d.ts.map +1 -1
  617. package/dist/views/settings/AdminThemeCard.js +63 -15
  618. package/dist/views/settings/AdminThemeCard.js.map +1 -1
  619. package/dist/views/settings/AppearanceTab.d.ts.map +1 -1
  620. package/dist/views/settings/AppearanceTab.js +4 -2
  621. package/dist/views/settings/AppearanceTab.js.map +1 -1
  622. package/dist/views/settings/BrandPreviewCard.js +1 -1
  623. package/dist/views/settings/BrandPreviewCard.js.map +1 -1
  624. package/dist/views/settings/BrandVoiceCard.js +1 -1
  625. package/dist/views/settings/BrandVoiceCard.js.map +1 -1
  626. package/dist/views/settings/EmailSettingsTab.d.ts.map +1 -1
  627. package/dist/views/settings/EmailSettingsTab.js +5 -3
  628. package/dist/views/settings/EmailSettingsTab.js.map +1 -1
  629. package/dist/views/settings/GeneralSettingsTab.d.ts.map +1 -1
  630. package/dist/views/settings/GeneralSettingsTab.js +4 -2
  631. package/dist/views/settings/GeneralSettingsTab.js.map +1 -1
  632. package/dist/views/settings/ProviderConnectionCard.js +1 -1
  633. package/dist/views/settings/ProviderConnectionCard.js.map +1 -1
  634. package/dist/views/settings/SearchDataCard.js +1 -1
  635. package/dist/views/settings/SearchDataCard.js.map +1 -1
  636. package/dist/views/settings/SecurityStatusCard.js +2 -2
  637. package/dist/views/settings/SecurityStatusCard.js.map +1 -1
  638. package/dist/views/settings/SecurityTab.d.ts.map +1 -1
  639. package/dist/views/settings/SecurityTab.js +4 -2
  640. package/dist/views/settings/SecurityTab.js.map +1 -1
  641. package/dist/views/settings/SeoAutopilotCard.js +2 -2
  642. package/dist/views/settings/SeoAutopilotCard.js.map +1 -1
  643. package/dist/views/settings/SeoSettingsTab.d.ts.map +1 -1
  644. package/dist/views/settings/SeoSettingsTab.js +5 -3
  645. package/dist/views/settings/SeoSettingsTab.js.map +1 -1
  646. package/dist/views/settings/SiteIndexStatusCard.js +1 -1
  647. package/dist/views/settings/SiteIndexStatusCard.js.map +1 -1
  648. package/dist/views/settings/SiteThemeCard.js +1 -1
  649. package/dist/views/settings/UpdatesTab.d.ts.map +1 -1
  650. package/dist/views/settings/UpdatesTab.js +6 -5
  651. package/dist/views/settings/UpdatesTab.js.map +1 -1
  652. package/dist/views/settings/components.d.ts +7 -11
  653. package/dist/views/settings/components.d.ts.map +1 -1
  654. package/dist/views/settings/components.js +18 -12
  655. package/dist/views/settings/components.js.map +1 -1
  656. package/dist/views/settings/useAppearanceSettings.d.ts +7 -1
  657. package/dist/views/settings/useAppearanceSettings.d.ts.map +1 -1
  658. package/dist/views/settings/useAppearanceSettings.js +89 -22
  659. package/dist/views/settings/useAppearanceSettings.js.map +1 -1
  660. package/dist/views/users/UserDetailDrawer.js +1 -1
  661. package/dist/views/users/UserDetailDrawer.js.map +1 -1
  662. package/package.json +10 -8
  663. package/src/AdminRoot.tsx +103 -31
  664. package/src/LoginRoot.tsx +2 -1
  665. package/src/__tests__/components/author-avatar.test.tsx +3 -20
  666. package/src/__tests__/components/notification-bell.test.tsx +146 -14
  667. package/src/__tests__/components/responsive-tiers.render.test.tsx +461 -0
  668. package/src/__tests__/components/serp-preview.render.test.tsx +6 -3
  669. package/src/__tests__/components/shell-primitives.render.test.tsx +951 -0
  670. package/src/__tests__/components/theme-provider-accent.test.tsx +83 -8
  671. package/src/__tests__/components/toggle.test.tsx +11 -3
  672. package/src/__tests__/components/ui-primitives.test.tsx +5 -8
  673. package/src/__tests__/layout/build-nav-items.test.ts +1 -1
  674. package/src/__tests__/layout/shell-nav.render.test.tsx +983 -0
  675. package/src/__tests__/layout/sidebar-forms-submenu.render.test.tsx +8 -4
  676. package/src/__tests__/layout/sidebar-groups.render.test.tsx +116 -0
  677. package/src/__tests__/layout/sidebar-menus.render.test.tsx +116 -0
  678. package/src/__tests__/layout/sidebar-plugin-nav.test.ts +1 -1
  679. package/src/__tests__/layout/sidebar-posts.test.ts +48 -40
  680. package/src/__tests__/layout/sidebar-submenu.render.test.tsx +35 -10
  681. package/src/__tests__/layout/topbar.render.test.tsx +464 -0
  682. package/src/__tests__/lib/api.test.ts +2 -0
  683. package/src/__tests__/lib/batch-wizard.test.ts +199 -0
  684. package/src/__tests__/lib/job-drawer.test.ts +495 -0
  685. package/src/__tests__/lib/job-view.test.ts +558 -0
  686. package/src/__tests__/lib/jobs-service.test.ts +167 -0
  687. package/src/__tests__/router/admin-route-order.test.ts +39 -0
  688. package/src/__tests__/styles/ai-gradient-contrast.test.ts +135 -0
  689. package/src/__tests__/styles/breakpoint-order.test.ts +68 -0
  690. package/src/__tests__/styles/responsive-layer.test.ts +367 -12
  691. package/src/__tests__/styles/tailwind-preset.test.ts +154 -0
  692. package/src/__tests__/styles/theme-token-scope.test.ts +10 -3
  693. package/src/__tests__/styles/tiers.test.ts +136 -0
  694. package/src/__tests__/styles/tokens-drift.test.ts +67 -6
  695. package/src/__tests__/views/admin-lazy-route.render.test.tsx +41 -1
  696. package/src/__tests__/views/ai-settings.render.test.tsx +4 -4
  697. package/src/__tests__/views/appearance-chrome.render.test.tsx +818 -0
  698. package/src/__tests__/views/appearance-menus-route.render.test.tsx +58 -0
  699. package/src/__tests__/views/appearance-settings.render.test.tsx +10 -8
  700. package/src/__tests__/views/appearance-theme-provider.render.test.tsx +347 -0
  701. package/src/__tests__/views/dashboard.test.tsx +48 -0
  702. package/src/__tests__/views/document-edit-navigation.render.test.tsx +130 -0
  703. package/src/__tests__/views/document-edit-routes.render.test.tsx +130 -0
  704. package/src/__tests__/views/email-settings.render.test.tsx +2 -2
  705. package/src/__tests__/views/form-entries.render.test.tsx +22 -0
  706. package/src/__tests__/views/form-submissions.render.test.tsx +22 -0
  707. package/src/__tests__/views/forms-list.render.test.tsx +13 -0
  708. package/src/__tests__/views/general-settings.render.test.tsx +38 -11
  709. package/src/__tests__/views/header-footer-form.test.ts +333 -0
  710. package/src/__tests__/views/jobs-drawer.render.test.tsx +1553 -0
  711. package/src/__tests__/views/jobs-list.render.test.tsx +853 -0
  712. package/src/__tests__/views/jobs-wizard.render.test.tsx +707 -0
  713. package/src/__tests__/views/kpi-grid.test.ts +29 -0
  714. package/src/__tests__/views/post-section-editor.test.tsx +5 -1
  715. package/src/__tests__/views/script-tags.render.test.tsx +29 -0
  716. package/src/__tests__/views/security-settings.render.test.tsx +9 -10
  717. package/src/__tests__/views/seo-settings.render.test.tsx +7 -9
  718. package/src/__tests__/views/settings-save-bar.render.test.tsx +91 -0
  719. package/src/__tests__/views/settings-save-footer.render.test.tsx +25 -15
  720. package/src/__tests__/views/settings-tabs.render.test.tsx +371 -0
  721. package/src/__tests__/views/updates-tab.render.test.tsx +4 -3
  722. package/src/components/Breadcrumbs.tsx +7 -1
  723. package/src/components/CommandPalette.tsx +51 -3
  724. package/src/components/LocaleSwitcher.tsx +5 -6
  725. package/src/components/NotificationBell.tsx +74 -18
  726. package/src/components/OfflineStatus.tsx +1 -1
  727. package/src/components/PresenceIndicator.tsx +2 -1
  728. package/src/components/ReflowMeter.tsx +3 -3
  729. package/src/components/SEOPanel.tsx +2 -2
  730. package/src/components/ThemeProvider.tsx +122 -42
  731. package/src/components/TipTapEditor.tsx +3 -4
  732. package/src/components/forms/EmbedPanel.tsx +1 -1
  733. package/src/components/forms/FormEntryDetailPane.tsx +7 -1
  734. package/src/components/forms/ShortcodeGlossary.tsx +7 -1
  735. package/src/components/forms/primitives.tsx +37 -21
  736. package/src/components/pages/tag-badges.tsx +4 -3
  737. package/src/components/seo/PageAuditPanel.tsx +2 -2
  738. package/src/components/seo/SerpPreview.tsx +5 -5
  739. package/src/components/seo/primitives.tsx +2 -2
  740. package/src/components/ui/AuthorAvatar.tsx +11 -10
  741. package/src/components/ui/Badge.tsx +1 -1
  742. package/src/components/ui/Breadcrumb.tsx +109 -0
  743. package/src/components/ui/Button.tsx +40 -15
  744. package/src/components/ui/Card.tsx +35 -32
  745. package/src/components/ui/CardListControls.tsx +90 -0
  746. package/src/components/ui/CountChip.tsx +90 -0
  747. package/src/components/ui/FilterPill.tsx +5 -3
  748. package/src/components/ui/InspectorPane.tsx +21 -7
  749. package/src/components/ui/Modal.tsx +20 -6
  750. package/src/components/ui/PageHeader.tsx +63 -0
  751. package/src/components/ui/RowActions.tsx +7 -2
  752. package/src/components/ui/Segmented.tsx +97 -0
  753. package/src/components/ui/Toaster.tsx +139 -0
  754. package/src/components/ui/Toggle.tsx +18 -14
  755. package/src/components/ui/UnderlineTabs.tsx +373 -0
  756. package/src/components/ui/UnsavedBar.tsx +119 -0
  757. package/src/components/ui/admin-links.tsx +20 -0
  758. package/src/components/ui/card-table.ts +34 -0
  759. package/src/components/virtual-window.tsx +13 -2
  760. package/src/fields/RelationshipField.tsx +1 -1
  761. package/src/hooks/useAppearanceBootstrap.ts +9 -3
  762. package/src/index.ts +19 -0
  763. package/src/layout/Header.tsx +210 -135
  764. package/src/layout/Layout.tsx +61 -108
  765. package/src/layout/MobileTabBar.tsx +92 -0
  766. package/src/layout/Sidebar.tsx +555 -593
  767. package/src/layout/nav-config.tsx +425 -0
  768. package/src/layout/primitives/AdminShell.tsx +154 -6
  769. package/src/layout/topbar-classes.ts +8 -0
  770. package/src/layout/use-nav-items.ts +40 -0
  771. package/src/lib/api.ts +18 -1
  772. package/src/lib/appearance-api.ts +4 -0
  773. package/src/lib/batch-wizard.ts +188 -0
  774. package/src/lib/chrome-service.ts +200 -0
  775. package/src/lib/job-drawer.ts +424 -0
  776. package/src/lib/job-view.ts +550 -0
  777. package/src/lib/jobs-service.ts +345 -0
  778. package/src/lib/sheet-focus.ts +71 -0
  779. package/src/lib/tiers.ts +43 -0
  780. package/src/router/admin-routes.ts +8 -0
  781. package/src/router/index.ts +9 -1
  782. package/src/styles/prenta-tokens.css +280 -82
  783. package/src/styles/responsive.css +460 -22
  784. package/src/styles/theme.css +192 -28
  785. package/src/views/ApiKeys.tsx +2 -2
  786. package/src/views/AppearanceChrome.tsx +732 -0
  787. package/src/views/CollectionList.tsx +264 -205
  788. package/src/views/Dashboard.tsx +26 -13
  789. package/src/views/DocumentEdit.tsx +111 -56
  790. package/src/views/FormDeliveries.tsx +30 -17
  791. package/src/views/FormEntries.tsx +28 -20
  792. package/src/views/FormSubmissions.tsx +32 -34
  793. package/src/views/Forms.tsx +55 -34
  794. package/src/views/FormsSettings.tsx +94 -0
  795. package/src/views/Jobs/BusyButton.tsx +56 -0
  796. package/src/views/Jobs/CancelJobDialog.tsx +145 -0
  797. package/src/views/Jobs/DrawerActivity.tsx +142 -0
  798. package/src/views/Jobs/DrawerOverview.tsx +139 -0
  799. package/src/views/Jobs/DrawerResults.tsx +155 -0
  800. package/src/views/Jobs/DrawerReview.tsx +464 -0
  801. package/src/views/Jobs/JobDrawer.tsx +636 -0
  802. package/src/views/Jobs/JobsTable.tsx +267 -0
  803. package/src/views/Jobs/NewBatchJob.tsx +857 -0
  804. package/src/views/Jobs/WorkerStrip.tsx +149 -0
  805. package/src/views/Jobs/atoms.tsx +93 -0
  806. package/src/views/Jobs/focus.ts +67 -0
  807. package/src/views/Jobs/icons.tsx +111 -0
  808. package/src/views/Jobs/useJobDetail.ts +64 -0
  809. package/src/views/Jobs/useJobsFeed.ts +249 -0
  810. package/src/views/Jobs/useReviewItems.ts +187 -0
  811. package/src/views/Jobs.tsx +396 -105
  812. package/src/views/MediaBrowser.tsx +71 -21
  813. package/src/views/Pages/PagesListView.tsx +115 -39
  814. package/src/views/Posts/NewPostTypeInstructions.tsx +15 -10
  815. package/src/views/Posts/PostTypesView.tsx +28 -6
  816. package/src/views/Posts/PostsListView.tsx +130 -40
  817. package/src/views/SEO.tsx +23 -8
  818. package/src/views/ScriptTags.tsx +233 -153
  819. package/src/views/Settings.tsx +215 -256
  820. package/src/views/SpamFolder.tsx +32 -0
  821. package/src/views/Trash.tsx +12 -1
  822. package/src/views/Users.tsx +2 -2
  823. package/src/views/WidgetsPlaceholder.tsx +33 -0
  824. package/src/views/dashboard/kpi-grid.ts +38 -0
  825. package/src/views/header-footer/AnnouncementTab.tsx +231 -0
  826. package/src/views/header-footer/ChromeField.tsx +358 -0
  827. package/src/views/header-footer/ChromePreview.tsx +497 -0
  828. package/src/views/header-footer/DeveloperTab.tsx +331 -0
  829. package/src/views/header-footer/RegionTab.tsx +398 -0
  830. package/src/views/header-footer/chrome-form.ts +603 -0
  831. package/src/views/header-footer/code-samples.ts +146 -0
  832. package/src/views/header-footer/controls.tsx +166 -0
  833. package/src/views/lazy-views.tsx +12 -0
  834. package/src/views/page-editor/PageTab.tsx +2 -2
  835. package/src/views/page-editor/ReviewBar.tsx +1 -1
  836. package/src/views/page-editor/VersionHistoryPanel.tsx +1 -1
  837. package/src/views/seo/AuditTab.tsx +1 -1
  838. package/src/views/seo/OverviewTab.tsx +6 -5
  839. package/src/views/seo/ProposalsTab.tsx +1 -1
  840. package/src/views/settings/AISettingsTab.tsx +10 -6
  841. package/src/views/settings/AccessProtectionCard.tsx +1 -1
  842. package/src/views/settings/AdminThemeCard.tsx +228 -35
  843. package/src/views/settings/AppearanceTab.tsx +10 -6
  844. package/src/views/settings/BrandPreviewCard.tsx +1 -1
  845. package/src/views/settings/BrandVoiceCard.tsx +1 -1
  846. package/src/views/settings/EmailSettingsTab.tsx +11 -7
  847. package/src/views/settings/GeneralSettingsTab.tsx +10 -6
  848. package/src/views/settings/ProviderConnectionCard.tsx +1 -1
  849. package/src/views/settings/SearchDataCard.tsx +1 -1
  850. package/src/views/settings/SecurityStatusCard.tsx +2 -2
  851. package/src/views/settings/SecurityTab.tsx +10 -6
  852. package/src/views/settings/SeoAutopilotCard.tsx +2 -2
  853. package/src/views/settings/SeoSettingsTab.tsx +13 -9
  854. package/src/views/settings/SiteIndexStatusCard.tsx +1 -1
  855. package/src/views/settings/SiteThemeCard.tsx +1 -1
  856. package/src/views/settings/UpdatesTab.tsx +107 -101
  857. package/src/views/settings/components.tsx +26 -62
  858. package/src/views/settings/useAppearanceSettings.ts +116 -25
  859. package/src/views/users/UserDetailDrawer.tsx +1 -1
  860. package/dist/components/ui/StatPill.d.ts +0 -18
  861. package/dist/components/ui/StatPill.d.ts.map +0 -1
  862. package/dist/components/ui/StatPill.js +0 -5
  863. package/dist/components/ui/StatPill.js.map +0 -1
  864. package/src/components/ui/StatPill.tsx +0 -37
@@ -0,0 +1,857 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useId, useMemo, useRef, useState, type ReactNode } from 'react'
4
+ import * as Dialog from '@radix-ui/react-dialog'
5
+ import type { BatchAiEstimate, BatchAiPreviewItem, BatchAiTextField, Job } from '@prenta/core'
6
+ import { Button } from '../../components/ui/Button.js'
7
+ import {
8
+ CUSTOM_FIELDS,
9
+ INSTRUCTION_MAX,
10
+ MEDIA_SOURCE,
11
+ batchOp,
12
+ batchRequest,
13
+ batchSources,
14
+ estimateKey,
15
+ estimateNote,
16
+ estimateTiles,
17
+ instructionError,
18
+ isAdminRole,
19
+ matchProblem,
20
+ operationFits,
21
+ previewKey,
22
+ runsOnMedia,
23
+ type BatchDraft,
24
+ type BatchOperation,
25
+ type BatchSelection,
26
+ } from '../../lib/batch-wizard.js'
27
+ import { JOB_OPS } from '../../lib/job-view.js'
28
+ import { createBatchAiJob, estimateBatchAi, previewBatchAi } from '../../lib/jobs-service.js'
29
+ import { adminPortalContainer } from '../../lib/portal-container.js'
30
+ import { BusyButton } from './BusyButton.js'
31
+ import { LinkLine } from './DrawerReview.js'
32
+ import { useReturnFocus } from './focus.js'
33
+ import { JobIcons } from './icons.js'
34
+
35
+ const STEPS = ['What to change', 'Which documents', 'Review & queue'] as const
36
+ const AUTO_ADMIN_ONLY = 'Only an admin can apply changes automatically.'
37
+ /** The server's reason, cms-core `parseBatchAiRequest`. */
38
+ const AUTO_KEEPS_STATUS =
39
+ 'Applying automatically keeps each document’s status. To publish, review the changes and approve them with Publish.'
40
+
41
+ /* jobs-new.jsx `.nb-*`, as utilities. */
42
+ const LABEL_TEXT = 'text-sm2 text-txt font-semibold leading-[normal]'
43
+ const LABEL = `${LABEL_TEXT} mb-[5px] block p-0`
44
+ const HELP = 'text-hint text-faint'
45
+ /*
46
+ * `.nb-in`. Its focus ring (2px --acc-m, no offset, an --acc border) replaces
47
+ * the admin-wide one, which is unlayered, hence the `!`.
48
+ */
49
+ const FIELD =
50
+ 'rounded-8 border-bdr bg-card text-txt text-body focus:border-acc w-full border px-2.5 focus:outline-2! focus:outline-offset-0! focus:outline-[var(--acc-m)]! disabled:cursor-not-allowed disabled:opacity-60'
51
+ const CARD =
52
+ 'rounded-10 border-bdr text-body text-txt font-normal leading-[normal] has-checked:border-acc has-checked:bg-acc-l has-focus-visible:outline-acc flex cursor-pointer items-start gap-2.5 border px-3 py-2.5 has-disabled:cursor-not-allowed has-disabled:opacity-55 has-focus-visible:outline-2 has-focus-visible:outline-offset-2'
53
+ const RADIO = 'accent-acc mt-[3px] shrink-0'
54
+ const SMALL = 'text-meta text-sub mt-px block'
55
+
56
+ interface EstimateState {
57
+ key: string
58
+ value: BatchAiEstimate | null
59
+ loading: boolean
60
+ error: string | null
61
+ }
62
+
63
+ interface PreviewState {
64
+ key: string
65
+ items: BatchAiPreviewItem[] | null
66
+ loading: boolean
67
+ error: string | null
68
+ }
69
+
70
+ export interface NewBatchJobProps {
71
+ /** The signed-in user's role. Only OWNER and ADMIN may apply changes automatically. */
72
+ role?: string | null
73
+ /** The CMS config; its page and post collections fill the Content select. */
74
+ config?: unknown
75
+ /**
76
+ * Documents picked in a list (P-21's "Run with AI…"). Step 2 then shows
77
+ * "{n} selected", Content is locked to their source, and they run without
78
+ * the operation's "Only where" preset.
79
+ */
80
+ selection?: BatchSelection | null
81
+ /** The operation to start on. Defaults to alt text for picked images, else meta descriptions. */
82
+ operation?: BatchOperation
83
+ onClose: () => void
84
+ /** The job the server queued. The wizard closes after this. */
85
+ onQueued: (job: Job) => void
86
+ /**
87
+ * Where focus goes on close when the control that opened the wizard is
88
+ * gone, or after a job was queued (the Jobs list focuses its new row).
89
+ */
90
+ onFocusFallback?: (job: Job | null) => void
91
+ }
92
+
93
+ /** The step bar: 3px segments, the current one in the accent, finished ones filled. */
94
+ function StepBar({ step }: { step: number }) {
95
+ return (
96
+ <ol aria-label="Steps" className="m-0 flex list-none gap-1.5 px-5 pt-3">
97
+ {STEPS.map((label, i) => (
98
+ <li
99
+ key={label}
100
+ aria-current={i === step ? 'step' : undefined}
101
+ className={`text-hint flex min-w-0 flex-1 flex-col gap-[5px] font-semibold ${
102
+ i === step ? 'text-acc' : i < step ? 'text-sub' : 'text-faint'
103
+ }`}
104
+ >
105
+ <i
106
+ aria-hidden="true"
107
+ className={`h-[3px] rounded-[2px] ${i <= step ? 'bg-acc' : 'bg-bdr'}`}
108
+ />
109
+ <span className="truncate">{label}</span>
110
+ </li>
111
+ ))}
112
+ </ol>
113
+ )
114
+ }
115
+
116
+ function Spinner() {
117
+ return (
118
+ <i
119
+ aria-hidden="true"
120
+ className="inline-block size-3 shrink-0 rounded-full border-2 border-current border-r-transparent motion-safe:animate-spin"
121
+ />
122
+ )
123
+ }
124
+
125
+ /** The read-only "Only where" box: `.nb-in` as text. */
126
+ function OnlyWhere({ children }: { children: ReactNode }) {
127
+ return (
128
+ <div>
129
+ <span className={LABEL}>Only where</span>
130
+ <div className="rounded-8 border-bdr bg-card text-body text-sub flex min-h-9 items-center gap-2 border px-2.5 py-1.5">
131
+ {children}
132
+ </div>
133
+ </div>
134
+ )
135
+ }
136
+
137
+ /**
138
+ * The New batch AI job wizard (jobs-new.jsx, TRACKING P-20): what to change,
139
+ * which documents, then review and queue. A centred modal, 680px wide; a
140
+ * full-screen sheet on phones. The match count and sample come from
141
+ * `/jobs/batch-ai/estimate`, Try it from `/preview`, and Queue posts to
142
+ * `/jobs/batch-ai`. Radix traps focus and closes on Esc, except while the
143
+ * job is being queued.
144
+ */
145
+ export function NewBatchJob({
146
+ role,
147
+ config,
148
+ selection = null,
149
+ operation: initialOperation,
150
+ onClose,
151
+ onQueued,
152
+ onFocusFallback,
153
+ }: NewBatchJobProps) {
154
+ const ids = useId()
155
+ const sources = useMemo(() => batchSources(config), [config])
156
+ const admin = isAdminRole(role)
157
+
158
+ const [step, setStep] = useState(0)
159
+ const [operation, setOperation] = useState<BatchOperation>(
160
+ () => initialOperation ?? (selection?.source === MEDIA_SOURCE.id ? 'alt' : 'meta'),
161
+ )
162
+ const [instruction, setInstruction] = useState('')
163
+ const [field, setField] = useState<BatchAiTextField>('metaDescription')
164
+ const [collection, setCollection] = useState(() =>
165
+ selection && selection.source !== MEDIA_SOURCE.id ? selection.source : (sources[0]?.id ?? ''),
166
+ )
167
+ // Picked documents run as they are, drafts included.
168
+ const [statusFilter, setStatusFilter] = useState<'published' | 'all'>(
169
+ selection ? 'all' : 'published',
170
+ )
171
+ const [review, setReview] = useState<'review' | 'auto'>('review')
172
+ const [onApprove, setOnApprove] = useState<'draft' | 'publish'>('draft')
173
+ const [instructionTouched, setInstructionTouched] = useState(false)
174
+ const [estimate, setEstimate] = useState<EstimateState | null>(null)
175
+ const [estimateRun, setEstimateRun] = useState(0)
176
+ const [preview, setPreview] = useState<PreviewState | null>(null)
177
+ const [creating, setCreating] = useState(false)
178
+ const [createError, setCreateError] = useState<string | null>(null)
179
+ const [announce, setAnnounce] = useState('')
180
+
181
+ const queued = useRef<Job | null>(null)
182
+ const previewAbort = useRef<AbortController | null>(null)
183
+ const body = useRef<HTMLDivElement>(null)
184
+ const instructionRef = useRef<HTMLTextAreaElement>(null)
185
+ const stepMoved = useRef(false)
186
+ const returnFocus = useReturnFocus(() => onFocusFallback?.(null))
187
+
188
+ const op = batchOp(operation)
189
+ const onMedia = runsOnMedia(operation)
190
+ const draft: BatchDraft = {
191
+ operation,
192
+ instruction,
193
+ field,
194
+ collection,
195
+ statusFilter,
196
+ review,
197
+ onApprove,
198
+ selection,
199
+ }
200
+ const request = batchRequest(draft)
201
+ const eKey = estimateKey(request)
202
+ const pKey = previewKey(request)
203
+ const sourceId = request.source
204
+ const sourceLabel =
205
+ sourceId === MEDIA_SOURCE.id
206
+ ? MEDIA_SOURCE.label
207
+ : (sources.find((s) => s.id === sourceId)?.label ?? sourceId)
208
+ const instructionProblem = instructionError(operation, instruction)
209
+ const showInstructionError = instructionTouched && instructionProblem !== null
210
+
211
+ const current = estimate?.key === eKey ? estimate : null
212
+ const counted = current?.value ?? null
213
+ const problem = counted ? matchProblem(counted) : null
214
+ const canQueue = counted !== null && problem === null
215
+ const shownPreview = preview?.key === pKey ? preview : null
216
+
217
+ // The estimate, read when step 2 opens and again whenever what it counts changes.
218
+ const counting = step > 0
219
+ useEffect(() => {
220
+ if (!counting) return
221
+ const controller = new AbortController()
222
+ const key = eKey
223
+ setEstimate((prev) => ({
224
+ key,
225
+ value: prev?.key === key ? prev.value : null,
226
+ loading: true,
227
+ error: null,
228
+ }))
229
+ void estimateBatchAi(request, controller.signal).then((res) => {
230
+ if (controller.signal.aborted) return
231
+ setEstimate((prev) => ({
232
+ key,
233
+ value: res.ok ? res.value : prev?.key === key ? prev.value : null,
234
+ loading: false,
235
+ error: res.ok ? null : res.error,
236
+ }))
237
+ })
238
+ return () => controller.abort()
239
+ // `eKey` stands for what changes the count: operation, source, status and
240
+ // picked ids. The instruction and field can only change on step 1.
241
+ // eslint-disable-next-line react-hooks/exhaustive-deps
242
+ }, [counting, eKey, estimateRun])
243
+
244
+ useEffect(() => () => previewAbort.current?.abort(), [])
245
+
246
+ // A new step: focus its first control and say which step it is.
247
+ useEffect(() => {
248
+ if (!stepMoved.current) return
249
+ stepMoved.current = false
250
+ body.current?.scrollTo?.({ top: 0 })
251
+ focusFirstControl(body.current)
252
+ setAnnounce(`Step ${step + 1} of ${STEPS.length}: ${STEPS[step]}`)
253
+ }, [step])
254
+
255
+ function goTo(next: number) {
256
+ stepMoved.current = true
257
+ setCreateError(null)
258
+ setStep(next)
259
+ }
260
+
261
+ function pickOperation(next: BatchOperation) {
262
+ setOperation(next)
263
+ if (next !== 'custom') setInstructionTouched(false)
264
+ }
265
+
266
+ function pickReview(next: 'review' | 'auto') {
267
+ setReview(next)
268
+ // "Apply automatically" keeps each document's status, so it never publishes.
269
+ if (next === 'auto') setOnApprove('draft')
270
+ }
271
+
272
+ function onContinue() {
273
+ if (step === 0) {
274
+ if (instructionProblem) {
275
+ setInstructionTouched(true)
276
+ instructionRef.current?.focus()
277
+ return
278
+ }
279
+ goTo(1)
280
+ return
281
+ }
282
+ if (canQueue) goTo(2)
283
+ }
284
+
285
+ async function tryIt() {
286
+ previewAbort.current?.abort()
287
+ const controller = new AbortController()
288
+ previewAbort.current = controller
289
+ const key = pKey
290
+ setPreview((prev) => ({
291
+ key,
292
+ items: prev?.key === key ? prev.items : null,
293
+ loading: true,
294
+ error: null,
295
+ }))
296
+ const res = await previewBatchAi(request, controller.signal)
297
+ if (controller.signal.aborted) return
298
+ setPreview({
299
+ key,
300
+ items: res.ok ? res.value : null,
301
+ loading: false,
302
+ error: res.ok ? null : res.error,
303
+ })
304
+ }
305
+
306
+ async function queue() {
307
+ setCreating(true)
308
+ setCreateError(null)
309
+ const res = await createBatchAiJob(request)
310
+ if (!res.ok) {
311
+ setCreating(false)
312
+ setCreateError(res.error)
313
+ return
314
+ }
315
+ queued.current = res.value
316
+ previewAbort.current?.abort()
317
+ onQueued(res.value)
318
+ }
319
+
320
+ function close() {
321
+ if (creating) return
322
+ onClose()
323
+ }
324
+
325
+ const opsName = `${ids}-op`
326
+ const errorId = `${ids}-ins-err`
327
+ const autoTipId = `${ids}-auto-tip`
328
+ const publishHelpId = `${ids}-pub-help`
329
+
330
+ return (
331
+ <Dialog.Root open onOpenChange={(open) => (open ? undefined : close())}>
332
+ <Dialog.Portal container={adminPortalContainer()}>
333
+ <Dialog.Overlay className="bg-scrim fixed inset-0 z-[var(--z-modal)]" />
334
+ <Dialog.Content
335
+ onOpenAutoFocus={(event) => {
336
+ returnFocus.onOpenAutoFocus()
337
+ event.preventDefault()
338
+ focusFirstControl(body.current)
339
+ }}
340
+ onCloseAutoFocus={(event) => {
341
+ const job = queued.current
342
+ if (job && onFocusFallback) {
343
+ event.preventDefault()
344
+ onFocusFallback(job)
345
+ return
346
+ }
347
+ returnFocus.onCloseAutoFocus(event)
348
+ }}
349
+ // Radix makes the rest of the page inert but doesn't say so on the dialog.
350
+ aria-modal="true"
351
+ aria-describedby={undefined}
352
+ data-batch-wizard=""
353
+ className="bg-card border-bdr rounded-14 shadow-shd2 mobile:inset-0 mobile:h-full mobile:max-h-none mobile:w-full mobile:translate-x-0 mobile:translate-y-0 mobile:rounded-none mobile:border-0 mobile:shadow-none fixed top-1/2 left-1/2 z-[var(--z-modal)] flex max-h-[calc(100vh-32px)] w-[min(680px,calc(100%-32px))] -translate-x-1/2 -translate-y-1/2 flex-col border leading-[normal] outline-none"
354
+ >
355
+ <div className="border-bdr mobile:pt-[max(16px,env(safe-area-inset-top,0px))] flex items-center gap-3 border-b px-5 pt-4 pb-3">
356
+ <span className="text-acc flex">
357
+ <JobIcons.spark size={16} />
358
+ </span>
359
+ <Dialog.Title className="text-txt m-0 min-w-0 flex-1 text-[length:var(--fs-drawer)] leading-[normal] font-bold">
360
+ New batch AI job
361
+ </Dialog.Title>
362
+ <Dialog.Close asChild>
363
+ <Button
364
+ variant="icon"
365
+ size="icon"
366
+ aria-label="Close"
367
+ aria-disabled={creating || undefined}
368
+ className="aria-disabled:opacity-50"
369
+ >
370
+ <JobIcons.x size={15} />
371
+ </Button>
372
+ </Dialog.Close>
373
+ </div>
374
+
375
+ <StepBar step={step} />
376
+
377
+ <div
378
+ ref={body}
379
+ data-batch-step={step + 1}
380
+ className="flex min-h-0 flex-1 flex-col gap-3.5 overflow-auto px-5 py-4 *:shrink-0"
381
+ >
382
+ {step === 0 ? (
383
+ <>
384
+ <fieldset className="m-0 min-w-0 border-0 p-0">
385
+ <legend className="sr-only">Operation</legend>
386
+ <div className="grid grid-cols-2 gap-2 max-[560px]:grid-cols-1">
387
+ {JOB_OPS.map((x) => {
388
+ const fits = operationFits(x.id, selection)
389
+ return (
390
+ <label
391
+ key={x.id}
392
+ className="rounded-10 border-bdr bg-card has-checked:border-acc has-focus-visible:outline-acc flex cursor-pointer flex-col gap-[3px] border px-3 py-2.5 text-left leading-[normal] font-normal has-checked:shadow-[0_0_0_3px_var(--acc-l)] has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-disabled:cursor-not-allowed has-disabled:opacity-55"
393
+ >
394
+ <input
395
+ type="radio"
396
+ name={opsName}
397
+ value={x.id}
398
+ checked={operation === x.id}
399
+ disabled={!fits}
400
+ onChange={() => pickOperation(x.id)}
401
+ className="sr-only"
402
+ />
403
+ <b className="text-body text-txt font-semibold">{x.label}</b>
404
+ <span className="text-meta text-sub leading-[1.45]">{x.d}</span>
405
+ </label>
406
+ )
407
+ })}
408
+ </div>
409
+ </fieldset>
410
+ {operation === 'custom' ? (
411
+ <div>
412
+ <label className={LABEL} htmlFor={`${ids}-field`}>
413
+ Field
414
+ </label>
415
+ <select
416
+ id={`${ids}-field`}
417
+ className={`${FIELD} h-9`}
418
+ value={field}
419
+ onChange={(event) => setField(event.target.value as BatchAiTextField)}
420
+ >
421
+ {CUSTOM_FIELDS.map((f) => (
422
+ <option key={f.id} value={f.id}>
423
+ {f.label}
424
+ </option>
425
+ ))}
426
+ </select>
427
+ </div>
428
+ ) : null}
429
+ <div>
430
+ <label className={LABEL} htmlFor={`${ids}-ins`}>
431
+ {operation === 'custom' ? 'Instruction' : 'Extra guidance'}
432
+ {operation !== 'custom' ? (
433
+ <span className="text-faint font-normal"> (optional)</span>
434
+ ) : null}
435
+ </label>
436
+ <textarea
437
+ id={`${ids}-ins`}
438
+ ref={instructionRef}
439
+ className={`${FIELD} block min-h-16 resize-y py-2 ${
440
+ showInstructionError ? 'border-err focus:border-err' : ''
441
+ }`}
442
+ value={instruction}
443
+ maxLength={INSTRUCTION_MAX}
444
+ aria-invalid={showInstructionError || undefined}
445
+ aria-describedby={showInstructionError ? errorId : undefined}
446
+ onChange={(event) => setInstruction(event.target.value)}
447
+ placeholder={
448
+ operation === 'custom'
449
+ ? 'e.g. Translate the excerpt to Spanish. Keep brand names in English.'
450
+ : 'e.g. Plain and specific. Mention Hampton Roads only on local posts.'
451
+ }
452
+ />
453
+ {showInstructionError ? (
454
+ <p id={errorId} className="text-hint text-err m-0 mt-1">
455
+ {instructionProblem}
456
+ </p>
457
+ ) : null}
458
+ {/* 9px: the kit's textarea is inline, so its line adds 5px to the help's 4px margin. */}
459
+ <div className={`${HELP} mt-[9px]`}>
460
+ The AI also follows your brand voice from Settings → AI.
461
+ </div>
462
+ </div>
463
+ </>
464
+ ) : null}
465
+
466
+ {step === 1 ? (
467
+ <>
468
+ <div className="grid grid-cols-2 gap-2.5 max-[560px]:grid-cols-1">
469
+ <div className="min-w-0">
470
+ <label className={LABEL} htmlFor={`${ids}-src`}>
471
+ Content
472
+ </label>
473
+ <select
474
+ id={`${ids}-src`}
475
+ className={`${FIELD} h-9`}
476
+ value={sourceId}
477
+ disabled={onMedia || selection !== null}
478
+ onChange={(event) => setCollection(event.target.value)}
479
+ >
480
+ {onMedia || selection ? (
481
+ <option value={sourceId}>{sourceLabel}</option>
482
+ ) : (
483
+ sources.map((s) => (
484
+ <option key={s.id} value={s.id}>
485
+ {s.label}
486
+ </option>
487
+ ))
488
+ )}
489
+ </select>
490
+ </div>
491
+ <div className="min-w-0">
492
+ <label className={LABEL} htmlFor={`${ids}-pub`}>
493
+ Status
494
+ </label>
495
+ {/* Images have no publish state. */}
496
+ <select
497
+ id={`${ids}-pub`}
498
+ className={`${FIELD} h-9`}
499
+ value={statusFilter}
500
+ disabled={onMedia}
501
+ onChange={(event) =>
502
+ setStatusFilter(event.target.value as 'published' | 'all')
503
+ }
504
+ >
505
+ <option value="published">Published only</option>
506
+ <option value="all">Published and drafts</option>
507
+ </select>
508
+ </div>
509
+ </div>
510
+ <OnlyWhere>
511
+ {selection ? (
512
+ <b className="text-txt">{selection.documentIds.length} selected</b>
513
+ ) : (
514
+ <>
515
+ <span>
516
+ {operation === 'custom'
517
+ ? (CUSTOM_FIELDS.find((f) => f.id === field)?.label ?? op.field)
518
+ : op.field}
519
+ :
520
+ </span>
521
+ <b className="text-txt">{op.filter}</b>
522
+ </>
523
+ )}
524
+ </OnlyWhere>
525
+ <MatchBox
526
+ estimate={current}
527
+ sourceLabel={sourceLabel}
528
+ problem={problem}
529
+ onRetry={() => setEstimateRun((n) => n + 1)}
530
+ />
531
+ </>
532
+ ) : null}
533
+
534
+ {step === 2 && counted ? (
535
+ <>
536
+ <fieldset className="m-0 min-w-0 border-0 p-0">
537
+ <legend className={LABEL}>Before anything changes</legend>
538
+ <div className="flex flex-col gap-1.5">
539
+ <label className={CARD}>
540
+ <input
541
+ type="radio"
542
+ name={`${ids}-r`}
543
+ className={RADIO}
544
+ checked={review === 'review'}
545
+ onChange={() => pickReview('review')}
546
+ />
547
+ <span>
548
+ Review each change
549
+ <em className="text-count text-acc ml-1.5 font-bold not-italic">
550
+ RECOMMENDED
551
+ </em>
552
+ <small className={SMALL}>
553
+ Suggestions wait in the job’s Review tab. Approve, edit or reject each
554
+ one.
555
+ </small>
556
+ </span>
557
+ </label>
558
+ {/* The tooltip sits beside the label, so the disabled card's dimming doesn't reach it. */}
559
+ <div className="group relative">
560
+ <label className={CARD}>
561
+ <input
562
+ type="radio"
563
+ name={`${ids}-r`}
564
+ className={RADIO}
565
+ checked={review === 'auto'}
566
+ disabled={!admin}
567
+ aria-describedby={admin ? undefined : autoTipId}
568
+ onChange={() => pickReview('auto')}
569
+ />
570
+ <span>
571
+ Apply automatically
572
+ <small className={SMALL}>
573
+ Admins only. Every change is saved to the document’s history, so it can
574
+ be undone.
575
+ </small>
576
+ </span>
577
+ </label>
578
+ {admin ? null : (
579
+ <span
580
+ role="tooltip"
581
+ id={autoTipId}
582
+ className="rounded-8 bg-txt text-sb text-hint shadow-shd2 pointer-events-none invisible absolute bottom-[calc(100%+6px)] left-3 z-[1] px-2.5 py-2 leading-[1.55] opacity-0 transition-opacity group-hover:visible group-hover:opacity-100"
583
+ >
584
+ {AUTO_ADMIN_ONLY}
585
+ </span>
586
+ )}
587
+ </div>
588
+ </div>
589
+ </fieldset>
590
+ <fieldset className="m-0 min-w-0 border-0 p-0">
591
+ <legend className={LABEL}>
592
+ {review === 'review' ? 'When a change is approved' : 'When a change is made'}
593
+ </legend>
594
+ <div className="flex flex-row flex-wrap gap-1.5">
595
+ {(
596
+ [
597
+ ['draft', 'Save as draft', 'Someone publishes later'],
598
+ ['publish', 'Publish right away', 'Goes live on the site'],
599
+ ] as const
600
+ ).map(([k, l, s]) => (
601
+ <label key={k} className={`${CARD} flex-[1_1_200px]`}>
602
+ <input
603
+ type="radio"
604
+ name={`${ids}-a`}
605
+ className={RADIO}
606
+ checked={onApprove === k}
607
+ disabled={k === 'publish' && review === 'auto'}
608
+ aria-describedby={
609
+ k === 'publish' && review === 'auto' ? publishHelpId : undefined
610
+ }
611
+ onChange={() => setOnApprove(k)}
612
+ />
613
+ <span>
614
+ {l}
615
+ <small className={SMALL}>{s}</small>
616
+ </span>
617
+ </label>
618
+ ))}
619
+ </div>
620
+ {review === 'auto' ? (
621
+ <p id={publishHelpId} className={`${HELP} mt-1`}>
622
+ {AUTO_KEEPS_STATUS}
623
+ </p>
624
+ ) : null}
625
+ </fieldset>
626
+ <EstimateTiles estimate={counted} />
627
+ <div>
628
+ <div className="mb-1.5 flex items-center gap-2">
629
+ <span className={`${LABEL_TEXT} flex-1`}>Preview on 3 documents</span>
630
+ <BusyButton
631
+ variant="secondary"
632
+ size="sm"
633
+ busy={shownPreview?.loading ?? false}
634
+ onClick={() => void tryIt()}
635
+ >
636
+ {shownPreview?.loading
637
+ ? 'Writing…'
638
+ : shownPreview?.items || shownPreview?.error
639
+ ? 'Try again'
640
+ : 'Try it'}
641
+ </BusyButton>
642
+ </div>
643
+ <PreviewList preview={shownPreview} operation={operation} />
644
+ </div>
645
+ </>
646
+ ) : null}
647
+ </div>
648
+
649
+ <div className="border-bdr mobile:pb-[calc(12px+env(safe-area-inset-bottom,0px))] flex flex-wrap items-center gap-2 border-t px-5 py-3">
650
+ {step > 0 ? (
651
+ <Button
652
+ variant="ghost"
653
+ aria-disabled={creating || undefined}
654
+ className="aria-disabled:pointer-events-none aria-disabled:opacity-50"
655
+ onClick={() => (creating ? undefined : goTo(step - 1))}
656
+ >
657
+ Back
658
+ </Button>
659
+ ) : (
660
+ <Button variant="ghost" onClick={close}>
661
+ Cancel
662
+ </Button>
663
+ )}
664
+ {createError ? (
665
+ <p role="alert" className="text-meta text-err m-0 min-w-0 flex-1 text-right">
666
+ Couldn’t queue the job: {createError}
667
+ </p>
668
+ ) : (
669
+ <span className="flex-1" />
670
+ )}
671
+ {step < 2 ? (
672
+ <Button
673
+ aria-disabled={(step === 0 ? instructionProblem !== null : !canQueue) || undefined}
674
+ className="aria-disabled:opacity-50"
675
+ onClick={onContinue}
676
+ >
677
+ Continue
678
+ </Button>
679
+ ) : (
680
+ <BusyButton
681
+ busy={creating}
682
+ leftIcon={<JobIcons.spark size={13} />}
683
+ onClick={() => void queue()}
684
+ >
685
+ Queue {counted?.count ?? 0} documents
686
+ </BusyButton>
687
+ )}
688
+ </div>
689
+ <div role="status" aria-live="polite" className="sr-only">
690
+ {announce}
691
+ </div>
692
+ </Dialog.Content>
693
+ </Dialog.Portal>
694
+ </Dialog.Root>
695
+ )
696
+ }
697
+
698
+ /** Focus a step's first control: the checked radio of a group, else the first enabled field. */
699
+ function focusFirstControl(root: HTMLElement | null) {
700
+ if (!root) return
701
+ const target = Array.from(
702
+ root.querySelectorAll<HTMLElement>('input, select, textarea, button'),
703
+ ).find((el) => {
704
+ if ((el as HTMLInputElement).disabled) return false
705
+ if (el instanceof HTMLInputElement && el.type === 'radio') return el.checked
706
+ return true
707
+ })
708
+ ;(target ?? root).focus()
709
+ }
710
+
711
+ /** `.nb-match`: the count, "one AI call per document", 3 sample rows and "and {n} more". */
712
+ function MatchBox({
713
+ estimate,
714
+ sourceLabel,
715
+ problem,
716
+ onRetry,
717
+ }: {
718
+ estimate: EstimateState | null
719
+ sourceLabel: string
720
+ problem: string | null
721
+ onRetry: () => void
722
+ }) {
723
+ const value = estimate?.value ?? null
724
+ const row = 'border-bdr text-sm2 text-txt flex min-w-0 gap-2.5 border-t px-3 py-[7px]'
725
+ return (
726
+ <div className="rounded-10 border-bdr overflow-hidden border" aria-busy={estimate?.loading}>
727
+ <div className="bg-bg text-sm2 text-sub flex items-center gap-2 px-3 py-[9px]">
728
+ {value ? (
729
+ <>
730
+ <b className="text-figure text-txt">{value.count.toLocaleString('en-US')}</b>
731
+ <span>{sourceLabel.toLowerCase()} match</span>
732
+ {estimate?.loading ? <Spinner /> : null}
733
+ <span className="ml-auto text-right">one AI call per document</span>
734
+ </>
735
+ ) : estimate?.error ? (
736
+ <>
737
+ <span role="alert" className="text-err min-w-0 flex-1">
738
+ Couldn’t count the documents: {estimate.error}
739
+ </span>
740
+ <Button variant="secondary" size="sm" onClick={onRetry}>
741
+ Try again
742
+ </Button>
743
+ </>
744
+ ) : (
745
+ <span role="status" className="flex items-center gap-2">
746
+ {/* The count's 14px line, so the header keeps its height once it loads. */}
747
+ <b className="text-figure">
748
+ <Spinner />
749
+ </b>
750
+ Counting documents…
751
+ </span>
752
+ )}
753
+ </div>
754
+ {value && estimate?.error ? (
755
+ <div role="alert" className={`${row} text-err`}>
756
+ Couldn’t update the count: {estimate.error}
757
+ <Button variant="link" size="sm" className="ml-auto" onClick={onRetry}>
758
+ Try again
759
+ </Button>
760
+ </div>
761
+ ) : null}
762
+ {value && problem ? (
763
+ <div role="alert" className={`${row} text-err`}>
764
+ {problem}
765
+ </div>
766
+ ) : null}
767
+ {value && !problem
768
+ ? value.sample.map((s, i) => (
769
+ <div key={`${s.url}|${i}`} className={row}>
770
+ <span className="min-w-0 truncate">{s.title}</span>
771
+ {s.url ? (
772
+ <code className="text-faint text-micro ml-auto max-w-[55%] shrink-0 truncate font-mono">
773
+ {s.url}
774
+ </code>
775
+ ) : null}
776
+ </div>
777
+ ))
778
+ : null}
779
+ {value && !problem && value.count > value.sample.length ? (
780
+ <div className={`${row} text-faint`}>
781
+ and {(value.count - value.sample.length).toLocaleString('en-US')} more
782
+ </div>
783
+ ) : null}
784
+ </div>
785
+ )
786
+ }
787
+
788
+ /** `.nb-est`: Documents, Estimated time and AI usage, then the pace and the limit's share. */
789
+ function EstimateTiles({ estimate }: { estimate: BatchAiEstimate }) {
790
+ const tiles = estimateTiles(estimate)
791
+ const cell = 'rounded-10 border-bdr text-hint text-faint min-w-0 border px-3 py-[9px]'
792
+ const value = 'text-figure text-txt m-0 mt-px block'
793
+ return (
794
+ <div className="flex flex-col gap-2">
795
+ <dl className="m-0 grid grid-cols-3 gap-2 max-[560px]:grid-cols-1">
796
+ <div className={cell}>
797
+ <dt>Documents</dt>
798
+ <dd className={value}>{estimate.count.toLocaleString('en-US')}</dd>
799
+ </div>
800
+ <div className={cell}>
801
+ <dt>Estimated time</dt>
802
+ <dd className={value}>{tiles.minutes}</dd>
803
+ </div>
804
+ <div className={cell}>
805
+ <dt>AI usage</dt>
806
+ <dd className={value}>{tiles.tokens}</dd>
807
+ </div>
808
+ </dl>
809
+ <p className={`${HELP} m-0`}>{estimateNote(estimate)}</p>
810
+ </div>
811
+ )
812
+ }
813
+
814
+ /** `.nb-pv`: the sample outputs, or what to expect before the first try. */
815
+ function PreviewList({
816
+ preview,
817
+ operation,
818
+ }: {
819
+ preview: PreviewState | null
820
+ operation: BatchOperation
821
+ }) {
822
+ if (preview?.error) {
823
+ return (
824
+ <p role="alert" className="text-meta text-err m-0">
825
+ Couldn’t write a preview: {preview.error}
826
+ </p>
827
+ )
828
+ }
829
+ if (preview?.items) {
830
+ if (preview.items.length === 0) {
831
+ return (
832
+ <p role="status" className={`${HELP} m-0`}>
833
+ The AI would leave the first 3 documents as they are.
834
+ </p>
835
+ )
836
+ }
837
+ return (
838
+ <ul role="list" className="m-0 flex list-none flex-col gap-1.5 p-0">
839
+ {preview.items.map((item, i) => (
840
+ <li
841
+ key={`${item.documentId}|${i}`}
842
+ className="rounded-9 border-bdr text-sm2 text-txt border px-3 py-[9px] leading-[1.5] break-words"
843
+ >
844
+ <small className="text-hint text-faint mb-0.5 block">{item.title}</small>
845
+ {operation === 'links' ? <LinkLine after={item.after} /> : item.after}
846
+ </li>
847
+ ))}
848
+ </ul>
849
+ )
850
+ }
851
+ if (preview?.loading) return null
852
+ return (
853
+ <p className={`${HELP} m-0`}>
854
+ See sample output before you queue the whole batch. Previews don’t change anything.
855
+ </p>
856
+ )
857
+ }