betterstart-cli 0.0.1

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 (369) hide show
  1. package/README.md +5 -0
  2. package/dist/assets/adapters/next/integrations/mailchimp/actions/mailchimp.ts +38 -0
  3. package/dist/assets/adapters/next/integrations/mailchimp/integration.ts +33 -0
  4. package/dist/assets/adapters/next/integrations/r2/actions/r2.ts +77 -0
  5. package/dist/assets/adapters/next/integrations/r2/integration.ts +39 -0
  6. package/dist/assets/adapters/next/integrations/resend/actions/resend.ts +138 -0
  7. package/dist/assets/adapters/next/integrations/resend/integration.ts +36 -0
  8. package/dist/assets/adapters/next/plugins/blog/plugin.ts +17 -0
  9. package/dist/assets/adapters/next/plugins/blog/schemas/posts.json +169 -0
  10. package/dist/assets/adapters/next/templates/init/admin-globals.css +677 -0
  11. package/dist/assets/adapters/next/templates/init/api/auth-route.ts +3 -0
  12. package/dist/assets/adapters/next/templates/init/api/upload-route.ts +132 -0
  13. package/dist/assets/adapters/next/templates/init/components/layouts/admin-header.tsx +32 -0
  14. package/dist/assets/adapters/next/templates/init/components/layouts/admin-nav-link.tsx +23 -0
  15. package/dist/assets/adapters/next/templates/init/components/layouts/admin-providers.tsx +33 -0
  16. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar-branding-rsc.tsx +8 -0
  17. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar-branding-skeleton.tsx +10 -0
  18. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar-nav-link-skeleton.tsx +11 -0
  19. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar-nav-link.tsx +12 -0
  20. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar-user-menu-skeleton.tsx +15 -0
  21. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar-user-menu.tsx +90 -0
  22. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar.tsx +78 -0
  23. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sign-out.tsx +44 -0
  24. package/dist/assets/adapters/next/templates/init/components/layouts/content-skeleton.tsx +44 -0
  25. package/dist/assets/adapters/next/templates/init/components/layouts/sidebar-branding.tsx +41 -0
  26. package/dist/assets/adapters/next/templates/init/components/shared/data-table/data-table-pagination.tsx +139 -0
  27. package/dist/assets/adapters/next/templates/init/components/shared/data-table/data-table.tsx +236 -0
  28. package/dist/assets/adapters/next/templates/init/components/shared/delete-dialog.tsx +67 -0
  29. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/copyable-code-block.tsx +104 -0
  30. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/dev-mode-code-mirror.tsx +68 -0
  31. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/dev-mode-types.ts +75 -0
  32. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/lifecycle-hooks-tab.tsx +111 -0
  33. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/plain-code-fallback.tsx +11 -0
  34. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/snippets-tab.tsx +125 -0
  35. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode-integrate.tsx +108 -0
  36. package/dist/assets/adapters/next/templates/init/components/shared/entity-filters-bar.tsx +184 -0
  37. package/dist/assets/adapters/next/templates/init/components/shared/entity-metadata.tsx +93 -0
  38. package/dist/assets/adapters/next/templates/init/components/shared/entity-versions/entity-version-item.tsx +55 -0
  39. package/dist/assets/adapters/next/templates/init/components/shared/entity-versions/entity-version-restore-dialog.tsx +80 -0
  40. package/dist/assets/adapters/next/templates/init/components/shared/entity-versions/entity-versions-button.tsx +74 -0
  41. package/dist/assets/adapters/next/templates/init/components/shared/entity-versions/entity-versions-current-row.tsx +48 -0
  42. package/dist/assets/adapters/next/templates/init/components/shared/entity-versions/entity-versions-drawer.tsx +79 -0
  43. package/dist/assets/adapters/next/templates/init/components/shared/media/edit-media-dialog-content.tsx +222 -0
  44. package/dist/assets/adapters/next/templates/init/components/shared/media/edit-media-dialog.tsx +56 -0
  45. package/dist/assets/adapters/next/templates/init/components/shared/media/media-delete-dialog.tsx +83 -0
  46. package/dist/assets/adapters/next/templates/init/components/shared/media/media-delete-drawer.tsx +148 -0
  47. package/dist/assets/adapters/next/templates/init/components/shared/media/media-empty-state.tsx +45 -0
  48. package/dist/assets/adapters/next/templates/init/components/shared/media/media-filters-bar.tsx +129 -0
  49. package/dist/assets/adapters/next/templates/init/components/shared/media/media-gallery-dialog.tsx +182 -0
  50. package/dist/assets/adapters/next/templates/init/components/shared/media/media-grid-item.tsx +56 -0
  51. package/dist/assets/adapters/next/templates/init/components/shared/media/media-grid-pagination.tsx +114 -0
  52. package/dist/assets/adapters/next/templates/init/components/shared/media/media-grid.tsx +44 -0
  53. package/dist/assets/adapters/next/templates/init/components/shared/media/media-preview.tsx +69 -0
  54. package/dist/assets/adapters/next/templates/init/components/shared/media/media-url-importer.tsx +139 -0
  55. package/dist/assets/adapters/next/templates/init/components/shared/page-header.tsx +46 -0
  56. package/dist/assets/adapters/next/templates/init/components/shared/search-input.tsx +88 -0
  57. package/dist/assets/adapters/next/templates/init/components/shared/sort-indicator.tsx +24 -0
  58. package/dist/assets/adapters/next/templates/init/components/shared/sort-order-dialog.tsx +242 -0
  59. package/dist/assets/adapters/next/templates/init/components/shared/sort-order-drag-overlay-item.tsx +15 -0
  60. package/dist/assets/adapters/next/templates/init/components/shared/sort-order-item.tsx +32 -0
  61. package/dist/assets/adapters/next/templates/init/components/shared/sort-order-types.ts +9 -0
  62. package/dist/assets/adapters/next/templates/init/data/navigation.ts +43 -0
  63. package/dist/assets/adapters/next/templates/init/drizzle.config.ts +36 -0
  64. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-blockquote.ts +251 -0
  65. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-code-block.ts +258 -0
  66. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-color-highlight.ts +347 -0
  67. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor-media-insertion.ts +59 -0
  68. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor-mobile-toolbar.ts +17 -0
  69. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor-slash-menu.ts +116 -0
  70. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor-source-mode.tsx +638 -0
  71. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor-table-add-controls.ts +174 -0
  72. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor.ts +288 -0
  73. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-heading-dropdown-menu.ts +127 -0
  74. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-heading.ts +269 -0
  75. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-is-breakpoint.ts +32 -0
  76. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-link-popover.ts +278 -0
  77. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-list-dropdown-menu.ts +199 -0
  78. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-list.ts +290 -0
  79. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-mark.ts +199 -0
  80. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-menu-navigation.ts +221 -0
  81. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-tiptap-editor.ts +46 -0
  82. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-undo-redo.ts +169 -0
  83. package/dist/assets/adapters/next/templates/init/hooks/use-admin-theme.tsx +74 -0
  84. package/dist/assets/adapters/next/templates/init/hooks/use-copy-to-clipboard.ts +48 -0
  85. package/dist/assets/adapters/next/templates/init/hooks/use-dev-mode-integration.ts +43 -0
  86. package/dist/assets/adapters/next/templates/init/hooks/use-entity-versions.ts +32 -0
  87. package/dist/assets/adapters/next/templates/init/hooks/use-failed-image-url.ts +21 -0
  88. package/dist/assets/adapters/next/templates/init/hooks/use-local-storage.ts +46 -0
  89. package/dist/assets/adapters/next/templates/init/hooks/use-media-filters-bar.ts +72 -0
  90. package/dist/assets/adapters/next/templates/init/hooks/use-media.ts +102 -0
  91. package/dist/assets/adapters/next/templates/init/hooks/use-mobile.ts +19 -0
  92. package/dist/assets/adapters/next/templates/init/hooks/use-page-boundary-blur-visibility.ts +59 -0
  93. package/dist/assets/adapters/next/templates/init/hooks/use-page-scroll-threshold.ts +27 -0
  94. package/dist/assets/adapters/next/templates/init/hooks/use-table-utils.ts +315 -0
  95. package/dist/assets/adapters/next/templates/init/hooks/use-upload.ts +321 -0
  96. package/dist/assets/adapters/next/templates/init/hooks/use-users.ts +12 -0
  97. package/dist/assets/adapters/next/templates/init/lib/actions/auth/auth.ts +58 -0
  98. package/dist/assets/adapters/next/templates/init/lib/actions/auth/client.ts +12 -0
  99. package/dist/assets/adapters/next/templates/init/lib/actions/auth/middleware.ts +46 -0
  100. package/dist/assets/adapters/next/templates/init/lib/actions/email/form-delivery.ts +72 -0
  101. package/dist/assets/adapters/next/templates/init/lib/actions/email/index.ts +4 -0
  102. package/dist/assets/adapters/next/templates/init/lib/actions/email/is-email-delivery-configured.ts +5 -0
  103. package/dist/assets/adapters/next/templates/init/lib/actions/email/none.ts +24 -0
  104. package/dist/assets/adapters/next/templates/init/lib/actions/email/provider.ts +6 -0
  105. package/dist/assets/adapters/next/templates/init/lib/actions/email/send-email.ts +6 -0
  106. package/dist/assets/adapters/next/templates/init/lib/actions/email/send-password-reset-email.ts +10 -0
  107. package/dist/assets/adapters/next/templates/init/lib/actions/email/types.ts +25 -0
  108. package/dist/assets/adapters/next/templates/init/lib/actions/entity-versions/get-entity-versions.ts +48 -0
  109. package/dist/assets/adapters/next/templates/init/lib/actions/entity-versions/index.ts +10 -0
  110. package/dist/assets/adapters/next/templates/init/lib/actions/entity-versions/internal-create-entity-version.ts +43 -0
  111. package/dist/assets/adapters/next/templates/init/lib/actions/entity-versions/internal-delete-entity-versions.ts +19 -0
  112. package/dist/assets/adapters/next/templates/init/lib/actions/entity-versions/types.ts +26 -0
  113. package/dist/assets/adapters/next/templates/init/lib/actions/forms/get-all-form-settings.ts +21 -0
  114. package/dist/assets/adapters/next/templates/init/lib/actions/forms/get-form-settings.ts +27 -0
  115. package/dist/assets/adapters/next/templates/init/lib/actions/forms/index.ts +9 -0
  116. package/dist/assets/adapters/next/templates/init/lib/actions/forms/test-form-webhook.ts +40 -0
  117. package/dist/assets/adapters/next/templates/init/lib/actions/forms/types.ts +26 -0
  118. package/dist/assets/adapters/next/templates/init/lib/actions/forms/upsert-form-settings.ts +40 -0
  119. package/dist/assets/adapters/next/templates/init/lib/actions/media/create-media.ts +39 -0
  120. package/dist/assets/adapters/next/templates/init/lib/actions/media/delete-media-bulk.ts +29 -0
  121. package/dist/assets/adapters/next/templates/init/lib/actions/media/delete-media.ts +22 -0
  122. package/dist/assets/adapters/next/templates/init/lib/actions/media/get-media-by-id.ts +18 -0
  123. package/dist/assets/adapters/next/templates/init/lib/actions/media/get-media-by-ids.ts +25 -0
  124. package/dist/assets/adapters/next/templates/init/lib/actions/media/get-media.ts +71 -0
  125. package/dist/assets/adapters/next/templates/init/lib/actions/media/index.ts +14 -0
  126. package/dist/assets/adapters/next/templates/init/lib/actions/media/types.ts +31 -0
  127. package/dist/assets/adapters/next/templates/init/lib/actions/media/update-media.ts +35 -0
  128. package/dist/assets/adapters/next/templates/init/lib/actions/profile/index.ts +4 -0
  129. package/dist/assets/adapters/next/templates/init/lib/actions/profile/invalidate-users-cache.ts +14 -0
  130. package/dist/assets/adapters/next/templates/init/lib/actions/profile/is-email-configured.ts +7 -0
  131. package/dist/assets/adapters/next/templates/init/lib/actions/profile/types.ts +4 -0
  132. package/dist/assets/adapters/next/templates/init/lib/actions/profile/update-email.ts +57 -0
  133. package/dist/assets/adapters/next/templates/init/lib/actions/storage/index.ts +2 -0
  134. package/dist/assets/adapters/next/templates/init/lib/actions/storage/local.ts +31 -0
  135. package/dist/assets/adapters/next/templates/init/lib/actions/storage/provider.ts +6 -0
  136. package/dist/assets/adapters/next/templates/init/lib/actions/storage/save-upload.ts +11 -0
  137. package/dist/assets/adapters/next/templates/init/lib/actions/storage/types.ts +18 -0
  138. package/dist/assets/adapters/next/templates/init/lib/actions/upload/index.ts +9 -0
  139. package/dist/assets/adapters/next/templates/init/lib/actions/upload/types.ts +17 -0
  140. package/dist/assets/adapters/next/templates/init/lib/actions/upload/upload-file.ts +11 -0
  141. package/dist/assets/adapters/next/templates/init/lib/actions/upload/upload-files.ts +92 -0
  142. package/dist/assets/adapters/next/templates/init/lib/actions/upload/upload-image-from-url.ts +22 -0
  143. package/dist/assets/adapters/next/templates/init/lib/actions/upload/upload-media-from-url.ts +133 -0
  144. package/dist/assets/adapters/next/templates/init/lib/actions/users/create-user.ts +55 -0
  145. package/dist/assets/adapters/next/templates/init/lib/actions/users/delete-user.ts +24 -0
  146. package/dist/assets/adapters/next/templates/init/lib/actions/users/get-users.ts +49 -0
  147. package/dist/assets/adapters/next/templates/init/lib/actions/users/index.ts +12 -0
  148. package/dist/assets/adapters/next/templates/init/lib/actions/users/types.ts +43 -0
  149. package/dist/assets/adapters/next/templates/init/lib/actions/users/update-user-role.ts +28 -0
  150. package/dist/assets/adapters/next/templates/init/lib/db/client.ts +13 -0
  151. package/dist/assets/adapters/next/templates/init/lib/db/core/schema.ts +160 -0
  152. package/dist/assets/adapters/next/templates/init/lib/db/schema.ts +1 -0
  153. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/index.ts +19 -0
  154. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/register.local.ts +2 -0
  155. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/register.ts +9 -0
  156. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/registry.ts +55 -0
  157. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/runner.ts +51 -0
  158. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/types.ts +39 -0
  159. package/dist/assets/adapters/next/templates/init/pages/account-layout.tsx +11 -0
  160. package/dist/assets/adapters/next/templates/init/pages/account-shell-rsc.tsx +30 -0
  161. package/dist/assets/adapters/next/templates/init/pages/admin-layout.tsx +24 -0
  162. package/dist/assets/adapters/next/templates/init/pages/auth-gate-rsc.tsx +6 -0
  163. package/dist/assets/adapters/next/templates/init/pages/authenticated-layout.tsx +18 -0
  164. package/dist/assets/adapters/next/templates/init/pages/dashboard-page.tsx +121 -0
  165. package/dist/assets/adapters/next/templates/init/pages/forgot-password-form.tsx +124 -0
  166. package/dist/assets/adapters/next/templates/init/pages/forgot-password-page-skeleton.tsx +24 -0
  167. package/dist/assets/adapters/next/templates/init/pages/forgot-password-page.tsx +21 -0
  168. package/dist/assets/adapters/next/templates/init/pages/login-form.tsx +131 -0
  169. package/dist/assets/adapters/next/templates/init/pages/login-page-rsc.tsx +14 -0
  170. package/dist/assets/adapters/next/templates/init/pages/login-page-skeleton.tsx +26 -0
  171. package/dist/assets/adapters/next/templates/init/pages/login-page.tsx +21 -0
  172. package/dist/assets/adapters/next/templates/init/pages/media/media-page-content.tsx +273 -0
  173. package/dist/assets/adapters/next/templates/init/pages/media/media-page-skeleton.tsx +7 -0
  174. package/dist/assets/adapters/next/templates/init/pages/media/media-page.tsx +11 -0
  175. package/dist/assets/adapters/next/templates/init/pages/minimal-account-shell.tsx +25 -0
  176. package/dist/assets/adapters/next/templates/init/pages/profile/profile-form.tsx +281 -0
  177. package/dist/assets/adapters/next/templates/init/pages/profile/profile-page.tsx +31 -0
  178. package/dist/assets/adapters/next/templates/init/pages/reset-password-form.tsx +161 -0
  179. package/dist/assets/adapters/next/templates/init/pages/reset-password-page-skeleton.tsx +26 -0
  180. package/dist/assets/adapters/next/templates/init/pages/reset-password-page.tsx +21 -0
  181. package/dist/assets/adapters/next/templates/init/pages/users/columns.tsx +170 -0
  182. package/dist/assets/adapters/next/templates/init/pages/users/create-user-dialog.tsx +221 -0
  183. package/dist/assets/adapters/next/templates/init/pages/users/delete-user-dialog.tsx +172 -0
  184. package/dist/assets/adapters/next/templates/init/pages/users/edit-role-dialog.tsx +91 -0
  185. package/dist/assets/adapters/next/templates/init/pages/users/users-page-content.tsx +25 -0
  186. package/dist/assets/adapters/next/templates/init/pages/users/users-page-skeleton.tsx +7 -0
  187. package/dist/assets/adapters/next/templates/init/pages/users/users-page.tsx +11 -0
  188. package/dist/assets/adapters/next/templates/init/pages/users/users-table.tsx +221 -0
  189. package/dist/assets/adapters/next/templates/init/types/auth.ts +71 -0
  190. package/dist/assets/adapters/next/templates/init/types/index.ts +108 -0
  191. package/dist/assets/adapters/next/templates/init/types/navigation.ts +11 -0
  192. package/dist/assets/adapters/next/templates/init/types/table-meta.ts +14 -0
  193. package/dist/assets/adapters/next/templates/init/utils/auth/roles.ts +17 -0
  194. package/dist/assets/adapters/next/templates/init/utils/date/date.ts +90 -0
  195. package/dist/assets/adapters/next/templates/init/utils/dev-mode/code-block-height.ts +9 -0
  196. package/dist/assets/adapters/next/templates/init/utils/editor/content-editor-rich-extensions.ts +824 -0
  197. package/dist/assets/adapters/next/templates/init/utils/editor/content-editor.ts +316 -0
  198. package/dist/assets/adapters/next/templates/init/utils/editor/editor-view.ts +19 -0
  199. package/dist/assets/adapters/next/templates/init/utils/editor/markdown.ts +542 -0
  200. package/dist/assets/adapters/next/templates/init/utils/editor/node-attrs.ts +25 -0
  201. package/dist/assets/adapters/next/templates/init/utils/editor/slash-commands.ts +148 -0
  202. package/dist/assets/adapters/next/templates/init/utils/editor/source-media.ts +11 -0
  203. package/dist/assets/adapters/next/templates/init/utils/editor/table-add-controls.ts +91 -0
  204. package/dist/assets/adapters/next/templates/init/utils/editor/table-bubble.ts +172 -0
  205. package/dist/assets/adapters/next/templates/init/utils/editor/table-input.ts +5 -0
  206. package/dist/assets/adapters/next/templates/init/utils/editor/task-item.ts +19 -0
  207. package/dist/assets/adapters/next/templates/init/utils/editor/tiptap.ts +991 -0
  208. package/dist/assets/adapters/next/templates/init/utils/email/form-delivery.ts +104 -0
  209. package/dist/assets/adapters/next/templates/init/utils/media/fallback.ts +37 -0
  210. package/dist/assets/adapters/next/templates/init/utils/media/media.ts +91 -0
  211. package/dist/assets/adapters/next/templates/init/utils/media/query.ts +96 -0
  212. package/dist/assets/adapters/next/templates/init/utils/navigation/order.ts +6 -0
  213. package/dist/assets/adapters/next/templates/init/utils/navigation/sidebar.ts +26 -0
  214. package/dist/assets/adapters/next/templates/init/utils/page/boundary.ts +32 -0
  215. package/dist/assets/adapters/next/templates/init/utils/seo/seo.ts +90 -0
  216. package/dist/assets/adapters/next/templates/init/utils/shared/cn.ts +6 -0
  217. package/dist/assets/adapters/next/templates/init/utils/storage/local.ts +9 -0
  218. package/dist/assets/adapters/next/templates/init/utils/table/table.ts +10 -0
  219. package/dist/assets/adapters/next/templates/init/utils/text/text.ts +4 -0
  220. package/dist/assets/adapters/next/templates/init/utils/theme/system.ts +6 -0
  221. package/dist/assets/adapters/next/templates/init/utils/upload/remote.ts +55 -0
  222. package/dist/assets/adapters/next/templates/init/utils/upload/upload.ts +26 -0
  223. package/dist/assets/adapters/next/templates/init/utils/user/user.ts +11 -0
  224. package/dist/assets/adapters/next/templates/init/utils/validation/validation.ts +114 -0
  225. package/dist/assets/adapters/next/templates/init/utils/webhook/webhook.ts +28 -0
  226. package/dist/assets/shared-assets/react-admin/custom/content-editor/editor-toolbar.tsx +25 -0
  227. package/dist/assets/shared-assets/react-admin/custom/content-editor/horizontal-rule-button.tsx +22 -0
  228. package/dist/assets/shared-assets/react-admin/custom/content-editor/index.tsx +142 -0
  229. package/dist/assets/shared-assets/react-admin/custom/content-editor/main-toolbar-content.tsx +118 -0
  230. package/dist/assets/shared-assets/react-admin/custom/content-editor/math-bubble-menu.tsx +80 -0
  231. package/dist/assets/shared-assets/react-admin/custom/content-editor/math-button.tsx +22 -0
  232. package/dist/assets/shared-assets/react-admin/custom/content-editor/math-editor-controls.tsx +117 -0
  233. package/dist/assets/shared-assets/react-admin/custom/content-editor/math-popover-button.tsx +59 -0
  234. package/dist/assets/shared-assets/react-admin/custom/content-editor/math-popover.tsx +6 -0
  235. package/dist/assets/shared-assets/react-admin/custom/content-editor/media-gallery-block.tsx +31 -0
  236. package/dist/assets/shared-assets/react-admin/custom/content-editor/mobile-toolbar-content.tsx +56 -0
  237. package/dist/assets/shared-assets/react-admin/custom/content-editor/mode-toggle-button.tsx +29 -0
  238. package/dist/assets/shared-assets/react-admin/custom/content-editor/remove-table-part-icon.tsx +17 -0
  239. package/dist/assets/shared-assets/react-admin/custom/content-editor/selection-bubble-menu.tsx +105 -0
  240. package/dist/assets/shared-assets/react-admin/custom/content-editor/slash-command-menu.tsx +65 -0
  241. package/dist/assets/shared-assets/react-admin/custom/content-editor/source-mode-dropdown-button.tsx +52 -0
  242. package/dist/assets/shared-assets/react-admin/custom/content-editor/source-mode.tsx +360 -0
  243. package/dist/assets/shared-assets/react-admin/custom/content-editor/table-add-controls.tsx +46 -0
  244. package/dist/assets/shared-assets/react-admin/custom/content-editor/table-bubble-menu.tsx +290 -0
  245. package/dist/assets/shared-assets/react-admin/custom/content-editor/table-button.tsx +116 -0
  246. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-extension/node-background-extension.ts +138 -0
  247. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/horizontal-rule-node/horizontal-rule-node-extension.ts +10 -0
  248. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/media-gallery-placeholder-node/index.tsx +1 -0
  249. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/media-gallery-placeholder-node/media-gallery-placeholder-node-extension.ts +117 -0
  250. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/media-gallery-placeholder-node/media-gallery-placeholder-node.tsx +63 -0
  251. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/removable-image-node/index.tsx +1 -0
  252. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/removable-image-node/removable-image-node-extension.ts +11 -0
  253. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/removable-image-node/removable-image-node.tsx +168 -0
  254. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/task-item-node/task-item-node-extension.tsx +142 -0
  255. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/blockquote-button/blockquote-button.tsx +114 -0
  256. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/blockquote-button/index.tsx +1 -0
  257. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/code-block-button/code-block-button.tsx +112 -0
  258. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/code-block-button/index.tsx +1 -0
  259. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-button/color-highlight-button.tsx +185 -0
  260. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-button/index.tsx +1 -0
  261. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/color-highlight-popover-button.tsx +40 -0
  262. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/color-highlight-popover-content.tsx +130 -0
  263. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/color-highlight-popover.tsx +98 -0
  264. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/highlight-color-button.tsx +75 -0
  265. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/highlight-colors.ts +24 -0
  266. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/index.tsx +1 -0
  267. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/source-color-highlight-popover.tsx +65 -0
  268. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/control-options.ts +27 -0
  269. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/heading-dropdown-menu/heading-dropdown-menu-item.tsx +35 -0
  270. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/heading-dropdown-menu/heading-dropdown-menu.tsx +119 -0
  271. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/heading-dropdown-menu/index.tsx +1 -0
  272. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/index.tsx +1 -0
  273. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-button.tsx +39 -0
  274. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-content.tsx +13 -0
  275. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-control-popover.tsx +90 -0
  276. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-main.tsx +96 -0
  277. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-popover.tsx +121 -0
  278. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/list-dropdown-menu/index.tsx +1 -0
  279. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/list-dropdown-menu/list-dropdown-menu-item.tsx +44 -0
  280. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/list-dropdown-menu/list-dropdown-menu.tsx +115 -0
  281. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/mark-button/index.tsx +1 -0
  282. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/mark-button/mark-button.tsx +117 -0
  283. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/undo-redo-button/index.tsx +1 -0
  284. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/undo-redo-button/undo-redo-button.tsx +115 -0
  285. package/dist/assets/shared-assets/react-admin/custom/date-picker.tsx +70 -0
  286. package/dist/assets/shared-assets/react-admin/custom/date-range-picker.tsx +216 -0
  287. package/dist/assets/shared-assets/react-admin/custom/dynamic-list-field.tsx +111 -0
  288. package/dist/assets/shared-assets/react-admin/custom/gallery-field.tsx +124 -0
  289. package/dist/assets/shared-assets/react-admin/custom/gallery-thumbnail.tsx +39 -0
  290. package/dist/assets/shared-assets/react-admin/custom/icon-picker.tsx +377 -0
  291. package/dist/assets/shared-assets/react-admin/custom/icons-column-skeleton.tsx +16 -0
  292. package/dist/assets/shared-assets/react-admin/custom/icons-data.ts +8 -0
  293. package/dist/assets/shared-assets/react-admin/custom/logo.tsx +115 -0
  294. package/dist/assets/shared-assets/react-admin/custom/media-gallery-field.tsx +182 -0
  295. package/dist/assets/shared-assets/react-admin/custom/page-skeleton.tsx +11 -0
  296. package/dist/assets/shared-assets/react-admin/custom/placeholder-card.tsx +34 -0
  297. package/dist/assets/shared-assets/react-admin/custom/placeholder.tsx +25 -0
  298. package/dist/assets/shared-assets/react-admin/custom/progressive-blur.tsx +62 -0
  299. package/dist/assets/shared-assets/react-admin/custom/sortable-gallery-item.tsx +68 -0
  300. package/dist/assets/shared-assets/react-admin/custom/upload-dropzone.tsx +107 -0
  301. package/dist/assets/shared-assets/react-admin/dependencies.ts +73 -0
  302. package/dist/assets/shared-assets/react-admin/schema.json +1670 -0
  303. package/dist/assets/shared-assets/react-admin/ui/accordion.tsx +86 -0
  304. package/dist/assets/shared-assets/react-admin/ui/alert-dialog.tsx +178 -0
  305. package/dist/assets/shared-assets/react-admin/ui/alert.tsx +72 -0
  306. package/dist/assets/shared-assets/react-admin/ui/aspect-ratio.tsx +9 -0
  307. package/dist/assets/shared-assets/react-admin/ui/avatar.tsx +95 -0
  308. package/dist/assets/shared-assets/react-admin/ui/badge.tsx +48 -0
  309. package/dist/assets/shared-assets/react-admin/ui/breadcrumb.tsx +99 -0
  310. package/dist/assets/shared-assets/react-admin/ui/button-group.tsx +76 -0
  311. package/dist/assets/shared-assets/react-admin/ui/button.tsx +66 -0
  312. package/dist/assets/shared-assets/react-admin/ui/calendar.tsx +184 -0
  313. package/dist/assets/shared-assets/react-admin/ui/card.tsx +94 -0
  314. package/dist/assets/shared-assets/react-admin/ui/carousel.tsx +239 -0
  315. package/dist/assets/shared-assets/react-admin/ui/chart.tsx +336 -0
  316. package/dist/assets/shared-assets/react-admin/ui/checkbox.tsx +28 -0
  317. package/dist/assets/shared-assets/react-admin/ui/collapsible.tsx +21 -0
  318. package/dist/assets/shared-assets/react-admin/ui/combobox.tsx +272 -0
  319. package/dist/assets/shared-assets/react-admin/ui/command.tsx +180 -0
  320. package/dist/assets/shared-assets/react-admin/ui/context-menu.tsx +243 -0
  321. package/dist/assets/shared-assets/react-admin/ui/dialog.tsx +141 -0
  322. package/dist/assets/shared-assets/react-admin/ui/direction.tsx +20 -0
  323. package/dist/assets/shared-assets/react-admin/ui/drawer.tsx +119 -0
  324. package/dist/assets/shared-assets/react-admin/ui/dropdown-menu.tsx +253 -0
  325. package/dist/assets/shared-assets/react-admin/ui/empty.tsx +93 -0
  326. package/dist/assets/shared-assets/react-admin/ui/field.tsx +234 -0
  327. package/dist/assets/shared-assets/react-admin/ui/form.tsx +172 -0
  328. package/dist/assets/shared-assets/react-admin/ui/hover-card.tsx +37 -0
  329. package/dist/assets/shared-assets/react-admin/ui/input-group.tsx +134 -0
  330. package/dist/assets/shared-assets/react-admin/ui/input-otp.tsx +85 -0
  331. package/dist/assets/shared-assets/react-admin/ui/input.tsx +18 -0
  332. package/dist/assets/shared-assets/react-admin/ui/item.tsx +180 -0
  333. package/dist/assets/shared-assets/react-admin/ui/kbd.tsx +26 -0
  334. package/dist/assets/shared-assets/react-admin/ui/label.tsx +20 -0
  335. package/dist/assets/shared-assets/react-admin/ui/menubar.tsx +259 -0
  336. package/dist/assets/shared-assets/react-admin/ui/native-select.tsx +54 -0
  337. package/dist/assets/shared-assets/react-admin/ui/navigation-menu.tsx +159 -0
  338. package/dist/assets/shared-assets/react-admin/ui/pagination.tsx +111 -0
  339. package/dist/assets/shared-assets/react-admin/ui/popover.tsx +75 -0
  340. package/dist/assets/shared-assets/react-admin/ui/progress.tsx +30 -0
  341. package/dist/assets/shared-assets/react-admin/ui/radio-group.tsx +43 -0
  342. package/dist/assets/shared-assets/react-admin/ui/resizable.tsx +41 -0
  343. package/dist/assets/shared-assets/react-admin/ui/scroll-area.tsx +54 -0
  344. package/dist/assets/shared-assets/react-admin/ui/select.tsx +183 -0
  345. package/dist/assets/shared-assets/react-admin/ui/separator.tsx +27 -0
  346. package/dist/assets/shared-assets/react-admin/ui/sheet.tsx +129 -0
  347. package/dist/assets/shared-assets/react-admin/ui/sidebar.tsx +688 -0
  348. package/dist/assets/shared-assets/react-admin/ui/skeleton.tsx +13 -0
  349. package/dist/assets/shared-assets/react-admin/ui/slider.tsx +53 -0
  350. package/dist/assets/shared-assets/react-admin/ui/sonner.tsx +45 -0
  351. package/dist/assets/shared-assets/react-admin/ui/spinner.tsx +15 -0
  352. package/dist/assets/shared-assets/react-admin/ui/switch.tsx +32 -0
  353. package/dist/assets/shared-assets/react-admin/ui/table.tsx +101 -0
  354. package/dist/assets/shared-assets/react-admin/ui/tabs.tsx +79 -0
  355. package/dist/assets/shared-assets/react-admin/ui/textarea.tsx +17 -0
  356. package/dist/assets/shared-assets/react-admin/ui/toggle-group.tsx +85 -0
  357. package/dist/assets/shared-assets/react-admin/ui/toggle.tsx +45 -0
  358. package/dist/assets/shared-assets/react-admin/ui/tooltip.tsx +51 -0
  359. package/dist/chunk-MUZQCVQA.js +306 -0
  360. package/dist/chunk-MUZQCVQA.js.map +1 -0
  361. package/dist/cli.d.ts +2 -0
  362. package/dist/cli.js +23437 -0
  363. package/dist/cli.js.map +1 -0
  364. package/dist/index.d.ts +90 -0
  365. package/dist/index.js +8 -0
  366. package/dist/index.js.map +1 -0
  367. package/dist/template-reader-YKWE2C7O.js +13 -0
  368. package/dist/template-reader-YKWE2C7O.js.map +1 -0
  369. package/package.json +74 -0
