@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
@@ -75,13 +75,7 @@ describe("ContentListTable", () => {
75
75
  ],
76
76
  });
77
77
 
78
- render(
79
- <ContentListTable
80
- data={dataRetriever}
81
- tableGeneratorType={mockModule as any}
82
- fields={["id", "title"]}
83
- />
84
- );
78
+ render(<ContentListTable data={dataRetriever} tableGeneratorType={mockModule as any} fields={["id", "title"]} />);
85
79
 
86
80
  expect(screen.getByRole("table")).toBeInTheDocument();
87
81
  });
@@ -97,7 +91,7 @@ describe("ContentListTable", () => {
97
91
  tableGeneratorType={mockModule as any}
98
92
  fields={["id", "title"]}
99
93
  title="Articles"
100
- />
94
+ />,
101
95
  );
102
96
 
103
97
  expect(screen.getByText("Articles")).toBeInTheDocument();
@@ -108,13 +102,7 @@ describe("ContentListTable", () => {
108
102
  data: [{ id: "1", title: "Article 1" }],
109
103
  });
110
104
 
111
- render(
112
- <ContentListTable
113
- data={dataRetriever}
114
- tableGeneratorType={mockModule as any}
115
- fields={["id", "title"]}
116
- />
117
- );
105
+ render(<ContentListTable data={dataRetriever} tableGeneratorType={mockModule as any} fields={["id", "title"]} />);
118
106
 
119
107
  expect(screen.getByText("ID")).toBeInTheDocument();
120
108
  expect(screen.getByText("Title")).toBeInTheDocument();
@@ -128,13 +116,7 @@ describe("ContentListTable", () => {
128
116
  ],
129
117
  });
130
118
 
131
- render(
132
- <ContentListTable
133
- data={dataRetriever}
134
- tableGeneratorType={mockModule as any}
135
- fields={["id", "title"]}
136
- />
137
- );
119
+ render(<ContentListTable data={dataRetriever} tableGeneratorType={mockModule as any} fields={["id", "title"]} />);
138
120
 
139
121
  expect(screen.getByText("1")).toBeInTheDocument();
140
122
  expect(screen.getByText("First Article")).toBeInTheDocument();
@@ -147,13 +129,7 @@ describe("ContentListTable", () => {
147
129
  data: [],
148
130
  });
149
131
 
150
- render(
151
- <ContentListTable
152
- data={dataRetriever}
153
- tableGeneratorType={mockModule as any}
154
- fields={["id", "title"]}
155
- />
156
- );
132
+ render(<ContentListTable data={dataRetriever} tableGeneratorType={mockModule as any} fields={["id", "title"]} />);
157
133
 
158
134
  expect(screen.getByText("No results.")).toBeInTheDocument();
159
135
  });
