@carlonicora/nextjs-jsonapi 1.36.1 → 1.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/dist/{BlockNoteEditor-4MDHRUS2.js → BlockNoteEditor-3S2B36O3.js} +15 -15
  2. package/dist/{BlockNoteEditor-4MDHRUS2.js.map → BlockNoteEditor-3S2B36O3.js.map} +1 -1
  3. package/dist/{BlockNoteEditor-SZWO3MDO.mjs → BlockNoteEditor-WQUJTVJL.mjs} +5 -5
  4. package/dist/BlockNoteEditor-WQUJTVJL.mjs.map +1 -0
  5. package/dist/billing/index.d.mts +15 -5
  6. package/dist/billing/index.d.ts +15 -5
  7. package/dist/billing/index.js +750 -520
  8. package/dist/billing/index.js.map +1 -1
  9. package/dist/billing/index.mjs +665 -435
  10. package/dist/billing/index.mjs.map +1 -1
  11. package/dist/{chunk-53IPQJVH.js → chunk-3EZX4G2E.js} +147 -23
  12. package/dist/chunk-3EZX4G2E.js.map +1 -0
  13. package/dist/{chunk-I7DFEJFF.mjs → chunk-4PHADEKA.mjs} +738 -1418
  14. package/dist/chunk-4PHADEKA.mjs.map +1 -0
  15. package/dist/{chunk-E6PQQTWF.js → chunk-T2JCZYWK.js} +999 -1679
  16. package/dist/chunk-T2JCZYWK.js.map +1 -0
  17. package/dist/{chunk-P7R2DPD6.mjs → chunk-TQ5GRRTM.mjs} +125 -1
  18. package/dist/chunk-TQ5GRRTM.mjs.map +1 -0
  19. package/dist/client/index.js +3 -3
  20. package/dist/client/index.mjs +2 -2
  21. package/dist/components/index.d.mts +23 -8
  22. package/dist/components/index.d.ts +23 -8
  23. package/dist/components/index.js +3 -3
  24. package/dist/components/index.mjs +2 -2
  25. package/dist/contexts/index.d.mts +1 -1
  26. package/dist/contexts/index.d.ts +1 -1
  27. package/dist/contexts/index.js +3 -3
  28. package/dist/contexts/index.mjs +2 -2
  29. package/dist/core/index.d.mts +47 -3
  30. package/dist/core/index.d.ts +47 -3
  31. package/dist/core/index.js +8 -2
  32. package/dist/core/index.js.map +1 -1
  33. package/dist/core/index.mjs +7 -1
  34. package/dist/index.d.mts +2 -2
  35. package/dist/index.d.ts +2 -2
  36. package/dist/index.js +8 -2
  37. package/dist/index.js.map +1 -1
  38. package/dist/index.mjs +7 -1
  39. package/dist/server/index.js +3 -3
  40. package/dist/server/index.mjs +1 -1
  41. package/dist/{stripe-subscription.interface-DK7BJaNd.d.ts → stripe-promotion-code.interface-BcJty0rv.d.ts} +18 -1
  42. package/dist/{stripe-subscription.interface-C8uhCYIZ.d.mts → stripe-promotion-code.interface-Dnm2DJKQ.d.mts} +18 -1
  43. package/dist/testing/index.js.map +1 -1
  44. package/dist/testing/index.mjs.map +1 -1
  45. package/package.json +2 -2
  46. package/src/billing/index.ts +1 -0
  47. package/src/client/context/JsonApiProvider.tsx +1 -5
  48. package/src/client/hooks/__tests__/useJsonApiGet.test.tsx +9 -9
  49. package/src/client/hooks/__tests__/useJsonApiMutation.test.tsx +11 -11
  50. package/src/client/hooks/__tests__/useRehydration.test.ts +13 -34
  51. package/src/components/editors/BlockNoteEditor.tsx +2 -2
  52. package/src/components/forms/CommonEditorTrigger.tsx +1 -1
  53. package/src/components/forms/FormCheckbox.tsx +2 -12
  54. package/src/components/forms/FormDate.tsx +1 -6
  55. package/src/components/forms/FormInput.tsx +1 -1
  56. package/src/components/forms/FormPassword.tsx +1 -7
  57. package/src/components/forms/FormSelect.tsx +2 -8
  58. package/src/components/forms/FormSlider.tsx +1 -5
  59. package/src/components/forms/FormSwitch.tsx +1 -5
  60. package/src/components/forms/GdprConsentCheckbox.tsx +2 -8
  61. package/src/components/forms/PasswordInput.tsx +28 -26
  62. package/src/components/forms/__tests__/FormCheckbox.test.tsx +16 -18
  63. package/src/components/forms/__tests__/FormDate.test.tsx +14 -30
  64. package/src/components/forms/__tests__/FormInput.test.tsx +21 -37
  65. package/src/components/forms/__tests__/FormSelect.test.tsx +15 -21
  66. package/src/components/tables/ContentListTable.tsx +1 -1
  67. package/src/components/tables/__tests__/ContentListTable.test.tsx +17 -89
  68. package/src/components/tables/cells/cell.component.tsx +1 -1
  69. package/src/contexts/HeaderChildrenContext.tsx +3 -1
  70. package/src/core/endpoint/__tests__/EndpointCreator.test.ts +2 -7
  71. package/src/core/factories/__tests__/JsonApiDataFactory.test.ts +3 -3
  72. package/src/core/factories/__tests__/RehydrationFactory.test.ts +4 -6
  73. package/src/core/index.ts +1 -0
  74. package/src/core/registry/ModuleRegistry.ts +1 -0
  75. package/src/core/registry/__tests__/DataClassRegistry.test.ts +5 -15
  76. package/src/core/registry/__tests__/ModuleRegistrar.test.ts +5 -15
  77. package/src/features/auth/components/GdprConsentSection.tsx +1 -6
  78. package/src/features/auth/components/details/LandingComponent.tsx +6 -1
  79. package/src/features/auth/components/forms/AcceptInvitation.tsx +1 -1
  80. package/src/features/auth/components/forms/ResetPassword.tsx +1 -1
  81. package/src/features/billing/components/cards/PaymentMethodSummaryCard.tsx +13 -18
  82. package/src/features/billing/components/cards/SubscriptionSummaryCard.tsx +12 -17
  83. package/src/features/billing/components/modals/BillingDetailModal.tsx +2 -13
  84. package/src/features/billing/stripe-customer/components/details/PaymentMethodCard.tsx +8 -1
  85. package/src/features/billing/stripe-customer/components/forms/PaymentMethodEditor.tsx +2 -13
  86. package/src/features/billing/stripe-customer/components/forms/PaymentMethodForm.tsx +2 -12
  87. package/src/features/billing/stripe-invoice/components/details/InvoiceDetails.tsx +6 -1
  88. package/src/features/billing/stripe-invoice/data/stripe-invoice.interface.ts +1 -0
  89. package/src/features/billing/stripe-price/components/lists/PricesList.tsx +13 -5
  90. package/src/features/billing/stripe-product/components/lists/ProductsList.tsx +5 -5
  91. package/src/features/billing/stripe-promotion-code/components/PromoCodeInput.tsx +108 -0
  92. package/src/features/billing/stripe-promotion-code/components/index.ts +1 -0
  93. package/src/features/billing/stripe-promotion-code/data/index.ts +3 -0
  94. package/src/features/billing/stripe-promotion-code/data/stripe-promotion-code.interface.ts +14 -0
  95. package/src/features/billing/stripe-promotion-code/data/stripe-promotion-code.service.ts +64 -0
  96. package/src/features/billing/stripe-promotion-code/data/stripe-promotion-code.ts +66 -0
  97. package/src/features/billing/stripe-promotion-code/index.ts +2 -0
  98. package/src/features/billing/stripe-promotion-code/stripe-promotion-code.module.ts +9 -0
  99. package/src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx +1 -3
  100. package/src/features/billing/stripe-subscription/components/details/SubscriptionDetails.tsx +4 -1
  101. package/src/features/billing/stripe-subscription/components/forms/CancelSubscriptionDialog.tsx +1 -1
  102. package/src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx +24 -4
  103. package/src/features/billing/stripe-subscription/components/widgets/PricingCard.tsx +9 -2
  104. package/src/features/billing/stripe-subscription/components/widgets/SubscriptionStatusBadge.tsx +3 -1
  105. package/src/features/billing/stripe-subscription/components/wizards/SubscriptionWizard.tsx +7 -7
  106. package/src/features/billing/stripe-subscription/components/wizards/WizardProgressIndicator.tsx +2 -10
  107. package/src/features/billing/stripe-subscription/components/wizards/WizardStepPaymentMethod.tsx +3 -13
  108. package/src/features/billing/stripe-subscription/components/wizards/WizardStepReview.tsx +134 -23
  109. package/src/features/billing/stripe-subscription/data/stripe-subscription.interface.ts +2 -0
  110. package/src/features/billing/stripe-subscription/data/stripe-subscription.ts +8 -0
  111. package/src/features/billing/stripe-subscription/hooks/useSubscriptionWizard.ts +93 -7
  112. package/src/features/billing/stripe-usage/components/details/UsageSummaryCard.tsx +1 -1
  113. package/src/features/billing/stripe-usage/components/lists/UsageHistoryTable.tsx +1 -1
  114. package/src/features/company/components/details/CompanyDetails.tsx +2 -2
  115. package/src/features/company/components/forms/CompanyConfigurationSecurityForm.tsx +1 -1
  116. package/src/features/index.ts +1 -0
  117. package/src/features/notification/components/containers/NotificationsListContainer.tsx +1 -1
  118. package/src/features/notification/components/modals/NotificationModal.tsx +6 -2
  119. package/src/features/notification/contexts/NotificationContext.tsx +1 -3
  120. package/src/features/oauth/components/OAuthClientCard.tsx +15 -17
  121. package/src/features/oauth/components/OAuthClientDetail.tsx +7 -19
  122. package/src/features/oauth/components/OAuthClientForm.tsx +4 -13
  123. package/src/features/oauth/components/OAuthClientSecretDisplay.tsx +4 -20
  124. package/src/features/oauth/components/OAuthRedirectUriInput.tsx +5 -12
  125. package/src/features/oauth/components/OAuthScopeSelector.tsx +17 -23
  126. package/src/features/oauth/components/consent/OAuthConsentActions.tsx +3 -16
  127. package/src/features/oauth/components/consent/OAuthConsentHeader.tsx +3 -12
  128. package/src/features/oauth/components/consent/OAuthConsentScreen.tsx +5 -20
  129. package/src/features/oauth/components/consent/OAuthScopeList.tsx +3 -18
  130. package/src/features/onboarding/contexts/OnboardingContext.tsx +3 -3
  131. package/src/features/role/components/forms/FormRoles.tsx +1 -7
  132. package/src/features/user/components/containers/UserContainer.tsx +1 -1
  133. package/src/features/user/components/details/UserDetails.tsx +1 -1
  134. package/src/features/user/components/forms/UserDeleter.tsx +1 -1
  135. package/src/features/user/components/forms/UserEditor.tsx +1 -1
  136. package/src/features/user/components/forms/UserMultiSelect.tsx +7 -7
  137. package/src/features/user/components/lists/UserListInAdd.tsx +2 -2
  138. package/src/features/user/components/lists/UsersList.tsx +7 -1
  139. package/src/features/user/contexts/CurrentUserContext.tsx +36 -33
  140. package/src/hooks/__tests__/useDataListRetriever.test.ts +15 -21
  141. package/src/hooks/__tests__/useDebounce.test.ts +2 -7
  142. package/src/hooks/useCustomD3Graph.tsx +2 -2
  143. package/src/shadcnui/custom/multi-select.tsx +28 -2
  144. package/src/shadcnui/ui/accordion.tsx +21 -23
  145. package/src/shadcnui/ui/alert-dialog.tsx +45 -62
  146. package/src/shadcnui/ui/alert.tsx +25 -41
  147. package/src/shadcnui/ui/avatar.tsx +23 -36
  148. package/src/shadcnui/ui/badge.tsx +13 -11
  149. package/src/shadcnui/ui/breadcrumb.tsx +21 -55
  150. package/src/shadcnui/ui/button.tsx +17 -18
  151. package/src/shadcnui/ui/calendar.tsx +44 -93
  152. package/src/shadcnui/ui/carousel.tsx +72 -100
  153. package/src/shadcnui/ui/chart.tsx +102 -161
  154. package/src/shadcnui/ui/checkbox.tsx +8 -9
  155. package/src/shadcnui/ui/combobox.tsx +52 -83
  156. package/src/shadcnui/ui/command.tsx +43 -77
  157. package/src/shadcnui/ui/context-menu.tsx +47 -86
  158. package/src/shadcnui/ui/dialog.tsx +34 -60
  159. package/src/shadcnui/ui/drawer.tsx +32 -53
  160. package/src/shadcnui/ui/dropdown-menu.tsx +48 -65
  161. package/src/shadcnui/ui/field.tsx +39 -48
  162. package/src/shadcnui/ui/hover-card.tsx +9 -14
  163. package/src/shadcnui/ui/input-group.tsx +44 -55
  164. package/src/shadcnui/ui/input-otp.tsx +22 -26
  165. package/src/shadcnui/ui/input.tsx +6 -6
  166. package/src/shadcnui/ui/label.tsx +6 -6
  167. package/src/shadcnui/ui/navigation-menu.tsx +36 -60
  168. package/src/shadcnui/ui/popover.tsx +15 -38
  169. package/src/shadcnui/ui/progress.tsx +12 -29
  170. package/src/shadcnui/ui/radio-group.tsx +9 -15
  171. package/src/shadcnui/ui/resizable.tsx +14 -24
  172. package/src/shadcnui/ui/scroll-area.tsx +12 -27
  173. package/src/shadcnui/ui/select.tsx +41 -65
  174. package/src/shadcnui/ui/separator.tsx +7 -11
  175. package/src/shadcnui/ui/sheet.tsx +30 -55
  176. package/src/shadcnui/ui/sidebar.tsx +141 -189
  177. package/src/shadcnui/ui/skeleton.tsx +3 -9
  178. package/src/shadcnui/ui/slider.tsx +11 -23
  179. package/src/shadcnui/ui/switch.tsx +8 -8
  180. package/src/shadcnui/ui/tabs.tsx +14 -21
  181. package/src/shadcnui/ui/textarea.tsx +5 -5
  182. package/src/shadcnui/ui/toggle.tsx +8 -14
  183. package/src/shadcnui/ui/tooltip.tsx +11 -23
  184. package/src/testing/providers/MockJsonApiProvider.tsx +1 -5
  185. package/src/testing/utils/renderWithProviders.tsx +6 -10
  186. package/dist/BlockNoteEditor-SZWO3MDO.mjs.map +0 -1
  187. package/dist/chunk-53IPQJVH.js.map +0 -1
  188. package/dist/chunk-E6PQQTWF.js.map +0 -1
  189. package/dist/chunk-I7DFEJFF.mjs.map +0 -1
  190. package/dist/chunk-P7R2DPD6.mjs.map +0 -1