@@ -0,0 +1,80 @@
1
+ 'use client'
2
+
3
+ import type { EntityVersionWithAuthor } from '@admin/actions/entity-versions'
4
+ import {
5
+ AlertDialog,
6
+ AlertDialogAction,
7
+ AlertDialogCancel,
8
+ AlertDialogContent,
9
+ AlertDialogDescription,
10
+ AlertDialogFooter,
11
+ AlertDialogHeader,
12
+ AlertDialogTitle,
13
+ AlertDialogTrigger
14
+ } from '@admin/components/ui/alert-dialog'
15
+ import { Button } from '@admin/components/ui/button'
16
+ import { useRouter } from 'next/navigation'
17
+ import * as React from 'react'
18
+ import { toast } from 'sonner'
19
+
20
+ interface EntityVersionRestoreDialogProps {
21
+ version: EntityVersionWithAuthor
22
+ restoreVersionAction: (versionId: string) => Promise<unknown>
23
+ onRestored: () => void
24
+ }
25
+
26
+ export function EntityVersionRestoreDialog({
27
+ version,
28
+ restoreVersionAction,
29
+ onRestored
30
+ }: EntityVersionRestoreDialogProps) {
31
+ const router = useRouter()
32
+ const [open, setOpen] = React.useState(false)
33
+ const [isPending, startTransition] = React.useTransition()
34
+
35
+ function handleRestore() {
36
+ startTransition(async () => {
37
+ try {
38
+ await restoreVersionAction(version.id)
39
+ toast.success(`Restored v${version.version}`)
40
+ setOpen(false)
41
+ onRestored()
42
+ router.refresh()
43
+ } catch (error) {
44
+ toast.error(error instanceof Error ? error.message : 'Failed to restore version')
45
+ }
46
+ })
47
+ }
48
+
49
+ return (
50
+ <AlertDialog open={open} onOpenChange={setOpen}>
51
+ <AlertDialogTrigger asChild>
52
+ <Button variant="outline" size="sm">
53
+ Restore
54
+ </Button>
55
+ </AlertDialogTrigger>
56
+ <AlertDialogContent>
57
+ <AlertDialogHeader>
58
+ <AlertDialogTitle>Restore v{version.version}?</AlertDialogTitle>
59
+ <AlertDialogDescription>
60
+ This will replace the current entity content with v{version.version}. The current
61
+ content will be saved as a new version before restoring.
62
+ </AlertDialogDescription>
63
+ </AlertDialogHeader>
64
+ <AlertDialogFooter>
65
+ <AlertDialogCancel disabled={isPending}>Cancel</AlertDialogCancel>
66
+ <AlertDialogAction
67
+ variant="default"
68
+ onClick={(event) => {
69
+ event.preventDefault()
70
+ handleRestore()
71
+ }}
72
+ disabled={isPending}
73
+ >
74
+ {isPending ? 'Restoring...' : 'Restore'}
75
+ </AlertDialogAction>
76
+ </AlertDialogFooter>
77
+ </AlertDialogContent>
78
+ </AlertDialog>
79
+ )
80
+ }
@@ -0,0 +1,74 @@
1
+ 'use client'
2
+
3
+ import type { EntityVersionWithAuthor } from '@admin/actions/entity-versions'
4
+ import { EntityVersionsDrawer } from '@admin/components/shared/entity-versions/entity-versions-drawer'
5
+ import { Button } from '@admin/components/ui/button'
6
+ import { useEntityVersions } from '@admin/hooks/use-entity-versions'
7
+ import { History } from 'lucide-react'
8
+ import * as React from 'react'
9
+
10
+ interface EntityVersionsButtonAuthor {
11
+ name: string
12
+ email: string
13
+ image?: string | null
14
+ }
15
+
16
+ interface EntityVersionsButtonProps {
17
+ entityType: string
18
+ entityId: string
19
+ updatedAt: Date | string
20
+ updatedByUser: EntityVersionsButtonAuthor | null
21
+ getVersionsAction: (entityId: string) => Promise<EntityVersionWithAuthor[]>
22
+ restoreVersionAction: (versionId: string) => Promise<unknown>
23
+ }
24
+
25
+ export function EntityVersionsButton({
26
+ entityType,
27
+ entityId,
28
+ updatedAt,
29
+ updatedByUser,
30
+ getVersionsAction,
31
+ restoreVersionAction
32
+ }: EntityVersionsButtonProps) {
33
+ const [open, setOpen] = React.useState(false)
34
+ const { versions, isPending, load } = useEntityVersions({
35
+ entityId,
36
+ getVersionsAction
37
+ })
38
+
39
+ const handleOpenChange = React.useCallback(
40
+ (nextOpen: boolean) => {
41
+ setOpen(nextOpen)
42
+ if (nextOpen) {
43
+ load()
44
+ }
45
+ },
46
+ [load]
47
+ )
48
+
49
+ return (
50
+ <>
51
+ <Button
52
+ type="button"
53
+ variant="ghost"
54
+ size="sm"
55
+ onClick={() => handleOpenChange(true)}
56
+ className="ml-auto shrink-0"
57
+ >
58
+ <History className="size-4" />
59
+ Versions
60
+ </Button>
61
+ <EntityVersionsDrawer
62
+ open={open}
63
+ onOpenChange={handleOpenChange}
64
+ entityType={entityType}
65
+ versions={versions}
66
+ isPending={isPending}
67
+ currentUpdatedAt={updatedAt}
68
+ currentAuthor={updatedByUser}
69
+ restoreVersionAction={restoreVersionAction}
70
+ onRestored={load}
71
+ />
72
+ </>
73
+ )
74
+ }
@@ -0,0 +1,48 @@
1
+ 'use client'
2
+
3
+ import { Avatar, AvatarFallback, AvatarImage } from '@admin/components/ui/avatar'
4
+ import { Badge } from '@admin/components/ui/badge'
5
+ import { Tooltip, TooltipContent, TooltipTrigger } from '@admin/components/ui/tooltip'
6
+ import { getUserInitials } from '@admin/utils/user/user'
7
+ import { format, formatDistanceToNow } from 'date-fns'
8
+
9
+ interface CurrentVersionAuthor {
10
+ name: string
11
+ email: string
12
+ image?: string | null
13
+ }
14
+
15
+ interface EntityVersionsCurrentRowProps {
16
+ updatedAt: Date | string
17
+ author: CurrentVersionAuthor | null
18
+ }
19
+
20
+ export function EntityVersionsCurrentRow({ updatedAt, author }: EntityVersionsCurrentRowProps) {
21
+ const updatedDate = new Date(updatedAt)
22
+ const authorLabel = author?.name || author?.email || 'System'
23
+
24
+ return (
25
+ <div className="flex items-center justify-between gap-3 rounded-2xl border border-border bg-card px-3 py-3">
26
+ <div className="flex min-w-0 items-center gap-3">
27
+ <Avatar size="sm">
28
+ {author?.image ? <AvatarImage src={author.image} alt={authorLabel} /> : null}
29
+ <AvatarFallback>{getUserInitials(authorLabel)}</AvatarFallback>
30
+ </Avatar>
31
+ <div className="min-w-0">
32
+ <div className="flex items-center gap-2">
33
+ <p className="truncate font-medium text-foreground">{authorLabel}</p>
34
+ <Badge variant="outline">Current</Badge>
35
+ </div>
36
+ <Tooltip>
37
+ <TooltipTrigger asChild>
38
+ <p className="w-fit text-xs text-muted-foreground">
39
+ Edited {formatDistanceToNow(updatedDate, { addSuffix: true })}
40
+ </p>
41
+ </TooltipTrigger>
42
+ <TooltipContent>{format(updatedDate, 'PPpp')}</TooltipContent>
43
+ </Tooltip>
44
+ </div>
45
+ </div>
46
+ </div>
47
+ )
48
+ }
@@ -0,0 +1,79 @@
1
+ 'use client'
2
+
3
+ import type { EntityVersionWithAuthor } from '@admin/actions/entity-versions'
4
+ import {
5
+ Drawer,
6
+ DrawerContent,
7
+ DrawerDescription,
8
+ DrawerHeader,
9
+ DrawerTitle
10
+ } from '@admin/components/ui/drawer'
11
+ import { ScrollArea } from '@admin/components/ui/scroll-area'
12
+ import { EntityVersionItem } from './entity-version-item'
13
+ import { EntityVersionsCurrentRow } from './entity-versions-current-row'
14
+
15
+ interface EntityVersionsDrawerAuthor {
16
+ name: string
17
+ email: string
18
+ image?: string | null
19
+ }
20
+
21
+ interface EntityVersionsDrawerProps {
22
+ open: boolean
23
+ onOpenChange: (open: boolean) => void
24
+ entityType: string
25
+ versions: EntityVersionWithAuthor[]
26
+ isPending: boolean
27
+ currentUpdatedAt: Date | string
28
+ currentAuthor: EntityVersionsDrawerAuthor | null
29
+ restoreVersionAction: (versionId: string) => Promise<unknown>
30
+ onRestored: () => void
31
+ }
32
+
33
+ export function EntityVersionsDrawer({
34
+ open,
35
+ onOpenChange,
36
+ entityType,
37
+ versions,
38
+ isPending,
39
+ currentUpdatedAt,
40
+ currentAuthor,
41
+ restoreVersionAction,
42
+ onRestored
43
+ }: EntityVersionsDrawerProps) {
44
+ return (
45
+ <Drawer direction="right" open={open} onOpenChange={onOpenChange} modal>
46
+ <DrawerContent className="h-full sm:max-w-lg">
47
+ <DrawerHeader className="border-b border-border">
48
+ <DrawerTitle>Versions</DrawerTitle>
49
+ <DrawerDescription>
50
+ Review previous {entityType} versions and restore a saved state.
51
+ </DrawerDescription>
52
+ </DrawerHeader>
53
+ <ScrollArea className="min-h-0 flex-1">
54
+ <div className="flex flex-col gap-3 p-4">
55
+ <EntityVersionsCurrentRow updatedAt={currentUpdatedAt} author={currentAuthor} />
56
+ {isPending ? (
57
+ <p className="rounded-2xl border border-dashed border-border px-3 py-6 text-center text-sm text-muted-foreground">
58
+ Loading versions...
59
+ </p>
60
+ ) : null}
61
+ {!isPending && versions.length === 0 ? (
62
+ <p className="rounded-2xl border border-dashed border-border px-3 py-6 text-center text-sm text-muted-foreground">
63
+ No previous versions yet.
64
+ </p>
65
+ ) : null}
66
+ {versions.map((version) => (
67
+ <EntityVersionItem
68
+ key={version.id}
69
+ version={version}
70
+ restoreVersionAction={restoreVersionAction}
71
+ onRestored={onRestored}
72
+ />
73
+ ))}
74
+ </div>
75
+ </ScrollArea>
76
+ </DrawerContent>
77
+ </Drawer>
78
+ )
79
+ }
@@ -0,0 +1,222 @@
1
+ 'use client'
2
+
3
+ import { updateMedia } from '@admin/actions/media'
4
+ import { MediaDeleteDrawer } from '@admin/components/shared/media/media-delete-drawer'
5
+ import { MediaPreview } from '@admin/components/shared/media/media-preview'
6
+ import { Button } from '@admin/components/ui/button'
7
+ import { Card, CardContent, CardFooter } from '@admin/components/ui/card'
8
+ import { DrawerContent, DrawerFooter, DrawerHeader, DrawerTitle } from '@admin/components/ui/drawer'
9
+ import { Form, FormControl, FormField, FormItem, FormLabel } from '@admin/components/ui/form'
10
+ import { Input } from '@admin/components/ui/input'
11
+ import { useCopyToClipboard } from '@admin/hooks/use-copy-to-clipboard'
12
+ import type { AdminMedia } from '@admin/types'
13
+ import { format } from 'date-fns'
14
+ import { Check, ClockPlus, Copy, Link2, LoaderCircle, UserRoundCheck } from 'lucide-react'
15
+ import Link from 'next/link'
16
+ import * as React from 'react'
17
+ import { useForm } from 'react-hook-form'
18
+ import { toast } from 'sonner'
19
+
20
+ interface EditMediaDialogContentProps {
21
+ media: AdminMedia
22
+ onOpenChange: (open: boolean) => void
23
+ onSaved: () => void
24
+ }
25
+
26
+ interface EditMediaFormValues {
27
+ alt: string
28
+ tags: string
29
+ }
30
+
31
+ export function EditMediaDialogContent({
32
+ media,
33
+ onOpenChange,
34
+ onSaved
35
+ }: EditMediaDialogContentProps) {
36
+ const formId = React.useId()
37
+ const form = useForm<EditMediaFormValues>({
38
+ defaultValues: {
39
+ alt: media.alt ?? '',
40
+ tags: media.tags?.join(', ') ?? ''
41
+ }
42
+ })
43
+ const { isDirty } = form.formState
44
+ const [isPending, startSaveTransition] = React.useTransition()
45
+ const [isDeleting, startDeleteTransition] = React.useTransition()
46
+ const [drawerContainer, setDrawerContainer] = React.useState<HTMLDivElement | null>(null)
47
+ const [copyMediaUrl, hasCopiedMediaUrl] = useCopyToClipboard()
48
+ const uploadedBy = media.createdByUser?.name || media.createdByUser?.email || 'System'
49
+
50
+ const handleDrawerContainerRef = React.useCallback((node: HTMLDivElement | null) => {
51
+ setDrawerContainer(node)
52
+ }, [])
53
+
54
+ const handleCopyMediaUrl = React.useCallback(() => {
55
+ void copyMediaUrl(media.url).then((copied) => {
56
+ if (copied) {
57
+ toast.success('Media URL copied')
58
+ return
59
+ }
60
+
61
+ toast.error('Failed to copy media URL')
62
+ })
63
+ }, [copyMediaUrl, media.url])
64
+
65
+ function handleSave(values: EditMediaFormValues) {
66
+ if (isPending || isDeleting || !isDirty) {
67
+ return
68
+ }
69
+
70
+ startSaveTransition(async () => {
71
+ try {
72
+ const tagArray = values.tags
73
+ .split(',')
74
+ .map((tag) => tag.trim())
75
+ .filter(Boolean)
76
+ const result = await updateMedia(media.id, {
77
+ alt: values.alt || undefined,
78
+ tags: tagArray.length > 0 ? tagArray : undefined
79
+ })
80
+ if (result.success) {
81
+ toast.success('Media updated')
82
+ onSaved()
83
+ } else {
84
+ toast.error(result.error ?? 'Failed to update')
85
+ }
86
+ } catch (error) {
87
+ toast.error(error instanceof Error ? error.message : 'Failed to update')
88
+ }
89
+ })
90
+ }
91
+
92
+ return (
93
+ <DrawerContent className="h-full w-full overflow-hidden">
94
+ <div
95
+ ref={handleDrawerContainerRef}
96
+ className="pointer-events-none absolute inset-3 z-20 overflow-hidden rounded-lg"
97
+ />
98
+ <div className="relative z-10 flex min-h-0 flex-1 flex-col overflow-hidden">
99
+ <DrawerHeader>
100
+ <DrawerTitle>Media file</DrawerTitle>
101
+ </DrawerHeader>
102
+ <Form {...form}>
103
+ <form
104
+ id={formId}
105
+ onSubmit={form.handleSubmit(handleSave)}
106
+ className="flex flex-1 flex-col gap-4 overflow-y-auto px-4 pb-4 pt-px"
107
+ >
108
+ <Card>
109
+ <CardContent className="p-0">
110
+ <div className="relative aspect-video overflow-hidden rounded-lg bg-background">
111
+ <MediaPreview media={media} showMetadata />
112
+ </div>
113
+ </CardContent>
114
+
115
+ <CardFooter className="grid w-full pt-5 gap-1 pb-3">
116
+ <div className="grid w-full gap-2">
117
+ <div className="flex w-full min-w-0 items-center gap-x-2">
118
+ <div className="flex min-w-0 flex-1 items-center gap-x-2 text-muted-foreground">
119
+ <UserRoundCheck className="size-4 shrink-0" />
120
+ <span>Uploaded by</span>
121
+ <span className="text-foreground">{uploadedBy}</span>
122
+ </div>
123
+ </div>
124
+
125
+ <div className="flex w-full min-w-0 items-center gap-x-2">
126
+ <div className="flex min-w-0 flex-1 items-center gap-x-2 text-muted-foreground">
127
+ <ClockPlus className="size-4 shrink-0" />
128
+ <span>Uploaded on</span>
129
+ <span className="text-foreground">
130
+ {format(new Date(media.createdAt), 'PPpp')}
131
+ </span>
132
+ </div>
133
+ </div>
134
+ </div>
135
+
136
+ <div className="flex w-full min-w-0 items-center gap-x-2">
137
+ <div className="flex min-w-0 flex-1 items-center gap-x-2 text-muted-foreground">
138
+ <Link2 className="size-4 shrink-0" />
139
+ <Link
140
+ href={media.url}
141
+ target="_blank"
142
+ rel="noopener noreferrer"
143
+ className="min-w-0 truncate text-foreground underline-offset-4 hover:underline"
144
+ >
145
+ {media.url}
146
+ </Link>
147
+ </div>
148
+ <Button
149
+ type="button"
150
+ aria-label={hasCopiedMediaUrl ? 'Media URL copied' : 'Copy media URL'}
151
+ variant="ghost"
152
+ title={hasCopiedMediaUrl ? 'Copied' : 'Copy media URL'}
153
+ onClick={handleCopyMediaUrl}
154
+ className="size-7 shrink-0"
155
+ >
156
+ {hasCopiedMediaUrl ? <Check /> : <Copy />}
157
+ </Button>
158
+ </div>
159
+ </CardFooter>
160
+ </Card>
161
+
162
+ <FormField
163
+ control={form.control}
164
+ name="alt"
165
+ render={({ field }) => (
166
+ <FormItem>
167
+ <FormLabel>Alt text</FormLabel>
168
+ <FormControl>
169
+ <Input
170
+ placeholder="Describe this media for accessibility"
171
+ disabled={isPending || isDeleting}
172
+ {...field}
173
+ />
174
+ </FormControl>
175
+ </FormItem>
176
+ )}
177
+ />
178
+
179
+ <FormField
180
+ control={form.control}
181
+ name="tags"
182
+ render={({ field }) => (
183
+ <FormItem>
184
+ <FormLabel>Tags (comma separated)</FormLabel>
185
+ <FormControl>
186
+ <Input
187
+ placeholder="e.g. hero, product, banner"
188
+ disabled={isPending || isDeleting}
189
+ {...field}
190
+ />
191
+ </FormControl>
192
+ </FormItem>
193
+ )}
194
+ />
195
+ </form>
196
+ </Form>
197
+
198
+ <DrawerFooter>
199
+ <Button type="submit" form={formId} disabled={isPending || isDeleting || !isDirty}>
200
+ {isPending && <LoaderCircle className="animate-spin" />}
201
+ Save
202
+ </Button>
203
+ <MediaDeleteDrawer
204
+ media={media}
205
+ container={drawerContainer}
206
+ disabled={isPending}
207
+ deletePending={isDeleting}
208
+ startDeleteTransition={startDeleteTransition}
209
+ onDeleted={onSaved}
210
+ />
211
+ <Button
212
+ variant="secondary"
213
+ onClick={() => onOpenChange(false)}
214
+ disabled={isPending || isDeleting}
215
+ >
216
+ Cancel
217
+ </Button>
218
+ </DrawerFooter>
219
+ </div>
220
+ </DrawerContent>
221
+ )
222
+ }
@@ -0,0 +1,56 @@
1
+ 'use client'
2
+
3
+ import { Drawer } from '@admin/components/ui/drawer'
4
+ import type { AdminMedia } from '@admin/types'
5
+ import * as React from 'react'
6
+ import { EditMediaDialogContent } from './edit-media-dialog-content'
7
+
8
+ interface EditMediaDialogProps {
9
+ media: AdminMedia | null
10
+ open: boolean
11
+ onOpenChange: (open: boolean) => void
12
+ onSaved: () => void
13
+ }
14
+
15
+ export function EditMediaDialog({ media, open, onOpenChange, onSaved }: EditMediaDialogProps) {
16
+ const [closingMedia, setClosingMedia] = React.useState<AdminMedia | null>(null)
17
+ const isOpen = open && media !== null
18
+ const activeMedia = media ?? closingMedia
19
+
20
+ function handleOpenChange(nextOpen: boolean) {
21
+ if (!nextOpen) {
22
+ setClosingMedia(activeMedia)
23
+ }
24
+ onOpenChange(nextOpen)
25
+ }
26
+
27
+ function handleSaved() {
28
+ setClosingMedia(activeMedia)
29
+ onSaved()
30
+ }
31
+
32
+ return (
33
+ <Drawer
34
+ direction="right"
35
+ open={isOpen}
36
+ onOpenChange={handleOpenChange}
37
+ onAnimationEnd={(nextOpen) => {
38
+ if (nextOpen) {
39
+ setClosingMedia(null)
40
+ return
41
+ }
42
+
43
+ setClosingMedia(null)
44
+ }}
45
+ >
46
+ {activeMedia ? (
47
+ <EditMediaDialogContent
48
+ key={activeMedia.id}
49
+ media={activeMedia}
50
+ onOpenChange={handleOpenChange}
51
+ onSaved={handleSaved}
52
+ />
53
+ ) : null}
54
+ </Drawer>
55
+ )
56
+ }
@@ -0,0 +1,83 @@
1
+ 'use client'
2
+
3
+ import { deleteMedia } from '@admin/actions/media'
4
+ import {
5
+ AlertDialog,
6
+ AlertDialogAction,
7
+ AlertDialogCancel,
8
+ AlertDialogContent,
9
+ AlertDialogDescription,
10
+ AlertDialogFooter,
11
+ AlertDialogHeader,
12
+ AlertDialogTitle,
13
+ AlertDialogTrigger
14
+ } from '@admin/components/ui/alert-dialog'
15
+ import { Button } from '@admin/components/ui/button'
16
+ import type { AdminMedia } from '@admin/types'
17
+ import { Trash2 } from 'lucide-react'
18
+ import * as React from 'react'
19
+ import { toast } from 'sonner'
20
+
21
+ interface MediaDeleteDialogProps {
22
+ media: AdminMedia
23
+ disabled?: boolean
24
+ onDeleted: () => void
25
+ }
26
+
27
+ export function MediaDeleteDialog({ media, disabled = false, onDeleted }: MediaDeleteDialogProps) {
28
+ const [isPending, startDeleteTransition] = React.useTransition()
29
+
30
+ function handleDelete() {
31
+ if (isPending) {
32
+ return
33
+ }
34
+
35
+ startDeleteTransition(async () => {
36
+ try {
37
+ const result = await deleteMedia(media.id)
38
+
39
+ if (result.success) {
40
+ toast.success('Media deleted')
41
+ onDeleted()
42
+ return
43
+ }
44
+
45
+ toast.error(result.error ?? 'Failed to delete media')
46
+ } catch (error) {
47
+ toast.error(error instanceof Error ? error.message : 'Failed to delete media')
48
+ }
49
+ })
50
+ }
51
+
52
+ return (
53
+ <AlertDialog>
54
+ <AlertDialogTrigger asChild>
55
+ <Button variant="destructive" disabled={disabled || isPending}>
56
+ <Trash2 data-icon="inline-start" />
57
+ Delete media
58
+ </Button>
59
+ </AlertDialogTrigger>
60
+ <AlertDialogContent>
61
+ <AlertDialogHeader>
62
+ <AlertDialogTitle>Delete this media?</AlertDialogTitle>
63
+ <AlertDialogDescription>
64
+ This removes {media.filename} from the media library. Storage files are not removed.
65
+ </AlertDialogDescription>
66
+ </AlertDialogHeader>
67
+ <AlertDialogFooter>
68
+ <AlertDialogCancel disabled={isPending}>Cancel</AlertDialogCancel>
69
+ <AlertDialogAction
70
+ variant="destructive"
71
+ disabled={isPending}
72
+ onClick={(event) => {
73
+ event.preventDefault()
74
+ void handleDelete()
75
+ }}
76
+ >
77
+ {isPending ? 'Deleting...' : 'Delete'}
78
+ </AlertDialogAction>
79
+ </AlertDialogFooter>
80
+ </AlertDialogContent>
81
+ </AlertDialog>
82
+ )
83
+ }