@@ -172,7 +148,7 @@ describe("ContentListTable", () => {
172
148
  fields={["id", "title"]}
173
149
  title="Articles"
174
150
  allowSearch={true}
175
- />
151
+ />,
176
152
  );
177
153
 
178
154
  expect(screen.getByTestId("content-table-search")).toBeInTheDocument();
@@ -187,13 +163,7 @@ describe("ContentListTable", () => {
187
163
  previous: vi.fn(),
188
164
  });
189
165
 
190
- render(
191
- <ContentListTable
192
- data={dataRetriever}
193
- tableGeneratorType={mockModule as any}
194
- fields={["id", "title"]}
195
- />
196
- );
166
+ render(<ContentListTable data={dataRetriever} tableGeneratorType={mockModule as any} fields={["id", "title"]} />);
197
167
 
198
168
  // Should have navigation buttons
199
169
  const buttons = screen.getAllByRole("button");
@@ -207,13 +177,7 @@ describe("ContentListTable", () => {
207
177
  previous: undefined,
208
178
  });
209
179
 
210
- render(
211
- <ContentListTable
212
- data={dataRetriever}
213
- tableGeneratorType={mockModule as any}
214
- fields={["id", "title"]}
215
- />
216
- );
180
+ render(<ContentListTable data={dataRetriever} tableGeneratorType={mockModule as any} fields={["id", "title"]} />);
217
181
 
218
182
  const buttons = screen.getAllByRole("button");
219
183
  const previousButton = buttons[0];
@@ -227,13 +191,7 @@ describe("ContentListTable", () => {
227
191
  previous: vi.fn(),
228
192
  });
229
193
 
230
- render(
231
- <ContentListTable
232
- data={dataRetriever}
233
- tableGeneratorType={mockModule as any}
234
- fields={["id", "title"]}
235
- />
236
- );
194
+ render(<ContentListTable data={dataRetriever} tableGeneratorType={mockModule as any} fields={["id", "title"]} />);
237
195
 
238
196
  const buttons = screen.getAllByRole("button");
239
197
  const nextButton = buttons[buttons.length - 1];
@@ -249,13 +207,7 @@ describe("ContentListTable", () => {
249
207
  next: nextFn,
250
208
  });
251
209
 
252
- render(
253
- <ContentListTable
254
- data={dataRetriever}
255
- tableGeneratorType={mockModule as any}
256
- fields={["id", "title"]}
257
- />
258
- );
210
+ render(<ContentListTable data={dataRetriever} tableGeneratorType={mockModule as any} fields={["id", "title"]} />);
259
211
 
260
212
  const buttons = screen.getAllByRole("button");
261
213
  const nextButton = buttons[buttons.length - 1];
@@ -274,13 +226,7 @@ describe("ContentListTable", () => {
274
226
  next: vi.fn(), // Need both to show footer
275
227
  });
276
228
 
277
- render(
278
- <ContentListTable
279
- data={dataRetriever}
280
- tableGeneratorType={mockModule as any}
281
- fields={["id", "title"]}
282
- />
283
- );
229
+ render(<ContentListTable data={dataRetriever} tableGeneratorType={mockModule as any} fields={["id", "title"]} />);
284
230
 
285
231
  const buttons = screen.getAllByRole("button");
286
232
  const previousButton = buttons[0];
@@ -300,13 +246,7 @@ describe("ContentListTable", () => {
300
246
  },
301
247
  });
302
248
 
303
- render(
304
- <ContentListTable
305
- data={dataRetriever}
306
- tableGeneratorType={mockModule as any}
307
- fields={["id", "title"]}
308
- />
309
- );
249
+ render(<ContentListTable data={dataRetriever} tableGeneratorType={mockModule as any} fields={["id", "title"]} />);
310
250
 
311
251
  expect(screen.getByText("1-25")).toBeInTheDocument();
312
252
  });