@@ -51,7 +51,7 @@ export const CurrentUserProvider = ({ children }: { children: React.ReactNode })
51
51
  if (!token && dehydratedUser) setDehydratedUser(null);
52
52
  }, [dehydratedUser, setDehydratedUser]);
53
53
 
54
- const matchUrlToModule = (params?: { path: string }): ModuleWithPermissions | undefined => {
54
+ const matchUrlToModule = (_params?: { path: string }): ModuleWithPermissions | undefined => {
55
55
  const moduleKeys = Object.getOwnPropertyNames(Modules).filter(
56
56
  (key) => key !== "prototype" && key !== "_factory" && key !== "length" && key !== "name",
57
57
  );
@@ -138,41 +138,44 @@ export const CurrentUserProvider = ({ children }: { children: React.ReactNode })
138
138
  // Function to refresh user data from the API
139
139
  // skipCookieUpdate: When true, only updates React state without calling the Server Action
140
140
  // This prevents page reloads when refresh is triggered by WebSocket events
141
- const refreshUser = useCallback(async (options?: { skipCookieUpdate?: boolean }): Promise<void> => {
142
- if (isRefreshing) {
143
- return;
144
- }
141
+ const refreshUser = useCallback(
142
+ async (options?: { skipCookieUpdate?: boolean }): Promise<void> => {
143
+ if (isRefreshing) {
144
+ return;
145
+ }
145
146
 
146
- setIsRefreshing(true);
147
- try {
148
- const fullUser = await UserService.findFullUser();
149
- if (fullUser) {
150
- const dehydrated = fullUser.dehydrate();
151
-
152
- setDehydratedUser(dehydrated as any);
153
- setUser(fullUser);
154
-
155
- // Update authentication cookies with fresh user data
156
- // Skip when triggered by WebSocket to prevent page reload (Server Actions modify cookies)
157
- if (!options?.skipCookieUpdate) {
158
- await getTokenHandler()?.updateToken({
159
- userId: fullUser.id,
160
- companyId: fullUser.company?.id,
161
- roles: fullUser.roles.map((role) => role.id),
162
- features: fullUser.company?.features?.map((feature) => feature.id) ?? [],
163
- modules: fullUser.modules.map((module) => ({
164
- id: module.id,
165
- permissions: module.permissions,
166
- })),
167
- });
147
+ setIsRefreshing(true);
148
+ try {
149
+ const fullUser = await UserService.findFullUser();
150
+ if (fullUser) {
151
+ const dehydrated = fullUser.dehydrate();
152
+
153
+ setDehydratedUser(dehydrated as any);
154
+ setUser(fullUser);
155
+
156
+ // Update authentication cookies with fresh user data
157
+ // Skip when triggered by WebSocket to prevent page reload (Server Actions modify cookies)
158
+ if (!options?.skipCookieUpdate) {
159
+ await getTokenHandler()?.updateToken({
160
+ userId: fullUser.id,
161
+ companyId: fullUser.company?.id,
162
+ roles: fullUser.roles.map((role) => role.id),
163
+ features: fullUser.company?.features?.map((feature) => feature.id) ?? [],
164
+ modules: fullUser.modules.map((module) => ({
165
+ id: module.id,
166
+ permissions: module.permissions,
167
+ })),
168
+ });
169
+ }
168
170
  }
171
+ } catch (error) {
172
+ console.error("Failed to refresh user data:", error);
173
+ } finally {
174
+ setIsRefreshing(false);
169
175
  }
170
- } catch (error) {
171
- console.error("Failed to refresh user data:", error);
172
- } finally {
173
- setIsRefreshing(false);
174
- }
175
- }, [isRefreshing, setDehydratedUser]);
176
+ },
177
+ [isRefreshing, setDehydratedUser],
178
+ );
176
179
 
177
180
  // WebSocket integration for real-time token updates
178
181
  const { socket, isConnected } = useSocketContext();
@@ -22,7 +22,7 @@ describe("useDataListRetriever", () => {
22
22
  useDataListRetriever({
23
23
  retriever,
24
24
  module: mockModule,
25
- })
25
+ }),
26
26
  );
27
27
 
28
28
  // Wait for async operations to settle
@@ -42,7 +42,7 @@ describe("useDataListRetriever", () => {
42
42
  retriever,
43
43
  module: mockModule,
44
44
  ready: false,
45
- })
45
+ }),
46
46
  );
