@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.
- package/dist/{BlockNoteEditor-4MDHRUS2.js → BlockNoteEditor-3S2B36O3.js} +15 -15
- package/dist/{BlockNoteEditor-4MDHRUS2.js.map → BlockNoteEditor-3S2B36O3.js.map} +1 -1
- package/dist/{BlockNoteEditor-SZWO3MDO.mjs → BlockNoteEditor-WQUJTVJL.mjs} +5 -5
- package/dist/BlockNoteEditor-WQUJTVJL.mjs.map +1 -0
- package/dist/billing/index.d.mts +15 -5
- package/dist/billing/index.d.ts +15 -5
- package/dist/billing/index.js +750 -520
- package/dist/billing/index.js.map +1 -1
- package/dist/billing/index.mjs +665 -435
- package/dist/billing/index.mjs.map +1 -1
- package/dist/{chunk-53IPQJVH.js → chunk-3EZX4G2E.js} +147 -23
- package/dist/chunk-3EZX4G2E.js.map +1 -0
- package/dist/{chunk-I7DFEJFF.mjs → chunk-4PHADEKA.mjs} +738 -1418
- package/dist/chunk-4PHADEKA.mjs.map +1 -0
- package/dist/{chunk-E6PQQTWF.js → chunk-T2JCZYWK.js} +999 -1679
- package/dist/chunk-T2JCZYWK.js.map +1 -0
- package/dist/{chunk-P7R2DPD6.mjs → chunk-TQ5GRRTM.mjs} +125 -1
- package/dist/chunk-TQ5GRRTM.mjs.map +1 -0
- package/dist/client/index.js +3 -3
- package/dist/client/index.mjs +2 -2
- package/dist/components/index.d.mts +23 -8
- package/dist/components/index.d.ts +23 -8
- package/dist/components/index.js +3 -3
- package/dist/components/index.mjs +2 -2
- package/dist/contexts/index.d.mts +1 -1
- package/dist/contexts/index.d.ts +1 -1
- package/dist/contexts/index.js +3 -3
- package/dist/contexts/index.mjs +2 -2
- package/dist/core/index.d.mts +47 -3
- package/dist/core/index.d.ts +47 -3
- package/dist/core/index.js +8 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +7 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7 -1
- package/dist/server/index.js +3 -3
- package/dist/server/index.mjs +1 -1
- package/dist/{stripe-subscription.interface-DK7BJaNd.d.ts → stripe-promotion-code.interface-BcJty0rv.d.ts} +18 -1
- package/dist/{stripe-subscription.interface-C8uhCYIZ.d.mts → stripe-promotion-code.interface-Dnm2DJKQ.d.mts} +18 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/billing/index.ts +1 -0
- package/src/client/context/JsonApiProvider.tsx +1 -5
- package/src/client/hooks/__tests__/useJsonApiGet.test.tsx +9 -9
- package/src/client/hooks/__tests__/useJsonApiMutation.test.tsx +11 -11
- package/src/client/hooks/__tests__/useRehydration.test.ts +13 -34
- package/src/components/editors/BlockNoteEditor.tsx +2 -2
- package/src/components/forms/CommonEditorTrigger.tsx +1 -1
- package/src/components/forms/FormCheckbox.tsx +2 -12
- package/src/components/forms/FormDate.tsx +1 -6
- package/src/components/forms/FormInput.tsx +1 -1
- package/src/components/forms/FormPassword.tsx +1 -7
- package/src/components/forms/FormSelect.tsx +2 -8
- package/src/components/forms/FormSlider.tsx +1 -5
- package/src/components/forms/FormSwitch.tsx +1 -5
- package/src/components/forms/GdprConsentCheckbox.tsx +2 -8
- package/src/components/forms/PasswordInput.tsx +28 -26
- package/src/components/forms/__tests__/FormCheckbox.test.tsx +16 -18
- package/src/components/forms/__tests__/FormDate.test.tsx +14 -30
- package/src/components/forms/__tests__/FormInput.test.tsx +21 -37
- package/src/components/forms/__tests__/FormSelect.test.tsx +15 -21
- package/src/components/tables/ContentListTable.tsx +1 -1
- package/src/components/tables/__tests__/ContentListTable.test.tsx +17 -89
- package/src/components/tables/cells/cell.component.tsx +1 -1
- package/src/contexts/HeaderChildrenContext.tsx +3 -1
- package/src/core/endpoint/__tests__/EndpointCreator.test.ts +2 -7
- package/src/core/factories/__tests__/JsonApiDataFactory.test.ts +3 -3
- package/src/core/factories/__tests__/RehydrationFactory.test.ts +4 -6
- package/src/core/index.ts +1 -0
- package/src/core/registry/ModuleRegistry.ts +1 -0
- package/src/core/registry/__tests__/DataClassRegistry.test.ts +5 -15
- package/src/core/registry/__tests__/ModuleRegistrar.test.ts +5 -15
- package/src/features/auth/components/GdprConsentSection.tsx +1 -6
- package/src/features/auth/components/details/LandingComponent.tsx +6 -1
- package/src/features/auth/components/forms/AcceptInvitation.tsx +1 -1
- package/src/features/auth/components/forms/ResetPassword.tsx +1 -1
- package/src/features/billing/components/cards/PaymentMethodSummaryCard.tsx +13 -18
- package/src/features/billing/components/cards/SubscriptionSummaryCard.tsx +12 -17
- package/src/features/billing/components/modals/BillingDetailModal.tsx +2 -13
- package/src/features/billing/stripe-customer/components/details/PaymentMethodCard.tsx +8 -1
- package/src/features/billing/stripe-customer/components/forms/PaymentMethodEditor.tsx +2 -13
- package/src/features/billing/stripe-customer/components/forms/PaymentMethodForm.tsx +2 -12
- package/src/features/billing/stripe-invoice/components/details/InvoiceDetails.tsx +6 -1
- package/src/features/billing/stripe-invoice/data/stripe-invoice.interface.ts +1 -0
- package/src/features/billing/stripe-price/components/lists/PricesList.tsx +13 -5
- package/src/features/billing/stripe-product/components/lists/ProductsList.tsx +5 -5
- package/src/features/billing/stripe-promotion-code/components/PromoCodeInput.tsx +108 -0
- package/src/features/billing/stripe-promotion-code/components/index.ts +1 -0
- package/src/features/billing/stripe-promotion-code/data/index.ts +3 -0
- package/src/features/billing/stripe-promotion-code/data/stripe-promotion-code.interface.ts +14 -0
- package/src/features/billing/stripe-promotion-code/data/stripe-promotion-code.service.ts +64 -0
- package/src/features/billing/stripe-promotion-code/data/stripe-promotion-code.ts +66 -0
- package/src/features/billing/stripe-promotion-code/index.ts +2 -0
- package/src/features/billing/stripe-promotion-code/stripe-promotion-code.module.ts +9 -0
- package/src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx +1 -3
- package/src/features/billing/stripe-subscription/components/details/SubscriptionDetails.tsx +4 -1
- package/src/features/billing/stripe-subscription/components/forms/CancelSubscriptionDialog.tsx +1 -1
- package/src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx +24 -4
- package/src/features/billing/stripe-subscription/components/widgets/PricingCard.tsx +9 -2
- package/src/features/billing/stripe-subscription/components/widgets/SubscriptionStatusBadge.tsx +3 -1
- package/src/features/billing/stripe-subscription/components/wizards/SubscriptionWizard.tsx +7 -7
- package/src/features/billing/stripe-subscription/components/wizards/WizardProgressIndicator.tsx +2 -10
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepPaymentMethod.tsx +3 -13
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepReview.tsx +134 -23
- package/src/features/billing/stripe-subscription/data/stripe-subscription.interface.ts +2 -0
- package/src/features/billing/stripe-subscription/data/stripe-subscription.ts +8 -0
- package/src/features/billing/stripe-subscription/hooks/useSubscriptionWizard.ts +93 -7
- package/src/features/billing/stripe-usage/components/details/UsageSummaryCard.tsx +1 -1
- package/src/features/billing/stripe-usage/components/lists/UsageHistoryTable.tsx +1 -1
- package/src/features/company/components/details/CompanyDetails.tsx +2 -2
- package/src/features/company/components/forms/CompanyConfigurationSecurityForm.tsx +1 -1
- package/src/features/index.ts +1 -0
- package/src/features/notification/components/containers/NotificationsListContainer.tsx +1 -1
- package/src/features/notification/components/modals/NotificationModal.tsx +6 -2
- package/src/features/notification/contexts/NotificationContext.tsx +1 -3
- package/src/features/oauth/components/OAuthClientCard.tsx +15 -17
- package/src/features/oauth/components/OAuthClientDetail.tsx +7 -19
- package/src/features/oauth/components/OAuthClientForm.tsx +4 -13
- package/src/features/oauth/components/OAuthClientSecretDisplay.tsx +4 -20
- package/src/features/oauth/components/OAuthRedirectUriInput.tsx +5 -12
- package/src/features/oauth/components/OAuthScopeSelector.tsx +17 -23
- package/src/features/oauth/components/consent/OAuthConsentActions.tsx +3 -16
- package/src/features/oauth/components/consent/OAuthConsentHeader.tsx +3 -12
- package/src/features/oauth/components/consent/OAuthConsentScreen.tsx +5 -20
- package/src/features/oauth/components/consent/OAuthScopeList.tsx +3 -18
- package/src/features/onboarding/contexts/OnboardingContext.tsx +3 -3
- package/src/features/role/components/forms/FormRoles.tsx +1 -7
- package/src/features/user/components/containers/UserContainer.tsx +1 -1
- package/src/features/user/components/details/UserDetails.tsx +1 -1
- package/src/features/user/components/forms/UserDeleter.tsx +1 -1
- package/src/features/user/components/forms/UserEditor.tsx +1 -1
- package/src/features/user/components/forms/UserMultiSelect.tsx +7 -7
- package/src/features/user/components/lists/UserListInAdd.tsx +2 -2
- package/src/features/user/components/lists/UsersList.tsx +7 -1
- package/src/features/user/contexts/CurrentUserContext.tsx +36 -33
- package/src/hooks/__tests__/useDataListRetriever.test.ts +15 -21
- package/src/hooks/__tests__/useDebounce.test.ts +2 -7
- package/src/hooks/useCustomD3Graph.tsx +2 -2
- package/src/shadcnui/custom/multi-select.tsx +28 -2
- package/src/shadcnui/ui/accordion.tsx +21 -23
- package/src/shadcnui/ui/alert-dialog.tsx +45 -62
- package/src/shadcnui/ui/alert.tsx +25 -41
- package/src/shadcnui/ui/avatar.tsx +23 -36
- package/src/shadcnui/ui/badge.tsx +13 -11
- package/src/shadcnui/ui/breadcrumb.tsx +21 -55
- package/src/shadcnui/ui/button.tsx +17 -18
- package/src/shadcnui/ui/calendar.tsx +44 -93
- package/src/shadcnui/ui/carousel.tsx +72 -100
- package/src/shadcnui/ui/chart.tsx +102 -161
- package/src/shadcnui/ui/checkbox.tsx +8 -9
- package/src/shadcnui/ui/combobox.tsx +52 -83
- package/src/shadcnui/ui/command.tsx +43 -77
- package/src/shadcnui/ui/context-menu.tsx +47 -86
- package/src/shadcnui/ui/dialog.tsx +34 -60
- package/src/shadcnui/ui/drawer.tsx +32 -53
- package/src/shadcnui/ui/dropdown-menu.tsx +48 -65
- package/src/shadcnui/ui/field.tsx +39 -48
- package/src/shadcnui/ui/hover-card.tsx +9 -14
- package/src/shadcnui/ui/input-group.tsx +44 -55
- package/src/shadcnui/ui/input-otp.tsx +22 -26
- package/src/shadcnui/ui/input.tsx +6 -6
- package/src/shadcnui/ui/label.tsx +6 -6
- package/src/shadcnui/ui/navigation-menu.tsx +36 -60
- package/src/shadcnui/ui/popover.tsx +15 -38
- package/src/shadcnui/ui/progress.tsx +12 -29
- package/src/shadcnui/ui/radio-group.tsx +9 -15
- package/src/shadcnui/ui/resizable.tsx +14 -24
- package/src/shadcnui/ui/scroll-area.tsx +12 -27
- package/src/shadcnui/ui/select.tsx +41 -65
- package/src/shadcnui/ui/separator.tsx +7 -11
- package/src/shadcnui/ui/sheet.tsx +30 -55
- package/src/shadcnui/ui/sidebar.tsx +141 -189
- package/src/shadcnui/ui/skeleton.tsx +3 -9
- package/src/shadcnui/ui/slider.tsx +11 -23
- package/src/shadcnui/ui/switch.tsx +8 -8
- package/src/shadcnui/ui/tabs.tsx +14 -21
- package/src/shadcnui/ui/textarea.tsx +5 -5
- package/src/shadcnui/ui/toggle.tsx +8 -14
- package/src/shadcnui/ui/tooltip.tsx +11 -23
- package/src/testing/providers/MockJsonApiProvider.tsx +1 -5
- package/src/testing/utils/renderWithProviders.tsx +6 -10
- package/dist/BlockNoteEditor-SZWO3MDO.mjs.map +0 -1
- package/dist/chunk-53IPQJVH.js.map +0 -1
- package/dist/chunk-E6PQQTWF.js.map +0 -1
- package/dist/chunk-I7DFEJFF.mjs.map +0 -1
- 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");
|
|
@@ -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
|
|
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
|
-
|
|
79
|
-
)
|
|
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
|
-
|
|
77
|
-
)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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";
|
|
@@ -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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
|
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.
|
|
135
|
-
? `
|
|
136
|
-
:
|
|
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
|
|
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
|
);
|