@@ -325,7 +265,7 @@ describe("ContentListTable", () => {
325
265
  fields={["id", "title"]}
326
266
  title="Articles"
327
267
  functions={<button data-testid="custom-function">Custom</button>}
328
- />
268
+ />,
329
269
  );
330
270
 
331
271
  expect(screen.getByTestId("custom-function")).toBeInTheDocument();
@@ -343,7 +283,7 @@ describe("ContentListTable", () => {
343
283
  fields={["id", "title"]}
344
284
  title="Articles"
345
285
  filters={<div data-testid="custom-filter">Filter</div>}
346
- />
286
+ />,
347
287
  );
348
288
 
349
289
  expect(screen.getByTestId("custom-filter")).toBeInTheDocument();
@@ -362,7 +302,7 @@ describe("ContentListTable", () => {
362
302
  tableGeneratorType={mockModule as any}
363
303
  fields={["id", "title"]}
364
304
  functions={<button>Action</button>}
365
- />
305
+ />,
366
306
  );
367
307
 
368
308
  // Footer should be present with buttons
@@ -376,13 +316,7 @@ describe("ContentListTable", () => {
376
316
  next: vi.fn(),
377
317
  });
378
318
 
379
- render(
380
- <ContentListTable
381
- data={dataRetriever}
382
- tableGeneratorType={mockModule as any}
383
- fields={["id", "title"]}
384
- />
385
- );
319
+ render(<ContentListTable data={dataRetriever} tableGeneratorType={mockModule as any} fields={["id", "title"]} />);
386
320
 
387
321
  // Should have navigation buttons in footer
388
322
  const buttons = screen.getAllByRole("button");
@@ -395,13 +329,7 @@ describe("ContentListTable", () => {
395
329
  previous: vi.fn(),
396
330
  });
397
331
 
398
- render(
399
- <ContentListTable
400
- data={dataRetriever}
401
- tableGeneratorType={mockModule as any}
402
- fields={["id", "title"]}
403
- />
404
- );
332
+ render(<ContentListTable data={dataRetriever} tableGeneratorType={mockModule as any} fields={["id", "title"]} />);
405
333
 
406
334
  // Should have navigation buttons in footer
407
335
  const buttons = screen.getAllByRole("button");
@@ -11,7 +11,7 @@ export const cellComponent = (params: {
11
11
  id: params.name,
12
12
  accessorKey: params.name,
13
13
  header: params.title,
14
- cell: ({ row }) => params.component,
14
+ cell: ({ row: _row }) => params.component,
15
15
  enableSorting: false,
16
16
  enableHiding: false,
17
17
  };
@@ -20,7 +20,9 @@ interface HeaderChildrenProviderProps {
20
20
  * Wrap your layout with this provider and pass the content you want in the header.
21
21
  */
22
22
  export function HeaderChildrenProvider({ children, content }: HeaderChildrenProviderProps) {
23
- return <HeaderChildrenContext.Provider value={{ headerChildren: content }}>{children}</HeaderChildrenContext.Provider>;
23
+ return (
24
+ <HeaderChildrenContext.Provider value={{ headerChildren: content }}>{children}</HeaderChildrenContext.Provider>
25
+ );
24
26
  }
25
27
 
26
28
  /**
@@ -74,10 +74,7 @@ describe("EndpointCreator", () => {
74
74
  });
75
75
 
76
76
  it("should support full fluent chain", () => {
77
- const creator = new EndpointCreator({ endpoint: "articles" })
78
- .id("123")
79
- .childEndpoint("comments")
80
- .childId("456");
77
+ const creator = new EndpointCreator({ endpoint: "articles" }).id("123").childEndpoint("comments").childId("456");
81
78
  expect(creator.generate()).toBe("articles/123/comments/456");
82
79
  });
83
80
  });
@@ -141,9 +138,7 @@ describe("EndpointCreator", () => {
141
138
  { type: "articles", fields: ["title", "body"] },
142
139
  { type: "authors", fields: ["name", "email"] },
143
140
  ]);
144
- expect(creator.generate()).toBe(
145
- "articles?fields[articles]=title,body&fields[authors]=name,email"
146
- );
141
+ expect(creator.generate()).toBe("articles?fields[articles]=title,body&fields[authors]=name,email");
147
142
  });
148
143
 
149
144
  it("should handle empty selectors array", () => {
@@ -74,9 +74,9 @@ describe("JsonApiDataFactory", () => {
74
74
  it("should throw for unregistered module", () => {
75
75
  const unregisteredModule = { name: "unknown", model: MockDataClass };
76
76
 
77
- expect(() =>
78
- JsonApiDataFactory.create(unregisteredModule as any, { data: "test" })
79
- ).toThrow("Class not registered for key: unknown");
77
+ expect(() => JsonApiDataFactory.create(unregisteredModule as any, { data: "test" })).toThrow(
78
+ "Class not registered for key: unknown",
79
+ );
80
80
  });
81
81
 
82
82
  it("should handle empty data", () => {
@@ -72,9 +72,9 @@ describe("RehydrationFactory", () => {
72
72
  included: [],
73
73
  };
74
74
 
75
- expect(() =>
76
- RehydrationFactory.rehydrate(unregisteredModule as any, hydratedData)
77
- ).toThrow("Class not registered for key: unknown");
75
+ expect(() => RehydrationFactory.rehydrate(unregisteredModule as any, hydratedData)).toThrow(
76
+ "Class not registered for key: unknown",
77
+ );
78
78
  });
79
79
 
80
80
  it("should create a new instance for rehydration", () => {
@@ -128,9 +128,7 @@ describe("RehydrationFactory", () => {
128
128
  const unregisteredModule = { name: "unknown", model: MockDataClass };
129
129
 
130
130
  expect(() =>
131
- RehydrationFactory.rehydrateList(unregisteredModule as any, [
132
- { jsonApi: {}, included: [] },
133
- ])
131
+ RehydrationFactory.rehydrateList(unregisteredModule as any, [{ jsonApi: {}, included: [] }]),
134
132
  ).toThrow("Class not registered for key: unknown");
135
133
  });
136
134
 
package/src/core/index.ts CHANGED
@@ -40,6 +40,7 @@ export * from "../features/billing/stripe-price";
40
40
  export * from "../features/billing/stripe-product";
41
41
  export * from "../features/billing/stripe-subscription";
42
42
  export * from "../features/billing/stripe-usage";
43
+ export * from "../features/billing/stripe-promotion-code";
43
44
  export * from "../features/company/company.module";
44
45
  export * from "../features/company/data";
45
46
  export * from "../features/content/content.module";
@@ -26,6 +26,7 @@ export interface FoundationModuleDefinitions {
26
26
  StripeProduct: ModuleWithPermissions;
27
27
  StripePrice: ModuleWithPermissions;
28
28
  StripeUsage: ModuleWithPermissions;
29
+ StripePromotionCode: ModuleWithPermissions;
29
30
  // OAuth modules
30
31
  OAuth: ModuleWithPermissions;
31
32
  }
@@ -67,9 +67,7 @@ describe("DataClassRegistry", () => {
67
67
 
68
68
  it("should throw for unregistered class", () => {
69
69
  const module = createMockModule({ name: "unknown" });
70
- expect(() => DataClassRegistry.get(module)).toThrow(
71
- "Class not registered for key: unknown"
72
- );
70
+ expect(() => DataClassRegistry.get(module)).toThrow("Class not registered for key: unknown");
73
71
  });
74
72
  });
75
73
 
@@ -88,12 +86,8 @@ describe("DataClassRegistry", () => {
88
86
 
89
87
  DataClassRegistry.bootstrap(modules as any);
90
88
 
91
- expect(() =>
92
- DataClassRegistry.get({ name: "articles", model: MockArticle } as any)
93
- ).not.toThrow();
94
- expect(() =>
95
- DataClassRegistry.get({ name: "users", model: MockArticle } as any)
96
- ).not.toThrow();
89
+ expect(() => DataClassRegistry.get({ name: "articles", model: MockArticle } as any)).not.toThrow();
90
+ expect(() => DataClassRegistry.get({ name: "users", model: MockArticle } as any)).not.toThrow();
97
91
  });
98
92
 
99
93
  it("should skip modules without model", () => {
@@ -110,12 +104,8 @@ describe("DataClassRegistry", () => {
110
104
 
111
105
  DataClassRegistry.bootstrap(modules as any);
112
106
 
113
- expect(() =>
114
- DataClassRegistry.get({ name: "articles", model: MockArticle } as any)
115
- ).not.toThrow();
116
- expect(() =>
117
- DataClassRegistry.get({ name: "invalid", model: null } as any)
118
- ).toThrow();
107
+ expect(() => DataClassRegistry.get({ name: "articles", model: MockArticle } as any)).not.toThrow();
108
+ expect(() => DataClassRegistry.get({ name: "invalid", model: null } as any)).toThrow();
119
109
  });
120
110
  });
121
111
 
@@ -98,12 +98,8 @@ describe("ModuleRegistrar", () => {
98
98
  ModuleRegistrar.bootstrap(MockModules);
99
99
 
100
100
  // Should be able to get registered classes
101
- expect(() =>
102
- DataClassRegistry.get({ name: "users", model: MockUser } as any)
103
- ).not.toThrow();
104
- expect(() =>
105
- DataClassRegistry.get({ name: "articles", model: MockArticle } as any)
106
- ).not.toThrow();
101
+ expect(() => DataClassRegistry.get({ name: "users", model: MockUser } as any)).not.toThrow();
102
+ expect(() => DataClassRegistry.get({ name: "articles", model: MockArticle } as any)).not.toThrow();
107
103
  });
108
104
 
109
105
  it("should only bootstrap once", () => {
@@ -114,9 +110,7 @@ describe("ModuleRegistrar", () => {
114
110
  ModuleRegistrar.bootstrap(MockModules);
115
111
 
116
112
  // Registry should still be empty because second bootstrap was skipped
117
- expect(() =>
118
- DataClassRegistry.get({ name: "users", model: MockUser } as any)
119
- ).toThrow();
113
+ expect(() => DataClassRegistry.get({ name: "users", model: MockUser } as any)).toThrow();
120
114
  });
121
115
 
122
116
  it("should handle class without model property", () => {
@@ -141,9 +135,7 @@ describe("ModuleRegistrar", () => {
141
135
  // Can bootstrap again after reset
142
136
  ModuleRegistrar.bootstrap(MockModules);
143
137
 
144
- expect(() =>
145
- DataClassRegistry.get({ name: "users", model: MockUser } as any)
146
- ).not.toThrow();
138
+ expect(() => DataClassRegistry.get({ name: "users", model: MockUser } as any)).not.toThrow();
147
139
  });
148
140
 
149
141
  it("should clear the registry", () => {
@@ -151,9 +143,7 @@ describe("ModuleRegistrar", () => {
151
143
  ModuleRegistrar.reset();
152
144
 
153
145
  // Registry should be cleared
154
- expect(() =>
155
- DataClassRegistry.get({ name: "users", model: MockUser } as any)
156
- ).toThrow();
146
+ expect(() => DataClassRegistry.get({ name: "users", model: MockUser } as any)).toThrow();
157
147
  });
158
148
  });
159
149
  });
@@ -33,12 +33,7 @@ export function GdprConsentSection<T extends FieldValues>({
33
33
 
34
34
  return (
35
35
  <div className="space-y-4 py-4">
36
- <GdprConsentCheckbox
37
- form={form}
38
- id={termsCheckboxId}
39
- label={termsLabel}
40
- required
41
- />
36
+ <GdprConsentCheckbox form={form} id={termsCheckboxId} label={termsLabel} required />
42
37
  <GdprConsentCheckbox
43
38
  form={form}
44
39
  id={marketingCheckboxId}
@@ -3,7 +3,12 @@
3
3
  import { useTranslations } from "next-intl";
4
4
  import Image from "next/image";
5
5
  import { getApiUrl } from "../../../../client/config";
6
- import { isDiscordAuthEnabled, isGoogleAuthEnabled, isInternalAuthEnabled, isRegistrationAllowed } from "../../../../login";
6
+ import {
7
+ isDiscordAuthEnabled,
8
+ isGoogleAuthEnabled,
9
+ isInternalAuthEnabled,
10
+ isRegistrationAllowed,
11
+ } from "../../../../login";
7
12
  import { Button, CardDescription, CardFooter, CardHeader, CardTitle, Link } from "../../../../shadcnui";
8
13
  import { useAuthContext } from "../../contexts";
9
14
  import { AuthComponent } from "../../enums";
@@ -82,7 +82,7 @@ export function AcceptInvitation() {
82
82
  setComponentType(AuthComponent.Login);
83
83
  setParams(undefined);
84
84
  }, 2000);
85
- } catch (e) {
85
+ } catch (_e) {
86
86
  errorToast({ title: t(`common.errors.error`), error });
87
87
  }
88
88
  };
@@ -82,7 +82,7 @@ export function ResetPassword() {
82
82
  setComponentType(AuthComponent.Login);
83
83
  setParams(undefined);
84
84
  }, 2000);
85
- } catch (e) {
85
+ } catch (_e) {
86
86
  errorToast({ title: t(`common.errors.error`), error });
87
87
  }
88
88
  };
@@ -1,14 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { Wallet, ChevronRight } from "lucide-react";
4
- import {
5
- Card,
6
- CardContent,
7
- CardHeader,
8
- CardTitle,
9
- Button,
10
- Skeleton,
11
- } from "../../../../shadcnui";
4
+ import { Card, CardContent, CardHeader, CardTitle, Button, Skeleton } from "../../../../shadcnui";
12
5
  import { PaymentMethodInterface } from "../../stripe-customer";
13
6
 
14
7
  type PaymentMethodSummaryCardProps = {
@@ -81,10 +74,16 @@ export function PaymentMethodSummaryCard({
81
74
  {paymentMethods.length === 0 ? (
82
75
  <div className="space-y-2">
83
76
  <p className="text-xl font-bold text-muted-foreground">No payment method</p>
84
- <p className="text-xs text-muted-foreground">
85
- Add a card to enable subscriptions
86
- </p>
87
- <Button variant="outline" size="sm" className="mt-2" onClick={(e) => { e.stopPropagation(); onManageClick(); }}>
77
+ <p className="text-xs text-muted-foreground">Add a card to enable subscriptions</p>
78
+ <Button
79
+ variant="outline"
80
+ size="sm"
81
+ className="mt-2"
82
+ onClick={(e) => {
83
+ e.stopPropagation();
84
+ onManageClick();
85
+ }}
86
+ >
88
87
  Add Card
89
88
  <ChevronRight className="h-4 w-4 ml-1" />
90
89
  </Button>
@@ -98,18 +97,14 @@ export function PaymentMethodSummaryCard({
98
97
  Expires {String(defaultMethod.card.expMonth).padStart(2, "0")}/{defaultMethod.card.expYear}
99
98
  </p>
100
99
  {paymentMethods.length > 1 && (
101
- <p className="text-xs text-muted-foreground">
102
- +{paymentMethods.length - 1} more card(s)
103
- </p>
100
+ <p className="text-xs text-muted-foreground">+{paymentMethods.length - 1} more card(s)</p>
104
101
  )}
105
102
  </div>
106
103
  ) : (
107
104
  <div className="space-y-2">
108
105
  <p className="text-xl font-bold">{defaultMethod?.type || "Payment Method"}</p>
109
106
  {paymentMethods.length > 1 && (
110
- <p className="text-xs text-muted-foreground">
111
- +{paymentMethods.length - 1} more method(s)
112
- </p>
107
+ <p className="text-xs text-muted-foreground">+{paymentMethods.length - 1} more method(s)</p>
113
108
  )}
114
109
  </div>
115
110
  )}
@@ -34,15 +34,6 @@ function formatDate(date: Date): string {
34
34
  });
35
35
  }
36
36
 
37
- function formatPrice(amount: number | undefined, currency: string | undefined): string {
38
- if (amount === undefined) return "N/A";
39
- const currencyCode = currency?.toUpperCase() || "USD";
40
- return new Intl.NumberFormat(undefined, {
41
- style: "currency",
42
- currency: currencyCode,
43
- }).format(amount / 100);
44
- }
45
-
46
37
  function formatPlanName(subscription: StripeSubscriptionInterface): string {
47
38
  const productName = subscription.price?.product?.name || "";
48
39
  const nickname = subscription.price?.nickname || "";
@@ -122,18 +113,22 @@ export function SubscriptionSummaryCard({
122
113
  <div className="space-y-2">
123
114
  <div className="flex items-center gap-2">
124
115
  <p className="text-xl font-bold">{formatPlanName(primarySubscription)}</p>
125
- <Badge variant={primarySubscription.cancelAtPeriodEnd ? "secondary" : getStatusBadgeVariant(primarySubscription.status)}>
116
+ <Badge
117
+ variant={
118
+ primarySubscription.cancelAtPeriodEnd
119
+ ? "secondary"
120
+ : getStatusBadgeVariant(primarySubscription.status)
121
+ }
122
+ >
126
123
  {primarySubscription.cancelAtPeriodEnd ? "Canceling" : primarySubscription.status}
127
124
  </Badge>
128
125
  </div>
129
- <p className="text-sm text-muted-foreground">
130
- {formatPrice(primarySubscription.price?.unitAmount, primarySubscription.price?.currency)}
131
- {primarySubscription.price?.recurring && <span>/{primarySubscription.price.recurring.interval}</span>}
132
- </p>
133
126
  <p className="text-xs text-muted-foreground">
134
- {primarySubscription.cancelAtPeriodEnd
135
- ? `Cancels on ${formatDate(primarySubscription.currentPeriodEnd)}`
136
- : `Renews on ${formatDate(primarySubscription.currentPeriodEnd)}`}
127
+ {!primarySubscription.price?.isTrial
128
+ ? `Ends on ${formatDate(primarySubscription.currentPeriodEnd)}`
129
+ : primarySubscription.cancelAtPeriodEnd
130
+ ? `Cancels on ${formatDate(primarySubscription.currentPeriodEnd)}`
131
+ : `Renews on ${formatDate(primarySubscription.currentPeriodEnd)}`}
137
132
  </p>
138
133
  {activeSubscriptions.length > 1 && (
139
134
  <p className="text-xs text-muted-foreground">+{activeSubscriptions.length - 1} more subscription(s)</p>
@@ -1,12 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { ReactNode } from "react";
4
- import {
5
- Dialog,
6
- DialogContent,
7
- DialogHeader,
8
- DialogTitle,
9
- } from "../../../../shadcnui";
4
+ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../../../../shadcnui";
10
5
 
11
6
  type BillingDetailModalProps = {
12
7
  open: boolean;
@@ -16,13 +11,7 @@ type BillingDetailModalProps = {
16
11
  className?: string;
17
12
  };
18
13
 
19
- export function BillingDetailModal({
20
- open,
21
- onOpenChange,
22
- title,
23
- children,
24
- className,
25
- }: BillingDetailModalProps) {
14
+ export function BillingDetailModal({ open, onOpenChange, title, children, className }: BillingDetailModalProps) {
26
15
  return (
27
16
  <Dialog open={open} onOpenChange={onOpenChange}>
28
17
  <DialogContent className={className ?? "max-w-4xl max-h-[90vh] overflow-y-auto"}>
@@ -101,7 +101,14 @@ export function PaymentMethodCard({ paymentMethod, onUpdate }: PaymentMethodCard
101
101
  </div>
102
102
  <DropdownMenu>
103
103
  <DropdownMenuTrigger>
104
- <Button render={<div />} nativeButton={false} variant="ghost" size="sm" disabled={loading} className="h-8 w-8 p-0">
104
+ <Button
105
+ render={<div />}
106
+ nativeButton={false}
107
+ variant="ghost"
108
+ size="sm"
109
+ disabled={loading}
110
+ className="h-8 w-8 p-0"
111
+ >
105
112
  <MoreVertical className="h-4 w-4" />
106
113
  </Button>
107
114
  </DropdownMenuTrigger>
@@ -1,12 +1,6 @@
1
1
  "use client";
2
2
 
3
- import {
4
- Dialog,
5
- DialogContent,
6
- DialogDescription,
7
- DialogHeader,
8
- DialogTitle,
9
- } from "../../../../../shadcnui";
3
+ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "../../../../../shadcnui";
10
4
  import { PaymentMethodForm } from "./PaymentMethodForm";
11
5
 
12
6
  type PaymentMethodEditorProps = {
@@ -34,12 +28,7 @@ export function PaymentMethodEditor({ open, onOpenChange, onSuccess }: PaymentMe
34
28
  Add a new payment method to your account. Your card information is securely processed by Stripe.
35
29
  </DialogDescription>
36
30
  </DialogHeader>
37
- {open && (
38
- <PaymentMethodForm
39
- onSuccess={handleSuccess}
40
- onCancel={handleCancel}
41
- />
42
- )}
31
+ {open && <PaymentMethodForm onSuccess={handleSuccess} onCancel={handleCancel} />}
43
32
  </DialogContent>
44
33
  </Dialog>
45
34
  );