@tulip-systems/core 0.5.2 → 0.5.4

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 (423) hide show
  1. package/dist/auth/client.d.mts +3 -3
  2. package/dist/auth/client.mjs +2 -2
  3. package/dist/components/client.d.mts +2 -2
  4. package/dist/components/common/icons.d.mts +2 -2
  5. package/dist/components/common/icons.d.mts.map +1 -1
  6. package/dist/components/common/status.d.mts +3 -3
  7. package/dist/components/common/status.d.mts.map +1 -1
  8. package/dist/components/editor/components/content.client.d.mts +2 -2
  9. package/dist/components/editor/components/content.client.d.mts.map +1 -1
  10. package/dist/components/editor/components/editor.client.d.mts +6 -6
  11. package/dist/components/editor/components/editor.client.d.mts.map +1 -1
  12. package/dist/components/editor/components/editor.client.mjs +2 -2
  13. package/dist/components/editor/components/editor.client.mjs.map +1 -1
  14. package/dist/components/editor/components/menu.client.d.mts +3 -3
  15. package/dist/components/editor/components/menu.client.d.mts.map +1 -1
  16. package/dist/components/editor/components/menu.client.mjs +3 -0
  17. package/dist/components/editor/components/menu.client.mjs.map +1 -1
  18. package/dist/components/editor/extensions/file-handler/extension.d.mts +21 -0
  19. package/dist/components/editor/extensions/file-handler/extension.d.mts.map +1 -0
  20. package/dist/components/editor/extensions/file-handler/extension.mjs +40 -0
  21. package/dist/components/editor/extensions/file-handler/extension.mjs.map +1 -0
  22. package/dist/components/editor/extensions/file-handler/strategy.d.mts +29 -0
  23. package/dist/components/editor/extensions/file-handler/strategy.d.mts.map +1 -0
  24. package/dist/components/editor/extensions/file-handler/strategy.mjs +111 -0
  25. package/dist/components/editor/extensions/file-handler/strategy.mjs.map +1 -0
  26. package/dist/components/editor/extensions/file-handler/utils.mjs +50 -0
  27. package/dist/components/editor/extensions/file-handler/utils.mjs.map +1 -0
  28. package/dist/components/editor/extensions/image/extension.d.mts +8 -0
  29. package/dist/components/editor/extensions/image/extension.d.mts.map +1 -0
  30. package/dist/components/editor/extensions/image/extension.mjs +51 -0
  31. package/dist/components/editor/extensions/image/extension.mjs.map +1 -0
  32. package/dist/components/editor/extensions/skeleton/extension.mjs +41 -0
  33. package/dist/components/editor/extensions/skeleton/extension.mjs.map +1 -0
  34. package/dist/components/editor/extensions/skeleton/renderer.mjs +25 -0
  35. package/dist/components/editor/extensions/skeleton/renderer.mjs.map +1 -0
  36. package/dist/components/editor/lib/constants.d.mts +31 -28
  37. package/dist/components/editor/lib/constants.d.mts.map +1 -1
  38. package/dist/components/editor/lib/constants.mjs +17 -5
  39. package/dist/components/editor/lib/constants.mjs.map +1 -1
  40. package/dist/components/editor/lib/extensions.d.mts +7 -3
  41. package/dist/components/editor/lib/extensions.d.mts.map +1 -1
  42. package/dist/components/editor/lib/extensions.mjs.map +1 -1
  43. package/dist/components/editor/lib/helpers.d.mts.map +1 -1
  44. package/dist/components/editor/lib/helpers.mjs +38 -3
  45. package/dist/components/editor/lib/helpers.mjs.map +1 -1
  46. package/dist/components/editor/lib/variants.mjs +10 -2
  47. package/dist/components/editor/lib/variants.mjs.map +1 -1
  48. package/dist/components/header/back-button.client.d.mts +2 -2
  49. package/dist/components/header/back-button.client.d.mts.map +1 -1
  50. package/dist/components/header/bottom-bar.client.d.mts +3 -3
  51. package/dist/components/header/bottom-bar.client.d.mts.map +1 -1
  52. package/dist/components/header/breadcrumbs.client.d.mts +7 -7
  53. package/dist/components/header/breadcrumbs.client.d.mts.map +1 -1
  54. package/dist/components/header/header.client.d.mts +2 -2
  55. package/dist/components/header/header.client.d.mts.map +1 -1
  56. package/dist/components/header/mobile-nav-switcher.client.d.mts +2 -2
  57. package/dist/components/header/mobile-nav-switcher.client.d.mts.map +1 -1
  58. package/dist/components/header/top-bar.client.d.mts +4 -4
  59. package/dist/components/header/top-bar.client.d.mts.map +1 -1
  60. package/dist/components/layouts/admin-content.client.d.mts +2 -2
  61. package/dist/components/layouts/admin-content.client.d.mts.map +1 -1
  62. package/dist/components/layouts/admin-layout.d.mts +2 -2
  63. package/dist/components/layouts/admin-layout.d.mts.map +1 -1
  64. package/dist/components/layouts/admin-loading.d.mts +2 -2
  65. package/dist/components/layouts/admin-loading.d.mts.map +1 -1
  66. package/dist/components/layouts/empty-page.d.mts +4 -4
  67. package/dist/components/layouts/empty-page.d.mts.map +1 -1
  68. package/dist/components/layouts/error-page.d.mts +23 -0
  69. package/dist/components/layouts/error-page.d.mts.map +1 -0
  70. package/dist/components/layouts/error-page.mjs +58 -0
  71. package/dist/components/layouts/error-page.mjs.map +1 -0
  72. package/dist/components/layouts/list-layout.d.mts +2 -2
  73. package/dist/components/layouts/list-layout.d.mts.map +1 -1
  74. package/dist/components/layouts/not-allowed-page.d.mts +22 -0
  75. package/dist/components/layouts/not-allowed-page.d.mts.map +1 -0
  76. package/dist/components/layouts/not-allowed-page.mjs +25 -0
  77. package/dist/components/layouts/not-allowed-page.mjs.map +1 -0
  78. package/dist/components/layouts/not-found-page.d.mts +3 -3
  79. package/dist/components/layouts/not-found-page.d.mts.map +1 -1
  80. package/dist/components/layouts/providers.client.d.mts +2 -2
  81. package/dist/components/layouts/providers.client.d.mts.map +1 -1
  82. package/dist/components/layouts/root-layout.server.d.mts +2 -2
  83. package/dist/components/layouts/root-layout.server.d.mts.map +1 -1
  84. package/dist/components/layouts/root-loading.d.mts +2 -2
  85. package/dist/components/layouts/root-loading.d.mts.map +1 -1
  86. package/dist/components/layouts/tab-layout.d.mts +2 -2
  87. package/dist/components/layouts/tab-layout.d.mts.map +1 -1
  88. package/dist/components/lists/data-list.d.mts +5 -5
  89. package/dist/components/lists/data-list.d.mts.map +1 -1
  90. package/dist/components/lists/data-stack.d.mts +8 -8
  91. package/dist/components/lists/data-stack.d.mts.map +1 -1
  92. package/dist/components/navigation/admin-sidebar-paths.client.d.mts +21 -11
  93. package/dist/components/navigation/admin-sidebar-paths.client.d.mts.map +1 -1
  94. package/dist/components/navigation/admin-sidebar-paths.client.mjs +7 -5
  95. package/dist/components/navigation/admin-sidebar-paths.client.mjs.map +1 -1
  96. package/dist/components/ui/accordion.d.mts +5 -5
  97. package/dist/components/ui/accordion.d.mts.map +1 -1
  98. package/dist/components/ui/alert-dialog.d.mts +12 -12
  99. package/dist/components/ui/alert-dialog.d.mts.map +1 -1
  100. package/dist/components/ui/alert.d.mts +6 -6
  101. package/dist/components/ui/alert.d.mts.map +1 -1
  102. package/dist/components/ui/aspect-ratio.d.mts +2 -2
  103. package/dist/components/ui/aspect-ratio.d.mts.map +1 -1
  104. package/dist/components/ui/avatar.client.d.mts +4 -4
  105. package/dist/components/ui/avatar.client.d.mts.map +1 -1
  106. package/dist/components/ui/badge.d.mts +4 -4
  107. package/dist/components/ui/badge.d.mts.map +1 -1
  108. package/dist/components/ui/breadcrumb.d.mts +8 -8
  109. package/dist/components/ui/breadcrumb.d.mts.map +1 -1
  110. package/dist/components/ui/button.d.mts +5 -5
  111. package/dist/components/ui/button.d.mts.map +1 -1
  112. package/dist/components/ui/calendar.d.mts +3 -3
  113. package/dist/components/ui/calendar.d.mts.map +1 -1
  114. package/dist/components/ui/card.d.mts +7 -7
  115. package/dist/components/ui/card.d.mts.map +1 -1
  116. package/dist/components/ui/carousel.d.mts +6 -6
  117. package/dist/components/ui/carousel.d.mts.map +1 -1
  118. package/dist/components/ui/chart.client.d.mts +5 -5
  119. package/dist/components/ui/chart.client.d.mts.map +1 -1
  120. package/dist/components/ui/checkbox.d.mts +2 -2
  121. package/dist/components/ui/checkbox.d.mts.map +1 -1
  122. package/dist/components/ui/collapsible.client.d.mts +4 -4
  123. package/dist/components/ui/collapsible.client.d.mts.map +1 -1
  124. package/dist/components/ui/color-picker.client.d.mts +2 -2
  125. package/dist/components/ui/color-picker.client.d.mts.map +1 -1
  126. package/dist/components/ui/combobox-dropdown.client.d.mts +2 -2
  127. package/dist/components/ui/combobox-dropdown.client.d.mts.map +1 -1
  128. package/dist/components/ui/combobox.client.d.mts +2 -2
  129. package/dist/components/ui/combobox.client.d.mts.map +1 -1
  130. package/dist/components/ui/command.d.mts +10 -10
  131. package/dist/components/ui/command.d.mts.map +1 -1
  132. package/dist/components/ui/context-menu.d.mts +16 -16
  133. package/dist/components/ui/context-menu.d.mts.map +1 -1
  134. package/dist/components/ui/date-picker.client.d.mts +2 -2
  135. package/dist/components/ui/date-picker.client.d.mts.map +1 -1
  136. package/dist/components/ui/dialog.client.d.mts +11 -11
  137. package/dist/components/ui/dialog.client.d.mts.map +1 -1
  138. package/dist/components/ui/drawer.client.d.mts +11 -11
  139. package/dist/components/ui/drawer.client.d.mts.map +1 -1
  140. package/dist/components/ui/dropdown-menu.d.mts +16 -16
  141. package/dist/components/ui/form.client.d.mts +7 -7
  142. package/dist/components/ui/form.client.d.mts.map +1 -1
  143. package/dist/components/ui/hover-card.client.d.mts +4 -4
  144. package/dist/components/ui/hover-card.client.d.mts.map +1 -1
  145. package/dist/components/ui/input-recipient.d.mts +2 -2
  146. package/dist/components/ui/input.d.mts +2 -2
  147. package/dist/components/ui/label.d.mts +2 -2
  148. package/dist/components/ui/navigation-menu.d.mts +11 -11
  149. package/dist/components/ui/pagination.d.mts +8 -8
  150. package/dist/components/ui/popover.d.mts +5 -5
  151. package/dist/components/ui/progress.client.d.mts +2 -2
  152. package/dist/components/ui/progress.client.d.mts.map +1 -1
  153. package/dist/components/ui/radio-group.d.mts +3 -3
  154. package/dist/components/ui/resizable.client.d.mts +4 -4
  155. package/dist/components/ui/resizable.client.d.mts.map +1 -1
  156. package/dist/components/ui/scroll-area.d.mts +3 -3
  157. package/dist/components/ui/select.client.d.mts +11 -11
  158. package/dist/components/ui/select.client.d.mts.map +1 -1
  159. package/dist/components/ui/separator.d.mts +2 -2
  160. package/dist/components/ui/sheet.client.d.mts +9 -9
  161. package/dist/components/ui/sheet.client.d.mts.map +1 -1
  162. package/dist/components/ui/sidebar.client.d.mts +26 -26
  163. package/dist/components/ui/sidebar.client.d.mts.map +1 -1
  164. package/dist/components/ui/skeleton.d.mts +2 -2
  165. package/dist/components/ui/slider.d.mts +2 -2
  166. package/dist/components/ui/sonner.client.d.mts +2 -2
  167. package/dist/components/ui/switch.d.mts +2 -2
  168. package/dist/components/ui/tabs.d.mts +5 -5
  169. package/dist/components/ui/textarea.d.mts +2 -2
  170. package/dist/components/ui/time-input.client.d.mts +2 -2
  171. package/dist/components/ui/toggle-group.client.d.mts +3 -3
  172. package/dist/components/ui/toggle.d.mts +2 -2
  173. package/dist/components/ui/toggle.d.mts.map +1 -1
  174. package/dist/components/ui/tooltip.client.d.mts +5 -5
  175. package/dist/components/ui/tooltip.client.d.mts.map +1 -1
  176. package/dist/components/ui/tooltip.client.mjs +1 -1
  177. package/dist/components/ui/tooltip.client.mjs.map +1 -1
  178. package/dist/components.d.mts +4 -1
  179. package/dist/components.mjs +5 -2
  180. package/dist/data-tables.d.mts +2 -2
  181. package/dist/data-tables.mjs +2 -2
  182. package/dist/inline-edit/client.d.mts +2 -3
  183. package/dist/inline-edit/client.mjs +1 -2
  184. package/dist/lib/client.d.mts +2 -2
  185. package/dist/lib/client.mjs +2 -2
  186. package/dist/lib/hooks/use-action.d.mts +2 -2
  187. package/dist/lib/hooks/use-indicator.d.mts +11 -3
  188. package/dist/lib/hooks/use-indicator.d.mts.map +1 -1
  189. package/dist/lib/hooks/use-indicator.mjs +19 -8
  190. package/dist/lib/hooks/use-indicator.mjs.map +1 -1
  191. package/dist/modules/auth/components/allowed.client.d.mts +21 -7
  192. package/dist/modules/auth/components/allowed.client.d.mts.map +1 -1
  193. package/dist/modules/auth/components/allowed.client.mjs +8 -2
  194. package/dist/modules/auth/components/allowed.client.mjs.map +1 -1
  195. package/dist/modules/auth/components/auth-layout.server.d.mts +2 -2
  196. package/dist/modules/auth/components/auth-layout.server.d.mts.map +1 -1
  197. package/dist/modules/auth/components/auth-loading.d.mts +2 -2
  198. package/dist/modules/auth/components/auth-loading.d.mts.map +1 -1
  199. package/dist/modules/auth/components/create-first-user-page.client.d.mts +2 -2
  200. package/dist/modules/auth/components/create-first-user-page.client.d.mts.map +1 -1
  201. package/dist/modules/auth/components/forget-password-page.client.d.mts +2 -2
  202. package/dist/modules/auth/components/forget-password-page.client.d.mts.map +1 -1
  203. package/dist/modules/auth/components/guard-first-user.server.d.mts +2 -2
  204. package/dist/modules/auth/components/guard-first-user.server.d.mts.map +1 -1
  205. package/dist/modules/auth/components/guard.server.d.mts +2 -2
  206. package/dist/modules/auth/components/guard.server.d.mts.map +1 -1
  207. package/dist/modules/auth/components/login-page.client.d.mts +2 -2
  208. package/dist/modules/auth/components/login-page.client.d.mts.map +1 -1
  209. package/dist/modules/auth/components/reset-password-page.client.d.mts +2 -2
  210. package/dist/modules/auth/components/reset-password-page.client.d.mts.map +1 -1
  211. package/dist/modules/auth/components/update-password-command.d.mts +2 -2
  212. package/dist/modules/auth/components/update-password-command.d.mts.map +1 -1
  213. package/dist/modules/auth/db/schema.d.mts +73 -73
  214. package/dist/modules/auth/db/schema.d.mts.map +1 -1
  215. package/dist/modules/auth/handler/client.client.d.mts +143 -143
  216. package/dist/modules/auth/handler/client.client.d.mts.map +1 -1
  217. package/dist/modules/auth/handler/init.d.mts +131 -131
  218. package/dist/modules/auth/handler/init.d.mts.map +1 -1
  219. package/dist/modules/auth/hooks/use-permission.d.mts +48 -4
  220. package/dist/modules/auth/hooks/use-permission.d.mts.map +1 -1
  221. package/dist/modules/auth/hooks/use-permission.mjs +51 -4
  222. package/dist/modules/auth/hooks/use-permission.mjs.map +1 -1
  223. package/dist/modules/auth/lib/helpers.server.mjs +1 -1
  224. package/dist/modules/auth/lib/validators.d.mts +2 -2
  225. package/dist/modules/auth/lib/validators.d.mts.map +1 -1
  226. package/dist/modules/commands/components/alert-dialog-command.client.d.mts +10 -10
  227. package/dist/modules/commands/components/click-command.client.d.mts +2 -2
  228. package/dist/modules/commands/components/command-trigger.client.d.mts +6 -6
  229. package/dist/modules/commands/components/dialog-command.client.d.mts +8 -8
  230. package/dist/modules/commands/components/dropdown-command.client.d.mts +5 -5
  231. package/dist/modules/commands/components/empty-command.client.d.mts +2 -2
  232. package/dist/modules/commands/components/form-dialog-command.client.d.mts +11 -11
  233. package/dist/modules/commands/hooks/use-command-menu.client.d.mts.map +1 -1
  234. package/dist/modules/commands/hooks/use-command-menu.client.mjs +0 -5
  235. package/dist/modules/commands/hooks/use-command-menu.client.mjs.map +1 -1
  236. package/dist/modules/commands/hooks/use-command-mutation.client.d.mts +2 -2
  237. package/dist/modules/commands/menus/context-menu.client.d.mts +2 -2
  238. package/dist/modules/commands/menus/dropdown-menu.client.d.mts +3 -3
  239. package/dist/modules/commands/menus/inline-menu.client.d.mts +3 -3
  240. package/dist/modules/commands/menus/responsive-menu.client.d.mts +3 -3
  241. package/dist/modules/commands/utils/archive-command.client.d.mts +3 -3
  242. package/dist/modules/commands/utils/delete-command.client.d.mts +3 -3
  243. package/dist/modules/config/db/helpers.d.mts +5 -5
  244. package/dist/modules/config/db/helpers.d.mts.map +1 -1
  245. package/dist/modules/data-tables/components/cell/common.client.d.mts +5 -5
  246. package/dist/modules/data-tables/components/column-header.d.mts +2 -2
  247. package/dist/modules/data-tables/components/filters/combobox.client.d.mts +2 -2
  248. package/dist/modules/data-tables/components/filters/slider.client.d.mts +2 -2
  249. package/dist/modules/data-tables/components/header.d.mts +4 -4
  250. package/dist/modules/data-tables/components/layout.d.mts +2 -2
  251. package/dist/modules/data-tables/components/search-input.client.d.mts +2 -2
  252. package/dist/modules/data-tables/components/skeleton.d.mts +2 -2
  253. package/dist/modules/data-tables/components/table.d.mts +7 -7
  254. package/dist/modules/data-tables/components/toolbar.d.mts +3 -3
  255. package/dist/modules/data-tables/hooks/use-context.client.d.mts +2 -2
  256. package/dist/modules/data-tables/lib/filters/parsers.d.mts +6 -1
  257. package/dist/modules/data-tables/lib/filters/parsers.d.mts.map +1 -1
  258. package/dist/modules/data-tables/lib/filters/parsers.mjs +6 -1
  259. package/dist/modules/data-tables/lib/filters/parsers.mjs.map +1 -1
  260. package/dist/modules/data-tables/tables/data-table/components/table.d.mts +2 -2
  261. package/dist/modules/data-tables/tables/inline-table/components/cells/common.d.mts +2 -2
  262. package/dist/modules/data-tables/tables/inline-table/components/cells/drag-handle.client.d.mts +2 -2
  263. package/dist/modules/data-tables/tables/inline-table/components/inputs/advanced-select.client.d.mts +2 -2
  264. package/dist/modules/data-tables/tables/inline-table/components/inputs/combobox.client.d.mts +2 -2
  265. package/dist/modules/data-tables/tables/inline-table/components/inputs/input.client.d.mts +3 -3
  266. package/dist/modules/data-tables/tables/inline-table/components/inputs/read-only.d.mts +2 -2
  267. package/dist/modules/data-tables/tables/inline-table/components/inputs/select.client.d.mts +2 -2
  268. package/dist/modules/data-tables/tables/inline-table/components/table.d.mts +2 -2
  269. package/dist/modules/data-tables/tables/inline-table/hooks/context.client.d.mts +2 -2
  270. package/dist/modules/inline-edit/components/combobox-dropdown.client.d.mts +6 -4
  271. package/dist/modules/inline-edit/components/combobox-dropdown.client.d.mts.map +1 -1
  272. package/dist/modules/inline-edit/components/combobox-dropdown.client.mjs +11 -6
  273. package/dist/modules/inline-edit/components/combobox-dropdown.client.mjs.map +1 -1
  274. package/dist/modules/inline-edit/components/combobox.client.d.mts +6 -4
  275. package/dist/modules/inline-edit/components/combobox.client.d.mts.map +1 -1
  276. package/dist/modules/inline-edit/components/combobox.client.mjs +7 -5
  277. package/dist/modules/inline-edit/components/combobox.client.mjs.map +1 -1
  278. package/dist/modules/inline-edit/components/date-input.client.d.mts +5 -3
  279. package/dist/modules/inline-edit/components/date-input.client.d.mts.map +1 -1
  280. package/dist/modules/inline-edit/components/date-input.client.mjs +26 -13
  281. package/dist/modules/inline-edit/components/date-input.client.mjs.map +1 -1
  282. package/dist/modules/inline-edit/components/date-picker.client.d.mts +6 -4
  283. package/dist/modules/inline-edit/components/date-picker.client.d.mts.map +1 -1
  284. package/dist/modules/inline-edit/components/date-picker.client.mjs +12 -7
  285. package/dist/modules/inline-edit/components/date-picker.client.mjs.map +1 -1
  286. package/dist/modules/inline-edit/components/editor.client.d.mts +6 -4
  287. package/dist/modules/inline-edit/components/editor.client.d.mts.map +1 -1
  288. package/dist/modules/inline-edit/components/editor.client.mjs +11 -6
  289. package/dist/modules/inline-edit/components/editor.client.mjs.map +1 -1
  290. package/dist/modules/inline-edit/components/input-recipient.client.d.mts +6 -4
  291. package/dist/modules/inline-edit/components/input-recipient.client.d.mts.map +1 -1
  292. package/dist/modules/inline-edit/components/input-recipient.client.mjs +11 -6
  293. package/dist/modules/inline-edit/components/input-recipient.client.mjs.map +1 -1
  294. package/dist/modules/inline-edit/components/input-toggle.client.d.mts +6 -4
  295. package/dist/modules/inline-edit/components/input-toggle.client.d.mts.map +1 -1
  296. package/dist/modules/inline-edit/components/input-toggle.client.mjs +9 -7
  297. package/dist/modules/inline-edit/components/input-toggle.client.mjs.map +1 -1
  298. package/dist/modules/inline-edit/components/input.client.d.mts +13 -10
  299. package/dist/modules/inline-edit/components/input.client.d.mts.map +1 -1
  300. package/dist/modules/inline-edit/components/input.client.mjs +35 -19
  301. package/dist/modules/inline-edit/components/input.client.mjs.map +1 -1
  302. package/dist/modules/inline-edit/components/select.client.d.mts +10 -9
  303. package/dist/modules/inline-edit/components/select.client.d.mts.map +1 -1
  304. package/dist/modules/inline-edit/components/select.client.mjs +14 -7
  305. package/dist/modules/inline-edit/components/select.client.mjs.map +1 -1
  306. package/dist/modules/inline-edit/components/switch.client.d.mts +5 -3
  307. package/dist/modules/inline-edit/components/switch.client.d.mts.map +1 -1
  308. package/dist/modules/inline-edit/components/switch.client.mjs +8 -6
  309. package/dist/modules/inline-edit/components/switch.client.mjs.map +1 -1
  310. package/dist/modules/inline-edit/components/toggle.client.d.mts +6 -4
  311. package/dist/modules/inline-edit/components/toggle.client.d.mts.map +1 -1
  312. package/dist/modules/inline-edit/components/toggle.client.mjs +9 -7
  313. package/dist/modules/inline-edit/components/toggle.client.mjs.map +1 -1
  314. package/dist/modules/inline-edit/hooks/context.client.d.mts +2 -2
  315. package/dist/modules/inline-edit/hooks/use-inline.client.d.mts +35 -3
  316. package/dist/modules/inline-edit/hooks/use-inline.client.d.mts.map +1 -1
  317. package/dist/modules/inline-edit/hooks/use-inline.client.mjs +71 -9
  318. package/dist/modules/inline-edit/hooks/use-inline.client.mjs.map +1 -1
  319. package/dist/modules/inline-edit/lib/variants.d.mts +1 -0
  320. package/dist/modules/inline-edit/lib/variants.d.mts.map +1 -1
  321. package/dist/modules/inline-edit/lib/variants.mjs +12 -4
  322. package/dist/modules/inline-edit/lib/variants.mjs.map +1 -1
  323. package/dist/modules/router/handler/init.server.d.mts +4 -4
  324. package/dist/modules/router/handler/init.server.mjs +1 -1
  325. package/dist/modules/router/lib/query-client.server.d.mts +2 -2
  326. package/dist/modules/storage/components/dropzone-context.client.d.mts +2 -2
  327. package/dist/modules/storage/components/dropzone.client.d.mts +5 -5
  328. package/dist/modules/storage/components/image-grid.client.d.mts +3 -3
  329. package/dist/modules/storage/components/image-grid.client.mjs +2 -2
  330. package/dist/modules/storage/components/image-grid.client.mjs.map +1 -1
  331. package/dist/modules/storage/components/upload-zone-context.client.d.mts +3 -11
  332. package/dist/modules/storage/components/upload-zone-context.client.d.mts.map +1 -1
  333. package/dist/modules/storage/components/upload-zone-context.client.mjs.map +1 -1
  334. package/dist/modules/storage/components/upload-zone.client.d.mts +10 -33
  335. package/dist/modules/storage/components/upload-zone.client.d.mts.map +1 -1
  336. package/dist/modules/storage/components/upload-zone.client.mjs +19 -93
  337. package/dist/modules/storage/components/upload-zone.client.mjs.map +1 -1
  338. package/dist/modules/storage/config/filters.d.mts +1 -0
  339. package/dist/modules/storage/config/filters.d.mts.map +1 -1
  340. package/dist/modules/storage/config/filters.mjs +1 -0
  341. package/dist/modules/storage/config/filters.mjs.map +1 -1
  342. package/dist/modules/storage/lib/create-upload.client.d.mts +56 -0
  343. package/dist/modules/storage/lib/create-upload.client.d.mts.map +1 -0
  344. package/dist/modules/storage/lib/create-upload.client.mjs +98 -0
  345. package/dist/modules/storage/lib/create-upload.client.mjs.map +1 -0
  346. package/dist/modules/storage/lib/helpers.d.mts +1 -1
  347. package/dist/modules/storage/lib/router.server.d.mts +3239 -2319
  348. package/dist/modules/storage/lib/router.server.d.mts.map +1 -1
  349. package/dist/modules/storage/lib/router.server.mjs +5 -2
  350. package/dist/modules/storage/lib/router.server.mjs.map +1 -1
  351. package/dist/modules/storage/lib/schema.d.mts +124 -90
  352. package/dist/modules/storage/lib/schema.d.mts.map +1 -1
  353. package/dist/modules/storage/lib/schema.mjs +2 -1
  354. package/dist/modules/storage/lib/schema.mjs.map +1 -1
  355. package/dist/modules/storage/lib/service.server.d.mts +28 -39
  356. package/dist/modules/storage/lib/service.server.d.mts.map +1 -1
  357. package/dist/modules/storage/lib/service.server.mjs +6 -6
  358. package/dist/modules/storage/lib/service.server.mjs.map +1 -1
  359. package/dist/modules/storage/lib/validators.d.mts +166 -86
  360. package/dist/modules/storage/lib/validators.d.mts.map +1 -1
  361. package/dist/modules/storage/lib/validators.mjs +2 -1
  362. package/dist/modules/storage/lib/validators.mjs.map +1 -1
  363. package/dist/router/server.mjs +1 -1
  364. package/dist/storage/client.d.mts +2 -1
  365. package/dist/storage/client.mjs +2 -8
  366. package/dist/storage.d.mts +2 -2
  367. package/dist/storage.mjs +1 -1
  368. package/package.json +4 -3
  369. package/src/components/editor/components/editor.client.tsx +7 -2
  370. package/src/components/editor/components/menu.client.tsx +26 -0
  371. package/src/components/editor/extensions/file-handler/extension.ts +69 -0
  372. package/src/components/editor/extensions/file-handler/strategy.ts +167 -0
  373. package/src/components/editor/extensions/file-handler/utils.ts +51 -0
  374. package/src/components/editor/extensions/image/extension.ts +55 -0
  375. package/src/components/editor/extensions/skeleton/extension.ts +35 -0
  376. package/src/components/editor/extensions/skeleton/renderer.tsx +14 -0
  377. package/src/components/editor/lib/constants.ts +16 -2
  378. package/src/components/editor/lib/extensions.ts +4 -1
  379. package/src/components/editor/lib/helpers.ts +39 -3
  380. package/src/components/editor/lib/utils.ts +32 -0
  381. package/src/components/editor/lib/variants.ts +72 -1
  382. package/src/components/entry.ts +4 -1
  383. package/src/components/layouts/error-page.tsx +61 -0
  384. package/src/components/layouts/not-allowed-page.tsx +1 -1
  385. package/src/components/navigation/admin-sidebar-paths.client.tsx +18 -2
  386. package/src/components/ui/tooltip.client.tsx +1 -1
  387. package/src/lib/hooks/use-indicator.tsx +27 -18
  388. package/src/modules/auth/components/allowed.client.tsx +35 -7
  389. package/src/modules/auth/hooks/use-permission.ts +88 -2
  390. package/src/modules/commands/hooks/use-command-menu.client.tsx +4 -7
  391. package/src/modules/data-tables/lib/filters/parsers.ts +14 -0
  392. package/src/modules/inline-edit/components/combobox-dropdown.client.tsx +21 -12
  393. package/src/modules/inline-edit/components/combobox.client.tsx +13 -6
  394. package/src/modules/inline-edit/components/date-input.client.tsx +29 -13
  395. package/src/modules/inline-edit/components/date-picker.client.tsx +13 -9
  396. package/src/modules/inline-edit/components/editor.client.tsx +14 -14
  397. package/src/modules/inline-edit/components/input-recipient.client.tsx +15 -8
  398. package/src/modules/inline-edit/components/input-toggle.client.tsx +16 -9
  399. package/src/modules/inline-edit/components/input.client.tsx +38 -24
  400. package/src/modules/inline-edit/components/select.client.tsx +16 -9
  401. package/src/modules/inline-edit/components/switch.client.tsx +10 -6
  402. package/src/modules/inline-edit/components/toggle.client.tsx +13 -9
  403. package/src/modules/inline-edit/entry.client.ts +0 -1
  404. package/src/modules/inline-edit/hooks/use-inline.client.tsx +139 -20
  405. package/src/modules/inline-edit/lib/variants.ts +7 -0
  406. package/src/modules/storage/components/image-grid.client.tsx +2 -2
  407. package/src/modules/storage/components/upload-zone-context.client.tsx +3 -4
  408. package/src/modules/storage/components/upload-zone.client.tsx +17 -116
  409. package/src/modules/storage/config/filters.ts +1 -0
  410. package/src/modules/storage/entry.client.ts +2 -1
  411. package/src/modules/storage/lib/create-upload.client.ts +134 -0
  412. package/src/modules/storage/lib/router.server.ts +1 -0
  413. package/src/modules/storage/lib/schema.ts +1 -0
  414. package/src/modules/storage/lib/service.server.ts +10 -4
  415. package/src/modules/storage/lib/validators.ts +1 -0
  416. package/dist/modules/auth/lib/utils.mjs +0 -21
  417. package/dist/modules/auth/lib/utils.mjs.map +0 -1
  418. package/dist/modules/inline-edit/components/deprecated-editor.client.d.mts +0 -21
  419. package/dist/modules/inline-edit/components/deprecated-editor.client.d.mts.map +0 -1
  420. package/dist/modules/inline-edit/components/deprecated-editor.client.mjs +0 -37
  421. package/dist/modules/inline-edit/components/deprecated-editor.client.mjs.map +0 -1
  422. package/dist/storage/client.mjs.map +0 -1
  423. package/src/modules/inline-edit/components/deprecated-editor.client.tsx +0 -48