47
47
 
48
48
  expect(result.current.ready).toBe(false);
@@ -63,7 +63,7 @@ describe("useDataListRetriever", () => {
63
63
  useDataListRetriever({
64
64
  retriever,
65
65
  module: mockModule,
66
- })
66
+ }),
67
67
  );
68
68
 
69
69
  await waitFor(() => {
@@ -82,7 +82,7 @@ describe("useDataListRetriever", () => {
82
82
  useDataListRetriever({
83
83
  retriever,
84
84
  module: mockModule,
85
- })
85
+ }),
86
86
  );
87
87
 
88
88
  await waitFor(() => {
@@ -96,16 +96,14 @@ describe("useDataListRetriever", () => {
96
96
 
97
97
  describe("search", () => {
98
98
  it("should search and call retriever with search term", async () => {
99
- const mockData = [
100
- createMockApiData({ type: "articles", id: "1", attributes: { title: "Search Result" } }),
101
- ];
99
+ const mockData = [createMockApiData({ type: "articles", id: "1", attributes: { title: "Search Result" } })];
102
100
  const retriever = vi.fn().mockResolvedValue(mockData);
103
101
 
104
102
  const { result } = renderHook(() =>
105
103
  useDataListRetriever({
106
104
  retriever,
107
105
  module: mockModule,
108
- })
106
+ }),
109
107
  );
110
108
 
111
109
  await waitFor(() => {
@@ -133,7 +131,7 @@ describe("useDataListRetriever", () => {
133
131
  useDataListRetriever({
134
132
  retriever,
135
133
  module: mockModule,
136
- })
134
+ }),
137
135
  );
138
136
 
139
137
  await waitFor(() => {
@@ -161,7 +159,7 @@ describe("useDataListRetriever", () => {
161
159
  useDataListRetriever({
162
160
  retriever,
163
161
  module: mockModule,
164
- })
162
+ }),
165
163
  );
166
164
 
167
165
  await waitFor(() => {
@@ -189,7 +187,7 @@ describe("useDataListRetriever", () => {
189
187
  retriever,
190
188
  module: mockModule,
191
189
  ready: false,
192
- })
190
+ }),
193
191
  );
194
192
 
195
193
  expect(result.current.ready).toBe(false);
@@ -209,16 +207,14 @@ describe("useDataListRetriever", () => {
209
207
 
210
208
  describe("element management", () => {
211
209
  it("should update element with setRefreshedElement", async () => {
212
- const mockData = [
213
- createMockApiData({ type: "articles", id: "1", attributes: { title: "Original" } }),
214
- ];
210
+ const mockData = [createMockApiData({ type: "articles", id: "1", attributes: { title: "Original" } })];
215
211
  const retriever = vi.fn().mockResolvedValue(mockData);
216
212
 
217
213
  const { result } = renderHook(() =>
218
214
  useDataListRetriever({
219
215
  retriever,
220
216
  module: mockModule,
221
- })
217
+ }),
222
218
  );
223
219
 
224
220
  await waitFor(() => {
@@ -250,7 +246,7 @@ describe("useDataListRetriever", () => {
250
246
  useDataListRetriever({
251
247
  retriever,
252
248
  module: mockModule,
253
- })
249
+ }),
254
250
  );
255
251
 
256
252
  await waitFor(() => {
@@ -276,7 +272,7 @@ describe("useDataListRetriever", () => {
276
272
  useDataListRetriever({
277
273
  retriever,
278
274
  module: mockModule,
279
- })
275
+ }),
280
276
  );
281
277
 
282
278
  await waitFor(() => {
@@ -288,9 +284,7 @@ describe("useDataListRetriever", () => {
288
284
  });
289
285
 
290
286
  await waitFor(() => {
291
- expect(retriever).toHaveBeenCalledWith(
292
- expect.objectContaining({ filter: "active" })
293
- );
287
+ expect(retriever).toHaveBeenCalledWith(expect.objectContaining({ filter: "active" }));
294
288
  });
295
289
  });
296
290
 
@@ -301,7 +295,7 @@ describe("useDataListRetriever", () => {
301
295
  useDataListRetriever({
302
296
  retriever,
303
297
  module: mockModule,
304
- })
298
+ }),
305
299
  );
306
300
 
307
301
  await waitFor(() => {
@@ -92,9 +92,7 @@ describe("useDebounce", () => {
92
92
 
93
93
  it("should pass multiple arguments to callback", () => {
94
94
  const callback = vi.fn();
95
- const { result } = renderHook(() =>
96
- useDebounce((a: string, b: number, c: boolean) => callback(a, b, c), 500)
97
- );
95
+ const { result } = renderHook(() => useDebounce((a: string, b: number, c: boolean) => callback(a, b, c), 500));
98
96
 
99
97
  act(() => {
100
98
  result.current("hello", 42, true);
@@ -111,10 +109,7 @@ describe("useDebounce", () => {
111
109
  const callback1 = vi.fn();
112
110
  const callback2 = vi.fn();
113
111
 
114
- const { result, rerender } = renderHook(
115
- ({ cb }) => useDebounce(cb, 500),
116
- { initialProps: { cb: callback1 } }
117
- );
112
+ const { result, rerender } = renderHook(({ cb }) => useDebounce(cb, 500), { initialProps: { cb: callback1 } });
118
113
 
119
114
  act(() => {
120
115
  result.current("test");
@@ -269,7 +269,7 @@ export function useCustomD3Graph(
269
269
  }
270
270
  });
271
271
 
272
- for (const [nodeId, node] of nodeHierarchy.entries()) {
272
+ for (const [_nodeId, node] of nodeHierarchy.entries()) {
273
273
  if (node.depth === 1 && node.angle !== undefined && node.x !== undefined && node.y !== undefined) {
274
274
  const childAngle = node.angle;
275
275
  const childX = node.x;
@@ -527,7 +527,7 @@ export function useCustomD3Graph(
527
527
  const Icon = d.icon as React.FC<{ size: number; color: string }>;
528
528
  const iconSvg = renderToStaticMarkup(<Icon size={nodeRadius / 2} color="white" />);
529
529
 
530
- const iconGroup = d3
530
+ const _iconGroup = d3
531
531
  .select(this)
532
532
  .append("g")
533
533
  .html(iconSvg)
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { cva, type VariantProps } from "class-variance-authority";
4
- import { CheckIcon, ChevronDown, WandSparkles, XCircle, XIcon } from "lucide-react";
4
+ import { CheckIcon, ChevronDown, Loader2, WandSparkles, XCircle, XIcon } from "lucide-react";
5
5
  import * as React from "react";
6
6
 
7
7
  import { cn } from "../../utils/cn";
@@ -108,6 +108,24 @@ interface MultiSelectProps
108
108
  * Optional, receives the search string.
109
109
  */
110
110
  onSearchChange?: (search: string) => void;
111
+
112
+ /**
113
+ * Whether the component is in a loading state (e.g., during search).
114
+ * Optional, defaults to false.
115
+ */
116
+ loading?: boolean;
117
+
118
+ /**
119
+ * Text to display when loading.
120
+ * Optional, defaults to "Searching...".
121
+ */
122
+ loadingText?: string;
123
+
124
+ /**
125
+ * Text to display when no results are found.
126
+ * Optional, defaults to "No results found.".
127
+ */
128
+ emptyText?: string;
111
129
  }
112
130
 
113
131
  export const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(
@@ -124,6 +142,9 @@ export const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>
124
142
  modalPopover = false,
125
143
  className,
126
144
  onSearchChange,
145
+ loading = false,
146
+ loadingText = "Searching...",
147
+ emptyText = "No results found.",
127
148
  ...props
128
149
  },
129
150
  _ref,
@@ -293,8 +314,13 @@ export const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>
293
314
  onKeyDown={handleInputKeyDown}
294
315
  onValueChange={onSearchChange}
295
316
  />
317
+ {loading && (
318
+ <div className="flex items-center justify-center py-2">
319
+ <Loader2 className="text-muted-foreground h-4 w-4 animate-spin" />
320
+ </div>
321
+ )}
296
322
  <CommandList>
297
- <CommandEmpty>No results found.</CommandEmpty>
323
+ <CommandEmpty>{loading ? loadingText : emptyText}</CommandEmpty>
298
324
  <CommandGroup>
299
325
  <CommandItem
300
326
  key="all"
@@ -1,9 +1,9 @@
1
- "use client"
1
+ "use client";
2
2
 
3
- import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion"
3
+ import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion";
4
4
 
5
- import { cn } from "@/lib/utils"
6
- import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
5
+ import { cn } from "@/lib/utils";
6
+ import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
7
7
 
8
8
  function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
9
9
  return (
@@ -12,7 +12,7 @@ function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
12
12
  className={cn("overflow-hidden rounded-md border flex w-full flex-col", className)}
13
13
  {...props}
14
14
  />
15
- )
15
+ );
16
16
  }
17
17
 
18
18
  function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) {
@@ -22,37 +22,35 @@ function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) {
22
22
  className={cn("data-open:bg-muted/50 not-last:border-b", className)}
23
23
  {...props}
24
24
  />
25
- )
25
+ );
26
26
  }
27
27
 
28
- function AccordionTrigger({
29
- className,
30
- children,
31
- ...props
32
- }: AccordionPrimitive.Trigger.Props) {
28
+ function AccordionTrigger({ className, children, ...props }: AccordionPrimitive.Trigger.Props) {
33
29
  return (
34
30
  <AccordionPrimitive.Header className="flex">
35
31
  <AccordionPrimitive.Trigger
36
32
  data-slot="accordion-trigger"
37
33
  className={cn(
38
34
  "**:data-[slot=accordion-trigger-icon]:text-muted-foreground gap-6 p-2 text-left text-xs/relaxed font-medium hover:underline **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 group/accordion-trigger relative flex flex-1 items-start justify-between border border-transparent transition-all outline-none disabled:pointer-events-none disabled:opacity-50",
39
- className
35
+ className,
40
36
  )}
41
37
  {...props}
42
38
  >
43
39
  {children}
44
- <ChevronDownIcon data-slot="accordion-trigger-icon" className="pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden" />
45
- <ChevronUpIcon data-slot="accordion-trigger-icon" className="pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline" />
40
+ <ChevronDownIcon
41
+ data-slot="accordion-trigger-icon"
42
+ className="pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden"
43
+ />
44
+ <ChevronUpIcon
45
+ data-slot="accordion-trigger-icon"
46
+ className="pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline"
47
+ />
46
48
  </AccordionPrimitive.Trigger>
47
49
  </AccordionPrimitive.Header>
48
- )
50
+ );
49
51
  }
50
52
 
51
- function AccordionContent({
52
- className,
53
- children,
54
- ...props
55
- }: AccordionPrimitive.Panel.Props) {
53
+ function AccordionContent({ className, children, ...props }: AccordionPrimitive.Panel.Props) {
56
54
  return (
57
55
  <AccordionPrimitive.Panel
58
56
  data-slot="accordion-content"
@@ -62,13 +60,13 @@ function AccordionContent({
62
60
  <div
63
61
  className={cn(
64
62
  "pt-0 pb-4 [&_a]:hover:text-foreground h-(--accordion-panel-height) data-ending-style:h-0 data-starting-style:h-0 [&_a]:underline [&_a]:underline-offset-3 [&_p:not(:last-child)]:mb-4",
65
- className
63
+ className,
66
64
  )}
67
65
  >
68
66
  {children}
69
67
  </div>
70
68
  </AccordionPrimitive.Panel>
71
- )
69
+ );
72
70
  }
73
71
 
74
- export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
72
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
@@ -1,41 +1,34 @@
1
- "use client"
1
+ "use client";
2
2
 
3
- import * as React from "react"
4
- import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog"
3
+ import * as React from "react";
4
+ import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog";
5
5
 
6
- import { cn } from "@/lib/utils"
7
- import { Button } from "@/components/ui/button"
6
+ import { cn } from "@/lib/utils";
7
+ import { Button } from "@/components/ui/button";
8
8
 
9
9
  function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
10
- return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
10
+ return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;
11
11
  }
12
12
 
13
13
  function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {
14
- return (
15
- <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
16
- )
14
+ return <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />;
17
15
  }
18
16
 
19
17
  function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {
20
- return (
21
- <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
22
- )
18
+ return <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />;
23
19
  }
24
20
 
25
- function AlertDialogOverlay({
26
- className,
27
- ...props
28
- }: AlertDialogPrimitive.Backdrop.Props) {
21
+ function AlertDialogOverlay({ className, ...props }: AlertDialogPrimitive.Backdrop.Props) {
29
22
  return (
30
23
  <AlertDialogPrimitive.Backdrop
31
24
  data-slot="alert-dialog-overlay"
32
25
  className={cn(
33
26
  "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/80 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50",
34
- className
27
+ className,
35
28
  )}
36
29
  {...props}
37
30
  />
38
- )
31
+ );
39
32
  }
40
33
 
41
34
  function AlertDialogContent({
@@ -43,7 +36,7 @@ function AlertDialogContent({
43
36
  size = "default",
44
37
  ...props
45
38
  }: AlertDialogPrimitive.Popup.Props & {
46
- size?: "default" | "sm"
39
+ size?: "default" | "sm";
47
40
  }) {
48
41
  return (
49
42
  <AlertDialogPortal>
@@ -53,67 +46,64 @@ function AlertDialogContent({
53
46
  data-size={size}
54
47
  className={cn(
55
48
  "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 bg-background ring-foreground/10 gap-3 rounded-xl p-4 ring-1 duration-100 data-[size=default]:max-w-xs data-[size=sm]:max-w-64 data-[size=default]:sm:max-w-sm group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 outline-none",
56
- className
49
+ className,
57
50
  )}
58
51
  {...props}
59
52
  />
60
53
  </AlertDialogPortal>
61
- )
54
+ );
62
55
  }
63
56
 
64
- function AlertDialogHeader({
65
- className,
66
- ...props
67
- }: React.ComponentProps<"div">) {
57
+ function AlertDialogHeader({ className, ...props }: React.ComponentProps<"div">) {
68
58
  return (
69
59
  <div
70
60
  data-slot="alert-dialog-header"
71
- className={cn("grid grid-rows-[auto_1fr] place-items-center gap-1 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]", className)}
61
+ className={cn(
62
+ "grid grid-rows-[auto_1fr] place-items-center gap-1 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
63
+ className,
64
+ )}
72
65
  {...props}
73
66
  />
74
- )
67
+ );
75
68
  }
76
69
 
77
- function AlertDialogFooter({
78
- className,
79
- ...props
80
- }: React.ComponentProps<"div">) {
70
+ function AlertDialogFooter({ className, ...props }: React.ComponentProps<"div">) {
81
71
  return (
82
72
  <div
83
73
  data-slot="alert-dialog-footer"
84
74
  className={cn(
85
75
  "flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
86
- className
76
+ className,
87
77
  )}
88
78
  {...props}
89
79
  />
90
- )
80
+ );
91
81
  }
92
82
 
93
- function AlertDialogMedia({
94
- className,
95
- ...props
96
- }: React.ComponentProps<"div">) {
83
+ function AlertDialogMedia({ className, ...props }: React.ComponentProps<"div">) {
97
84
  return (
98
85
  <div
99
86
  data-slot="alert-dialog-media"
100
- className={cn("bg-muted mb-2 inline-flex size-8 items-center justify-center rounded-md sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-4", className)}
87
+ className={cn(
88
+ "bg-muted mb-2 inline-flex size-8 items-center justify-center rounded-md sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-4",
89
+ className,
90
+ )}
101
91
  {...props}
102
92
  />
103
- )
93
+ );
104
94
  }
105
95
 
106
- function AlertDialogTitle({
107
- className,
108
- ...props
109
- }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
96
+ function AlertDialogTitle({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
110
97
  return (
111
98
  <AlertDialogPrimitive.Title
112
99
  data-slot="alert-dialog-title"
113
- className={cn("text-sm font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2", className)}
100
+ className={cn(
101
+ "text-sm font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
102
+ className,
103
+ )}
114
104
  {...props}
115
105
  />
116
- )
106
+ );
117
107
  }
118
108
 
119
109
  function AlertDialogDescription({
@@ -123,23 +113,17 @@ function AlertDialogDescription({
123
113
  return (
124
114
  <AlertDialogPrimitive.Description
125
115
  data-slot="alert-dialog-description"
126
- className={cn("text-muted-foreground *:[a]:hover:text-foreground text-xs/relaxed text-balance md:text-pretty *:[a]:underline *:[a]:underline-offset-3", className)}
116
+ className={cn(
117
+ "text-muted-foreground *:[a]:hover:text-foreground text-xs/relaxed text-balance md:text-pretty *:[a]:underline *:[a]:underline-offset-3",
118
+ className,
119
+ )}
127
120
  {...props}
128
121
  />
129
- )
122
+ );
130
123
  }
131
124
 
132
- function AlertDialogAction({
133
- className,
134
- ...props
135
- }: React.ComponentProps<typeof Button>) {
136
- return (
137
- <Button
138
- data-slot="alert-dialog-action"
139
- className={cn(className)}
140
- {...props}
141
- />
142
- )
125
+ function AlertDialogAction({ className, ...props }: React.ComponentProps<typeof Button>) {
126
+ return <Button data-slot="alert-dialog-action" className={cn(className)} {...props} />;
143
127
  }
144
128
 
145
129
  function AlertDialogCancel({
@@ -147,8 +131,7 @@ function AlertDialogCancel({
147
131
  variant = "outline",
148
132
  size = "default",
149
133
  ...props
150
- }: AlertDialogPrimitive.Close.Props &
151
- Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
134
+ }: AlertDialogPrimitive.Close.Props & Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
152
135
  return (
153
136
  <AlertDialogPrimitive.Close
154
137
  data-slot="alert-dialog-cancel"
@@ -156,7 +139,7 @@ function AlertDialogCancel({
156
139
  render={<Button variant={variant} size={size} />}
157
140
  {...props}
158
141
  />
159
- )
142
+ );
160
143
  }
161
144
 
162
145
  export {
@@ -172,4 +155,4 @@ export {
172
155
  AlertDialogPortal,
173
156
  AlertDialogTitle,
174
157
  AlertDialogTrigger,
175
- }
158
+ };