@@ -1,40 +1,105 @@
1
1
  "use client";
2
2
 
3
3
  import { EditorJSONContent } from "@/components/entry";
4
+ import { useLocalIndicator } from "@/lib/entry.client";
4
5
  import { useAction } from "@/lib/hooks/use-action";
5
6
  import { usePermission } from "@/modules/auth/hooks/use-permission";
6
7
  import { Permission } from "@/modules/auth/lib/permissions";
7
- import { useState } from "react";
8
+ import { useCallback, useState } from "react";
8
9
  import { toast } from "sonner";
10
+ import { useDebouncedCallback } from "use-debounce";
9
11
  import { useInlineEditContext } from "./context.client";
10
12
 
11
- export type useInlineEditOptions<TValue> = {
12
- initialValue?: TValue;
13
- action: (value: { id: string; value: TValue }) => Promise<unknown>;
13
+ type UpdateStrategy =
14
+ | {
15
+ mode: "change";
16
+ delay?: number;
17
+ }
18
+ | {
19
+ mode: "blur";
20
+ };
21
+
22
+ const defaultUpdateStrategy: UpdateStrategy = {
23
+ mode: "blur",
24
+ };
25
+
26
+ type MaybeValue<TValue, Required extends boolean> = Required extends true ? TValue : TValue | null;
27
+
28
+ /**
29
+ * Use the inline edit hook options.
30
+ */
31
+ export type useInlineEditOptions<TValue, Required extends boolean = false> = {
32
+ /**
33
+ * Initial value
34
+ */
35
+ initialValue?: MaybeValue<TValue, Required>;
36
+ /**
37
+ * Action to execute on update
38
+ */
39
+ action: (value: { id: string; value: MaybeValue<TValue, Required> }) => Promise<unknown>;
40
+ /**
41
+ * Permission required to edit
42
+ */
14
43
  permission?: Permission;
44
+ /**
45
+ * Update strategy
46
+ * - trigger: when to update the value (default: "blur")
47
+ * - delay: delay in milliseconds before updating the value (only for "change" trigger)
48
+ * @default { trigger: "blur" }
49
+ */
50
+ updateStrategy?: UpdateStrategy;
51
+ /**
52
+ * Is the value required
53
+ * @default false
54
+ */
55
+ isRequired?: Required;
15
56
  };
16
57
 
58
+ /**
59
+ * Use the inline edit hook.
60
+ */
17
61
  export function useInlineEdit<
18
- TValue extends string | number | Date | boolean | JSON | EditorJSONContent | undefined,
19
- >(props: useInlineEditOptions<TValue>) {
62
+ TValue extends string | number | Date | boolean | JSON | EditorJSONContent,
63
+ Required extends boolean = false,
64
+ >(props: useInlineEditOptions<TValue, Required>) {
65
+ /**
66
+ * The update strategy.
67
+ */
68
+ const strategy = props.updateStrategy ?? defaultUpdateStrategy;
69
+ const delay = strategy.mode === "change" ? (strategy.delay ?? 0) : 0;
70
+
20
71
  /**
21
72
  * The inline edit context.
22
73
  */
23
74
  const context = useInlineEditContext();
24
75
 
25
- const { data } = usePermission(props.permission ?? context.permission);
26
- const isAllowed = data?.success;
27
-
28
76
  /**
29
77
  * The current value of the inline edit.
30
78
  */
31
- const [value, setValue] = useState(props.initialValue);
79
+ const [value, setValue] = useState<MaybeValue<TValue, Required> | undefined>(props.initialValue);
80
+
81
+ /**
82
+ * The indicator status.
83
+ */
84
+ const { status, setStatus } = useLocalIndicator();
85
+
86
+ /**
87
+ * Permission guard
88
+ */
89
+ const { data } = usePermission(props.permission ?? context.permission);
90
+ const isAllowed = data?.success;
32
91
 
33
92
  /**
34
93
  * Execute the action to update the value.
35
94
  */
36
- const { mutate, status } = useAction({
95
+ const mutation = useAction({
37
96
  mutationFn: props.action,
97
+ onMutate: () => {
98
+ setStatus("pending");
99
+ },
100
+ onSuccess: () => {
101
+ setStatus("success");
102
+ },
38
103
  onError: (error) => {
39
104
  setValue(props.initialValue);
40
105
  toast.error("Er is iets misgegaan", {
@@ -44,19 +109,73 @@ export function useInlineEdit<
44
109
  });
45
110
 
46
111
  /**
47
- * Handle the update of the value.
112
+ * Handle instant mutate
113
+ */
114
+ const handleMutateInstant = useCallback(
115
+ (value: MaybeValue<TValue, Required>) => {
116
+ // Permission guard
117
+ if (!isAllowed) {
118
+ toast.error("Je hebt geen toestemming om dit te bewerken");
119
+ return;
120
+ }
121
+
122
+ // If the value is the same as the initial value, do nothing
123
+ if (value === props.initialValue) return;
124
+
125
+ // Update the value
126
+ return mutation.mutate({ id: context.id, value });
127
+ },
128
+ [isAllowed, props.initialValue, mutation, context.id],
129
+ );
130
+
131
+ /**
132
+ * Handle mutate with debounce
48
133
  */
49
- const handleUpdate = (value: TValue) => {
50
- if (!isAllowed) {
51
- toast.error("Je hebt geen toestemming om dit te bewerken");
134
+ const handleMutateDebounced = useDebouncedCallback(handleMutateInstant, delay);
135
+
136
+ /**
137
+ * Handle change updates
138
+ */
139
+ function handleChange(next: MaybeValue<TValue, Required> | null) {
140
+ // Set value in state
141
+ setValue(next ?? undefined);
142
+
143
+ // If the strategy is not change, exit
144
+ if (strategy.mode !== "change") return;
145
+
146
+ // If it is required and the value is null or empty, reset to initial value
147
+ if (props.isRequired && next === null) {
148
+ setValue(props.initialValue ?? undefined);
149
+ setStatus("error");
150
+ toast.error("Dit veld is verplicht");
52
151
  return;
53
152
  }
54
153
 
55
- // If the value is the same as the initial value, do nothing
56
- if (value === props.initialValue) return;
154
+ // If the strategy is change, update the value after the delay
155
+ handleMutateDebounced(next as MaybeValue<TValue, Required>);
156
+ }
157
+
158
+ /**
159
+ * Handle blur updates
160
+ */
161
+ function handleBlur(next: MaybeValue<TValue, Required> | null) {
162
+ // Set value in state
163
+ setValue(next ?? undefined);
164
+
165
+ // If the strategy is not blur, exit
166
+ if (strategy.mode !== "blur") return;
167
+
168
+ // If it is required and the value is null or empty, reset to initial value
169
+ if (props.isRequired && next === null) {
170
+ setValue(props.initialValue ?? undefined);
171
+ setStatus("error");
172
+ toast.error("Dit veld is verplicht");
173
+ return;
174
+ }
57
175
 
58
- return mutate({ id: context.id, value });
59
- };
176
+ // If the strategy is blur, update the value
177
+ handleMutateInstant(next as MaybeValue<TValue, Required>);
178
+ }
60
179
 
61
- return { value, setValue, handleUpdate, status, isAllowed };
180
+ return { value, setValue, handleChange, handleBlur, status, isAllowed };
62
181
  }
@@ -7,9 +7,16 @@ export const inlineEditVariants = cva("", {
7
7
  table:
8
8
  "min-h-9 px-3 py-1 text-base md:text-sm rounded-none h-full w-full border-none outline-none ring-0 focus:border-0 focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0",
9
9
  },
10
+ status: {
11
+ idle: "",
12
+ pending: "opacity-70 animate-pulse",
13
+ success: "",
14
+ error: "border-destructive text-destructive",
15
+ },
10
16
  },
11
17
  defaultVariants: {
12
18
  variant: "default",
19
+ // status: "idle",
13
20
  },
14
21
  });
15
22
 
@@ -156,7 +156,7 @@ type ImageGridItemProps = {
156
156
  };
157
157
 
158
158
  function ImageGridItem({ node, index, setCurrent }: ImageGridItemProps) {
159
- const { deleteMutation } = useUploadZone();
159
+ const { uploadClient } = useUploadZone();
160
160
 
161
161
  const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: node.id });
162
162
 
@@ -198,7 +198,7 @@ function ImageGridItem({ node, index, setCurrent }: ImageGridItemProps) {
198
198
  type="button"
199
199
  variant="destructive"
200
200
  size="icon"
201
- onClick={() => deleteMutation({ ids: [node.id] })}
201
+ onClick={() => uploadClient.deleteFiles([node.id])}
202
202
  className="absolute left-2 top-2 hidden p-1 group-hover:flex"
203
203
  >
204
204
  <Icons.trash className="size-4" />
@@ -1,15 +1,14 @@
1
1
  "use client";
2
2
 
3
3
  import React from "react";
4
- import { FileNode, Node, PresignFileSchema } from "../lib/validators";
4
+ import { UploadClient } from "../lib/create-upload.client";
5
+ import { FileNode, Node } from "../lib/validators";
5
6
 
6
7
  /**
7
8
  * Upload Zone Context Value
8
9
  */
9
10
  export type UploadZoneContextValue = {
10
- presignMutation: (params: PresignFileSchema) => Promise<{ id: string; presignedUrl: string }>;
11
- confirmMutation: (params: { id: string }) => Promise<Node>;
12
- deleteMutation: (params: { ids: string[] }) => Promise<unknown>;
11
+ uploadClient: UploadClient;
13
12
  updateData: (newValue: FileNode[] | ((oldValue: FileNode[]) => FileNode[])) => Promise<void>;
14
13
  invalidateQuery: () => Promise<void> | void;
15
14
  onUpload: (file: File) => Promise<Node>;
@@ -4,45 +4,28 @@ import { Input } from "@/components/ui/input";
4
4
  import { toast } from "@/components/ui/sonner.client";
5
5
  import { cn } from "@/lib/entry";
6
6
  import { useAction } from "@/lib/hooks/use-action";
7
- import { generateDefaultUUID } from "@/modules/config/entry";
8
7
  import { useQueryClient } from "@tanstack/react-query";
9
8
  import { ComponentProps, useCallback, useState } from "react";
10
- import { FileNode, Node, PresignFileSchema, UploadFileSchema } from "../lib/validators";
9
+ import { UploadClient, UploadFileRequest, UploadHooks } from "../lib/create-upload.client";
10
+ import { FileNode, Node, UploadFileSchema } from "../lib/validators";
11
11
  import { UploadZoneContext } from "./upload-zone-context.client";
12
12
 
13
13
  export type UploadZoneProps = ComponentProps<"div"> & {
14
- namespace: string;
15
- parentId: string | null;
16
- options?: Pick<UploadFileSchema, "readonly" | "hidden" | "mode">;
17
-
18
14
  queryKey: readonly unknown[];
19
15
 
20
- presignAction: (params: PresignFileSchema) => Promise<{ id: string; presignedUrl: string }>;
21
- onPresignStarted?: (file: PresignFileSchema) => Promise<void> | void;
22
- onPresignCompleted?: (data: { id: string; presignedUrl: string }) => Promise<void> | void;
23
-
24
- confirmAction: (params: { id: string }) => Promise<Node>;
25
- onConfirmStarted?: (id: string) => Promise<void> | void;
26
- onConfirmCompleted?: (node: Node) => Promise<void> | void;
27
-
28
- deleteAction: (params: { ids: string[] }) => Promise<unknown>;
16
+ variables: Pick<UploadFileSchema, "namespace" | "parentId" | "mode" | "hidden" | "readonly">;
17
+ uploadClient: UploadClient;
18
+ uploadHooks?: UploadHooks;
29
19
 
30
20
  onUploadCompleted?: (node: Node) => Promise<void> | void;
31
21
  onUploadFailed?: (error: unknown) => Promise<void> | void;
32
22
  };
33
23
 
34
24
  export function UploadZone({
35
- namespace,
36
- parentId,
37
- options,
25
+ variables,
38
26
  queryKey,
39
- presignAction,
40
- confirmAction,
41
- deleteAction,
42
- onPresignStarted,
43
- onPresignCompleted,
44
- onConfirmStarted,
45
- onConfirmCompleted,
27
+ uploadClient,
28
+ uploadHooks,
46
29
  onUploadCompleted,
47
30
  onUploadFailed,
48
31
  children,
@@ -76,7 +59,7 @@ export function UploadZone({
76
59
  * Delete mutation
77
60
  */
78
61
  const deleteMutation = useAction({
79
- mutationFn: deleteAction,
62
+ mutationFn: async (params: { ids: string[] }) => uploadClient.deleteFiles(params.ids),
80
63
  onMutate: async (variables) => {
81
64
  await updateData((prev) => prev.filter((node) => !variables.ids.includes(node.id)));
82
65
  },
@@ -90,15 +73,10 @@ export function UploadZone({
90
73
  });
91
74
 
92
75
  /**
93
- * Presign upload mutation
76
+ * Upload mutation
94
77
  */
95
- const presignMutation = useAction({
96
- mutationFn: async (params: PresignFileSchema) => {
97
- await onPresignStarted?.(params);
98
- const data = await presignAction(params);
99
- await onPresignCompleted?.(data);
100
- return data;
101
- },
78
+ const uploadMutation = useAction({
79
+ mutationFn: async (params: UploadFileRequest) => uploadClient.upload(params, uploadHooks),
102
80
  onMutate: async (variables) => {
103
81
  // Generate a new node
104
82
  const newNode = {
@@ -109,61 +87,6 @@ export function UploadZone({
109
87
 
110
88
  await updateData((prev) => [...(prev ?? []), newNode]);
111
89
  },
112
- onError: async (error, variables) => {
113
- await onUploadFailed?.(error);
114
-
115
- console.error("Presign error: ", error);
116
- toast.error("Bestand uploaden mislukt", {
117
- description: error instanceof Error ? error.message : undefined,
118
- });
119
-
120
- console.info("Deleting failed upload");
121
- await deleteMutation.mutateAsync({ ids: [variables.id] });
122
- },
123
- });
124
-
125
- /**
126
- * Upload mutation
127
- */
128
- const uploadMutation = useAction({
129
- mutationFn: async (input: { id: string; presignedUrl: string; file: File }) => {
130
- /**
131
- * Upload the file
132
- */
133
- const uploadResponse = await fetch(input.presignedUrl, {
134
- method: "PUT",
135
- headers: { "Content-Type": input.file.type },
136
- body: input.file,
137
- });
138
-
139
- if (!uploadResponse.ok) {
140
- const message = await uploadResponse.text();
141
- throw new Error(`Upload failed: ${message}`);
142
- }
143
- },
144
- onError: async (error, variables) => {
145
- await onUploadFailed?.(error);
146
-
147
- console.error("Upload error: ", error);
148
- toast.error("Bestand uploaden mislukt", {
149
- description: error instanceof Error ? error.message : undefined,
150
- });
151
-
152
- console.info("Deleting failed upload");
153
- await deleteMutation.mutateAsync({ ids: [variables.id] });
154
- },
155
- });
156
-
157
- /**
158
- * Confirm upload mutation
159
- */
160
- const confirmMutation = useAction({
161
- mutationFn: async (params: { id: string }) => {
162
- await onConfirmStarted?.(params.id);
163
- const data = await confirmAction(params);
164
- await onConfirmCompleted?.(data);
165
- return data;
166
- },
167
90
  onSuccess: async (data) => {
168
91
  await onUploadCompleted?.(data);
169
92
  toast.success(`Succesvol geupload: ${data.name}`);
@@ -171,7 +94,7 @@ export function UploadZone({
171
94
  onError: async (error, variables) => {
172
95
  await onUploadFailed?.(error);
173
96
 
174
- console.error("Confirm error: ", error);
97
+ console.error("Upload error: ", error);
175
98
  toast.error("Bestand uploaden mislukt", {
176
99
  description: error instanceof Error ? error.message : undefined,
177
100
  });
@@ -189,30 +112,10 @@ export function UploadZone({
189
112
  */
190
113
  const onUpload = useCallback(
191
114
  async (file: File) => {
192
- /**
193
- * Send the presign request
194
- */
195
- const { id, presignedUrl } = await presignMutation.mutateAsync({
196
- ...options,
197
- id: generateDefaultUUID(),
198
- name: file.name,
199
- size: file.size,
200
- contentType: file.type,
201
- namespace,
202
- parentId,
203
- });
204
-
205
- /**
206
- * Uploading the file
207
- */
208
- await uploadMutation.mutateAsync({ id, presignedUrl, file });
209
-
210
- /**
211
- * Confirming the upload
212
- */
213
- return await confirmMutation.mutateAsync({ id });
115
+ const req = uploadClient.prepareUpload({ ...variables, file });
116
+ return await uploadMutation.mutateAsync(req);
214
117
  },
215
- [presignMutation, options, namespace, parentId, uploadMutation, confirmMutation],
118
+ [uploadClient, variables, uploadMutation],
216
119
  );
217
120
 
218
121
  return (
@@ -246,9 +149,7 @@ export function UploadZone({
246
149
  )}
247
150
  <UploadZoneContext
248
151
  value={{
249
- presignMutation: (params) => presignMutation.mutateAsync(params),
250
- confirmMutation: (params) => confirmMutation.mutateAsync(params),
251
- deleteMutation: (params) => deleteMutation.mutateAsync(params),
152
+ uploadClient,
252
153
  onUpload,
253
154
  updateData,
254
155
  invalidateQuery,
@@ -7,5 +7,6 @@ export const nodesTableFilters = createTableFilters({
7
7
  types: parseFilterArray(z.array(z.enum(nodeTypeEnum.enumValues))),
8
8
  nodeIds: parseFilterArray(z.array(z.string())),
9
9
  isDeleted: parseFilterBoolean(z.boolean()),
10
+ isOrphaned: parseFilterBoolean(z.boolean()),
10
11
  hidden: parseFilterBoolean(z.boolean()),
11
12
  });
@@ -8,5 +8,6 @@ export * from "./components/upload-zone-context.client";
8
8
  export * from "./components/upload-zone.client";
9
9
 
10
10
  /**
11
- * Hooks
11
+ * Lib
12
12
  */
13
+ export * from "./lib/create-upload.client";
@@ -0,0 +1,134 @@
1
+ import { generateDefaultUUID } from "@/modules/config/entry";
2
+ import { BulkActionSchema } from "@/modules/router/entry";
3
+ import { PresignFileSchema, SelectNodeSchema, UpdateNodeSchema } from "./validators";
4
+
5
+ /**
6
+ * Upload request
7
+ */
8
+ export type PrepareUploadInput = Omit<UploadFileRequest, "id" | "name" | "size" | "contentType">;
9
+ export type UploadFileRequest = PresignFileSchema & { file: File };
10
+ export type UploadHooks = {
11
+ beforePresign?: (input: UploadFileRequest) => Promise<void> | void;
12
+ afterPresign?: (presignResult: { id: string; presignedUrl: string }) => Promise<void> | void;
13
+ beforeConfirm?: (presignResult: { id: string; presignedUrl: string }) => Promise<void> | void;
14
+ afterConfirm?: (node: SelectNodeSchema) => Promise<void> | void;
15
+ };
16
+
17
+ /**
18
+ * Upload client
19
+ */
20
+ export type UploadClient = {
21
+ prepareUpload: (input: PrepareUploadInput) => UploadFileRequest;
22
+ upload: (input: UploadFileRequest, hooks?: UploadHooks) => Promise<SelectNodeSchema>;
23
+ deleteFiles: (ids: string[]) => Promise<void>;
24
+ updateNode: (id: string, data: UpdateNodeSchema) => Promise<SelectNodeSchema>;
25
+ };
26
+
27
+ /**
28
+ * Create upload client
29
+ */
30
+ type CreateUploadClientProps = {
31
+ endpoints: {
32
+ presign: (input: PresignFileSchema) => Promise<{ id: string; presignedUrl: string }>;
33
+ confirm: (input: { id: string }) => Promise<SelectNodeSchema>;
34
+ deleteNodes: (input: BulkActionSchema) => Promise<void>;
35
+ updateNode: (input: { id: string; data: UpdateNodeSchema }) => Promise<SelectNodeSchema>;
36
+ };
37
+ };
38
+
39
+ export function createUploadClient(props: CreateUploadClientProps): UploadClient {
40
+ /**
41
+ * Create input schema for the upload method
42
+ * @param {PrepareUploadInput} input
43
+ * @returns {UploadFileRequest}
44
+ */
45
+ function prepareUpload(input: PrepareUploadInput): UploadFileRequest {
46
+ return {
47
+ ...input,
48
+ id: generateDefaultUUID(),
49
+ name: input.file.name,
50
+ size: input.file.size,
51
+ contentType: input.file.type,
52
+ };
53
+ }
54
+
55
+ /**
56
+ * Upload file to the server
57
+ * @param {UploadFileRequest} input
58
+ * @returns {Promise<SelectNodeSchema>}
59
+ */
60
+ async function upload(input: UploadFileRequest, hooks?: UploadHooks): Promise<SelectNodeSchema> {
61
+ try {
62
+ /**
63
+ * Presign
64
+ */
65
+ await hooks?.beforePresign?.(input);
66
+ const presignResult = await props.endpoints.presign(input);
67
+ await hooks?.afterPresign?.(presignResult);
68
+ /**
69
+ * Upload the file
70
+ */
71
+ const uploadResponse = await fetch(presignResult.presignedUrl, {
72
+ method: "PUT",
73
+ headers: { "Content-Type": input.file.type },
74
+ body: input.file,
75
+ });
76
+
77
+ if (!uploadResponse.ok) {
78
+ const message = await uploadResponse.text();
79
+ throw new Error(`Upload failed: ${message}`);
80
+ }
81
+
82
+ /**
83
+ * Confirm
84
+ */
85
+ await hooks?.beforeConfirm?.(presignResult);
86
+ const node = await props.endpoints.confirm({ id: presignResult.id });
87
+ await hooks?.afterConfirm?.(node);
88
+ /**
89
+ * Return
90
+ */
91
+ return node;
92
+ } catch (err) {
93
+ console.error("Upload error: ", err);
94
+
95
+ /**
96
+ * Delete if upload failed
97
+ * */
98
+ try {
99
+ await deleteFiles([input.id]);
100
+ } catch (cleanupErr) {
101
+ console.error("Cleanup delete failed:", cleanupErr);
102
+ }
103
+
104
+ throw err;
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Delete files
110
+ * @param {string[]} ids
111
+ */
112
+ async function deleteFiles(ids: string[]) {
113
+ await props.endpoints.deleteNodes({ ids });
114
+ }
115
+
116
+ /**
117
+ * Update node
118
+ * @param {string} id
119
+ * @param {UpdateNodeSchema} data
120
+ */
121
+ async function updateNode(id: string, data: UpdateNodeSchema) {
122
+ return props.endpoints.updateNode({ id, data });
123
+ }
124
+
125
+ /**
126
+ * Upload client
127
+ */
128
+ return {
129
+ prepareUpload,
130
+ upload,
131
+ deleteFiles,
132
+ updateNode,
133
+ };
134
+ }
@@ -100,6 +100,7 @@ export function createDriveBaseProcedures() {
100
100
  */
101
101
  presign: protectedProcedure
102
102
  .input(presignFileSchema)
103
+ .output(z.object({ id: z.string(), presignedUrl: z.string() }))
103
104
  .handler(async ({ context, input }) => context.storage.presignUpload(input)),
104
105
  /**
105
106
  * Confirm the upload
@@ -25,6 +25,7 @@ export const nodes = pgTable("nodes", (t) => ({
25
25
  createdBy: t.uuid().references(() => users.id, { onDelete: "set null" }),
26
26
  isPending: t.boolean().default(false).notNull(),
27
27
  isDeleted: t.boolean().default(false).notNull(),
28
+ orphanedAt: t.timestamp(),
28
29
  parentId: t.uuid().references((): AnyPgColumn => nodes.id, { onDelete: "cascade" }),
29
30
  }));
30
31
 
@@ -16,13 +16,14 @@ import {
16
16
  } from "@aws-sdk/client-s3";
17
17
  import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
18
18
  import { addSeconds } from "date-fns";
19
- import { and, asc, eq, inArray, isNull, SQL } from "drizzle-orm";
19
+ import { and, asc, eq, inArray, isNotNull, isNull, SQL } from "drizzle-orm";
20
20
  import { after } from "next/server";
21
21
  import { deviceSizes } from "./constants";
22
22
  import { getDriveBucketKey, inferNodeSubtype, isFile, isFolder } from "./helpers";
23
23
  import { nodePresignedUrls, nodes, nodeVariants } from "./schema";
24
24
  import {
25
25
  CreateFolderNodeSchema,
26
+ FileNode,
26
27
  GetFileURLSchema,
27
28
  getFileURLSchemaDefaults,
28
29
  GetNodesByParentIdInput,
@@ -101,8 +102,8 @@ export class StorageService<TSchema extends TDatabaseSchema> {
101
102
  Bucket: BUCKET_NAME,
102
103
  Key: getDriveBucketKey(input.id, input.variant),
103
104
  Body: input.body,
104
- ContentType: input.contentType ?? "",
105
- ContentLength: input.size ?? 0,
105
+ ContentType: input.contentType ?? undefined,
106
+ ContentLength: input.size ?? undefined,
106
107
  Metadata: {
107
108
  nodeId: input.id,
108
109
  },
@@ -139,6 +140,11 @@ export class StorageService<TSchema extends TDatabaseSchema> {
139
140
  filters.nodeIds != null ? inArray(nodes.id, filters.nodeIds) : undefined,
140
141
  filters.types != null ? inArray(nodes.type, filters.types) : undefined,
141
142
  filters.isDeleted != null ? eq(nodes.isDeleted, filters.isDeleted) : undefined,
143
+ filters.isOrphaned === true
144
+ ? isNotNull(nodes.orphanedAt)
145
+ : filters.isOrphaned === false
146
+ ? isNull(nodes.orphanedAt)
147
+ : undefined,
142
148
  filters.hidden != null ? eq(nodes.hidden, filters.hidden) : undefined,
143
149
  filters.parentId ? eq(nodes.parentId, filters.parentId) : isNull(nodes.parentId),
144
150
  eq(nodes.namespace, filters.namespace),
@@ -313,7 +319,7 @@ export class StorageService<TSchema extends TDatabaseSchema> {
313
319
  // after(async () => {});
314
320
  await this.generatePreviews(input);
315
321
 
316
- return result;
322
+ return result as FileNode;
317
323
  }
318
324
 
319
325
  /**
@@ -62,6 +62,7 @@ export const nodesTableFiltersSchema = resolveFiltersSchema(nodesTableFilters)
62
62
  ...input,
63
63
  hidden: input?.hidden ?? false,
64
64
  isDeleted: input?.isDeleted ?? false,
65
+ orphanedAt: input?.isOrphaned ?? false,
65
66
  }));
66
67
 
67
68
  export type NodesTableFilters = z.input<typeof nodesTableFiltersSchema>;