@carlonicora/nextjs-jsonapi 6.32.1 → 6.33.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-XIEMUQCO.mjs → BlockNoteEditor-DCVT7YHE.mjs} +4 -4
- package/dist/{BlockNoteEditor-RD3JNEV5.js → BlockNoteEditor-ZVK5MD34.js} +24 -24
- package/dist/{BlockNoteEditor-RD3JNEV5.js.map → BlockNoteEditor-ZVK5MD34.js.map} +1 -1
- package/dist/{BlockNoteViewer-RQBYHBQL.js → BlockNoteViewer-2KPGZSTJ.js} +3 -3
- package/dist/{BlockNoteViewer-RQBYHBQL.js.map → BlockNoteViewer-2KPGZSTJ.js.map} +1 -1
- package/dist/{BlockNoteViewer-NVSR67X5.mjs → BlockNoteViewer-XDAC6AL7.mjs} +2 -2
- package/dist/billing/index.js +477 -469
- package/dist/billing/index.js.map +1 -1
- package/dist/billing/index.mjs +15 -7
- package/dist/billing/index.mjs.map +1 -1
- package/dist/{chunk-5H25LM2Z.js → chunk-576XHOYH.js} +5 -5
- package/dist/{chunk-5H25LM2Z.js.map → chunk-576XHOYH.js.map} +1 -1
- package/dist/{chunk-NPGX2GJD.js → chunk-CI64OTRX.js} +98 -75
- package/dist/chunk-CI64OTRX.js.map +1 -0
- package/dist/{chunk-AEBZ67JJ.mjs → chunk-ETU3AW2X.mjs} +2 -2
- package/dist/{chunk-4JVLLBRS.mjs → chunk-GUNH3THR.mjs} +24 -1
- package/dist/chunk-GUNH3THR.mjs.map +1 -0
- package/dist/{chunk-SQNFYMYE.js → chunk-KH4Y4FU4.js} +1532 -1288
- package/dist/chunk-KH4Y4FU4.js.map +1 -0
- package/dist/{chunk-PNCO3HMJ.js → chunk-TCAOU6NH.js} +3 -3
- package/dist/{chunk-PNCO3HMJ.js.map → chunk-TCAOU6NH.js.map} +1 -1
- package/dist/{chunk-F25BVBEH.mjs → chunk-V4NPMUZ2.mjs} +2663 -2419
- package/dist/chunk-V4NPMUZ2.mjs.map +1 -0
- package/dist/{chunk-AMQFKCAX.mjs → chunk-WPFEPIKP.mjs} +2 -2
- package/dist/client/index.js +6 -4
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +5 -3
- package/dist/components/index.js +12 -4
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +11 -3
- package/dist/components/tables/ContentListTable.d.ts +14 -0
- package/dist/components/tables/ContentListTable.d.ts.map +1 -1
- package/dist/components/tables/SortableHeader.d.ts +17 -0
- package/dist/components/tables/SortableHeader.d.ts.map +1 -0
- package/dist/components/tables/cells/cell.date.d.ts +4 -0
- package/dist/components/tables/cells/cell.date.d.ts.map +1 -1
- package/dist/components/tables/cells/cell.dateTime.d.ts +4 -0
- package/dist/components/tables/cells/cell.dateTime.d.ts.map +1 -1
- package/dist/components/tables/cells/cell.link.d.ts +4 -0
- package/dist/components/tables/cells/cell.link.d.ts.map +1 -1
- package/dist/components/tables/cells/cell.text.d.ts +4 -0
- package/dist/components/tables/cells/cell.text.d.ts.map +1 -1
- package/dist/components/tables/index.d.ts +2 -0
- package/dist/components/tables/index.d.ts.map +1 -1
- package/dist/components/tables/sorting.d.ts +24 -0
- package/dist/components/tables/sorting.d.ts.map +1 -0
- package/dist/contexts/index.js +4 -4
- package/dist/contexts/index.mjs +3 -3
- package/dist/core/index.js +2 -2
- package/dist/core/index.mjs +1 -1
- package/dist/email-automations/index.js +120 -115
- package/dist/email-automations/index.js.map +1 -1
- package/dist/email-automations/index.mjs +18 -13
- package/dist/email-automations/index.mjs.map +1 -1
- package/dist/features/analytics/index.js +114 -114
- package/dist/features/analytics/index.mjs +3 -3
- package/dist/features/analytics/server.js +4 -4
- package/dist/features/analytics/server.mjs +2 -2
- package/dist/features/billing/stripe-coupon/components/lists/CouponCodesList.d.ts.map +1 -1
- package/dist/features/billing/stripe-coupon/components/lists/CouponsList.d.ts.map +1 -1
- package/dist/features/billing/stripe-coupon/data/stripe-coupon-code.service.d.ts +2 -0
- package/dist/features/billing/stripe-coupon/data/stripe-coupon-code.service.d.ts.map +1 -1
- package/dist/features/billing/stripe-coupon/data/stripe-coupon.service.d.ts +1 -0
- package/dist/features/billing/stripe-coupon/data/stripe-coupon.service.d.ts.map +1 -1
- package/dist/features/billing/stripe-coupon/hooks/useStripeCouponCodeTableStructure.d.ts.map +1 -1
- package/dist/features/billing/stripe-coupon/hooks/useStripeCouponTableStructure.d.ts.map +1 -1
- package/dist/features/billing/stripe-price/components/lists/PricesList.d.ts.map +1 -1
- package/dist/features/billing/stripe-price/data/stripe-price.service.d.ts +2 -0
- package/dist/features/billing/stripe-price/data/stripe-price.service.d.ts.map +1 -1
- package/dist/features/billing/stripe-price/hooks/useStripePriceTableStructure.d.ts.map +1 -1
- package/dist/features/billing/stripe-product/components/lists/ProductsList.d.ts.map +1 -1
- package/dist/features/billing/stripe-product/data/stripe-product.service.d.ts +2 -0
- package/dist/features/billing/stripe-product/data/stripe-product.service.d.ts.map +1 -1
- package/dist/features/billing/stripe-product/hooks/useStripeProductTableStructure.d.ts.map +1 -1
- package/dist/features/blog/components/admin/BlogPostsList.d.ts.map +1 -1
- package/dist/features/blog/data/BlogPostService.d.ts +1 -0
- package/dist/features/blog/data/BlogPostService.d.ts.map +1 -1
- package/dist/features/blog/index.js +134 -132
- package/dist/features/blog/index.js.map +1 -1
- package/dist/features/blog/index.mjs +8 -6
- package/dist/features/blog/index.mjs.map +1 -1
- package/dist/features/blog/server.js +7 -7
- package/dist/features/blog/server.mjs +1 -1
- package/dist/features/company/components/lists/CompaniesList.d.ts.map +1 -1
- package/dist/features/company/data/company.service.d.ts +1 -0
- package/dist/features/company/data/company.service.d.ts.map +1 -1
- package/dist/features/content/data/content.service.d.ts +3 -0
- package/dist/features/content/data/content.service.d.ts.map +1 -1
- package/dist/features/email-automation/components/lists/EmailAutomationRecipientsList.d.ts.map +1 -1
- package/dist/features/email-automation/components/lists/EmailAutomationsList.d.ts.map +1 -1
- package/dist/features/email-automation/data/EmailAutomationService.d.ts +3 -0
- package/dist/features/email-automation/data/EmailAutomationService.d.ts.map +1 -1
- package/dist/features/email-automation/hooks/useEmailAutomationTableStructure.d.ts.map +1 -1
- package/dist/features/email-blast/components/lists/EmailBlastsList.d.ts.map +1 -1
- package/dist/features/email-blast/data/EmailBlastService.d.ts +1 -0
- package/dist/features/email-blast/data/EmailBlastService.d.ts.map +1 -1
- package/dist/features/email-blast/hooks/useEmailBlastTableStructure.d.ts.map +1 -1
- package/dist/features/help/index.js +37 -37
- package/dist/features/help/index.mjs +3 -3
- package/dist/features/how-to/components/lists/HowToList.d.ts.map +1 -1
- package/dist/features/how-to/data/HowToService.d.ts +2 -0
- package/dist/features/how-to/data/HowToService.d.ts.map +1 -1
- package/dist/features/notification/components/lists/NotificationsList.d.ts.map +1 -1
- package/dist/features/notification/data/notification.service.d.ts +2 -0
- package/dist/features/notification/data/notification.service.d.ts.map +1 -1
- package/dist/features/referral/components/containers/ReferralsAdminContainer.d.ts.map +1 -1
- package/dist/features/referral/data/ReferralAdminService.d.ts +1 -0
- package/dist/features/referral/data/ReferralAdminService.d.ts.map +1 -1
- package/dist/features/referral/hooks/useReferralAdminReferralTableStructure.d.ts.map +1 -1
- package/dist/features/role/components/lists/RolesList.d.ts.map +1 -1
- package/dist/features/role/components/lists/UserRolesList.d.ts.map +1 -1
- package/dist/features/role/data/role.service.d.ts +3 -0
- package/dist/features/role/data/role.service.d.ts.map +1 -1
- package/dist/features/tokenusage/index.js +114 -114
- package/dist/features/tokenusage/index.mjs +3 -3
- package/dist/features/user/components/lists/AdminUsersList.d.ts.map +1 -1
- package/dist/features/user/components/lists/CompanyUsersList.d.ts.map +1 -1
- package/dist/features/user/components/lists/PlatformUsersList.d.ts.map +1 -1
- package/dist/features/user/components/lists/RoleUsersList.d.ts.map +1 -1
- package/dist/features/user/components/lists/UsersListByContentIds.d.ts.map +1 -1
- package/dist/features/user/data/user.service.d.ts +11 -0
- package/dist/features/user/data/user.service.d.ts.map +1 -1
- package/dist/features/waitlist/components/lists/WaitlistList.d.ts.map +1 -1
- package/dist/features/waitlist/data/WaitlistService.d.ts +2 -0
- package/dist/features/waitlist/data/WaitlistService.d.ts.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/useDataListRetriever.d.ts +13 -0
- package/dist/hooks/useDataListRetriever.d.ts.map +1 -1
- package/dist/hooks/useListSortParam.d.ts +23 -0
- package/dist/hooks/useListSortParam.d.ts.map +1 -0
- package/dist/index.js +3 -3
- package/dist/index.mjs +2 -2
- package/dist/server/index.js +3 -3
- package/dist/server/index.mjs +2 -2
- package/dist/shadcnui/ui/calendar.d.ts +1 -1
- package/dist/shadcnui/ui/calendar.d.ts.map +1 -1
- package/messages/en.json +11 -0
- package/messages/it.json +11 -0
- package/package.json +1 -1
- package/src/components/tables/ContentListTable.tsx +108 -10
- package/src/components/tables/SortableHeader.tsx +43 -0
- package/src/components/tables/__tests__/ContentListTable.rendering.spec.tsx +1 -0
- package/src/components/tables/__tests__/ContentListTable.sort.spec.tsx +273 -0
- package/src/components/tables/__tests__/sorting.spec.ts +61 -0
- package/src/components/tables/cells/cell.date.tsx +10 -2
- package/src/components/tables/cells/cell.dateTime.tsx +10 -2
- package/src/components/tables/cells/cell.link.tsx +6 -1
- package/src/components/tables/cells/cell.text.tsx +11 -2
- package/src/components/tables/index.ts +2 -0
- package/src/components/tables/sorting.ts +41 -0
- package/src/features/billing/stripe-coupon/components/lists/CouponCodesList.tsx +2 -0
- package/src/features/billing/stripe-coupon/components/lists/CouponsList.tsx +2 -0
- package/src/features/billing/stripe-coupon/data/stripe-coupon-code.service.ts +4 -0
- package/src/features/billing/stripe-coupon/data/stripe-coupon.service.ts +7 -1
- package/src/features/billing/stripe-coupon/hooks/__tests__/useStripeCouponCodeTableStructure.sort.spec.tsx +30 -0
- package/src/features/billing/stripe-coupon/hooks/__tests__/useStripeCouponTableStructure.sort.spec.tsx +33 -0
- package/src/features/billing/stripe-coupon/hooks/useStripeCouponCodeTableStructure.tsx +5 -4
- package/src/features/billing/stripe-coupon/hooks/useStripeCouponTableStructure.tsx +7 -4
- package/src/features/billing/stripe-price/components/lists/PricesList.tsx +2 -0
- package/src/features/billing/stripe-price/data/stripe-price.service.ts +4 -0
- package/src/features/billing/stripe-price/hooks/__tests__/useStripePriceTableStructure.sort.spec.tsx +29 -0
- package/src/features/billing/stripe-price/hooks/useStripePriceTableStructure.tsx +8 -5
- package/src/features/billing/stripe-product/components/lists/ProductsList.tsx +2 -0
- package/src/features/billing/stripe-product/data/stripe-product.service.ts +4 -0
- package/src/features/billing/stripe-product/hooks/__tests__/useStripeProductTableStructure.sort.spec.tsx +29 -0
- package/src/features/billing/stripe-product/hooks/useStripeProductTableStructure.tsx +3 -2
- package/src/features/blog/components/admin/BlogPostsList.tsx +2 -0
- package/src/features/blog/data/BlogPostService.ts +3 -1
- package/src/features/blog/hooks/__tests__/useBlogPostTableStructure.sort.spec.tsx +19 -0
- package/src/features/blog/hooks/useBlogPostTableStructure.tsx +2 -2
- package/src/features/company/components/lists/CompaniesList.tsx +2 -0
- package/src/features/company/data/company.service.ts +3 -1
- package/src/features/company/hooks/__tests__/useCompanyTableStructure.sort.spec.tsx +19 -0
- package/src/features/company/hooks/useCompanyTableStructure.tsx +1 -1
- package/src/features/content/data/content.service.ts +12 -1
- package/src/features/content/hooks/__tests__/useContentTableStructure.sort.spec.tsx +21 -0
- package/src/features/content/hooks/useContentTableStructure.tsx +1 -1
- package/src/features/email-automation/components/lists/EmailAutomationRecipientsList.tsx +2 -0
- package/src/features/email-automation/components/lists/EmailAutomationsList.tsx +2 -0
- package/src/features/email-automation/data/EmailAutomationService.ts +9 -1
- package/src/features/email-automation/hooks/__tests__/useEmailAutomationEnrollmentTableStructure.sort.spec.tsx +21 -0
- package/src/features/email-automation/hooks/__tests__/useEmailAutomationTableStructure.sort.spec.tsx +29 -0
- package/src/features/email-automation/hooks/useEmailAutomationEnrollmentTableStructure.tsx +4 -4
- package/src/features/email-automation/hooks/useEmailAutomationTableStructure.tsx +5 -4
- package/src/features/email-blast/components/lists/EmailBlastsList.tsx +2 -0
- package/src/features/email-blast/data/EmailBlastService.ts +5 -1
- package/src/features/email-blast/hooks/__tests__/useEmailBlastTableStructure.sort.spec.tsx +29 -0
- package/src/features/email-blast/hooks/useEmailBlastTableStructure.tsx +16 -8
- package/src/features/how-to/components/lists/HowToList.tsx +2 -0
- package/src/features/how-to/data/HowToService.ts +4 -0
- package/src/features/how-to/hooks/__tests__/useHowToTableStructure.sort.spec.tsx +18 -0
- package/src/features/how-to/hooks/useHowToTableStructure.tsx +1 -1
- package/src/features/notification/components/lists/NotificationsList.tsx +2 -0
- package/src/features/notification/data/notification.service.ts +4 -0
- package/src/features/notification/hooks/__tests__/useNotificationTableStructure.sort.spec.tsx +19 -0
- package/src/features/notification/hooks/useNotificationTableStructure.tsx +1 -1
- package/src/features/referral/components/containers/ReferralsAdminContainer.tsx +4 -2
- package/src/features/referral/data/ReferralAdminService.ts +6 -1
- package/src/features/referral/hooks/__tests__/useReferralAdminReferralTableStructure.sort.spec.tsx +35 -0
- package/src/features/referral/hooks/useReferralAdminReferralTableStructure.tsx +10 -5
- package/src/features/role/components/lists/RolesList.tsx +2 -0
- package/src/features/role/components/lists/UserRolesList.tsx +2 -0
- package/src/features/role/data/role.service.ts +7 -1
- package/src/features/role/hooks/__tests__/useRoleTableStructure.sort.spec.tsx +16 -0
- package/src/features/role/hooks/useRoleTableStructure.tsx +2 -2
- package/src/features/user/components/lists/AdminUsersList.tsx +2 -0
- package/src/features/user/components/lists/CompanyUsersList.tsx +4 -0
- package/src/features/user/components/lists/PlatformUsersList.tsx +2 -0
- package/src/features/user/components/lists/RoleUsersList.tsx +2 -0
- package/src/features/user/components/lists/UsersListByContentIds.tsx +2 -0
- package/src/features/user/data/user.service.ts +28 -1
- package/src/features/user/hooks/__tests__/useUserTableStructure.sort.spec.tsx +16 -0
- package/src/features/user/hooks/useUserTableStructure.tsx +2 -2
- package/src/features/waitlist/components/lists/WaitlistList.tsx +2 -0
- package/src/features/waitlist/data/WaitlistService.ts +4 -0
- package/src/features/waitlist/hooks/__tests__/useWaitlistTableStructure.sort.spec.tsx +17 -0
- package/src/features/waitlist/hooks/useWaitlistTableStructure.tsx +3 -3
- package/src/hooks/__tests__/useDataListRetriever.sort.spec.tsx +114 -0
- package/src/hooks/__tests__/useListSortParam.spec.tsx +91 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useDataListRetriever.ts +44 -4
- package/src/hooks/useListSortParam.ts +53 -0
- package/src/shadcnui/ui/__tests__/calendar.spec.tsx +222 -0
- package/src/shadcnui/ui/calendar.tsx +28 -1
- package/dist/chunk-4JVLLBRS.mjs.map +0 -1
- package/dist/chunk-F25BVBEH.mjs.map +0 -1
- package/dist/chunk-NPGX2GJD.js.map +0 -1
- package/dist/chunk-SQNFYMYE.js.map +0 -1
- /package/dist/{BlockNoteEditor-XIEMUQCO.mjs.map → BlockNoteEditor-DCVT7YHE.mjs.map} +0 -0
- /package/dist/{BlockNoteViewer-NVSR67X5.mjs.map → BlockNoteViewer-XDAC6AL7.mjs.map} +0 -0
- /package/dist/{chunk-AEBZ67JJ.mjs.map → chunk-ETU3AW2X.mjs.map} +0 -0
- /package/dist/{chunk-AMQFKCAX.mjs.map → chunk-WPFEPIKP.mjs.map} +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Direction of a column sort. */
|
|
2
|
+
export type SortDirection = "asc" | "desc";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The fields a column sorts by: its `meta.sortFields` when declared (a column
|
|
6
|
+
* built from several stored fields, or whose stored property differs from its
|
|
7
|
+
* id), otherwise its id.
|
|
8
|
+
*/
|
|
9
|
+
export function sortFieldsOf(column: { id?: string; columnDef: { meta?: unknown } }): string[] {
|
|
10
|
+
const meta = column.columnDef.meta as { sortFields?: unknown } | undefined;
|
|
11
|
+
if (Array.isArray(meta?.sortFields) && meta.sortFields.length > 0) {
|
|
12
|
+
return meta.sortFields.filter((field): field is string => typeof field === "string");
|
|
13
|
+
}
|
|
14
|
+
return column.id ? [column.id] : [];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The direction `sort` (JSON:API wire format) gives a column sorting by `fields`,
|
|
19
|
+
* or `undefined` when `sort` is about another column or absent.
|
|
20
|
+
*/
|
|
21
|
+
export function sortStateFor(sort: string | undefined, fields: string[]): SortDirection | undefined {
|
|
22
|
+
if (!sort || fields.length === 0) return undefined;
|
|
23
|
+
if (sort === fields.join(",")) return "asc";
|
|
24
|
+
if (sort === fields.map((field) => `-${field}`).join(",")) return "desc";
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The sort a header click sets: none → ascending → descending → none.
|
|
30
|
+
* Returns `null` to clear the sort.
|
|
31
|
+
*/
|
|
32
|
+
export function nextSortValue(current: string | undefined, fields: string[]): string | null {
|
|
33
|
+
switch (sortStateFor(current, fields)) {
|
|
34
|
+
case undefined:
|
|
35
|
+
return fields.join(",");
|
|
36
|
+
case "asc":
|
|
37
|
+
return fields.map((field) => `-${field}`).join(",");
|
|
38
|
+
case "desc":
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -25,6 +25,8 @@ export function CouponCodesList({ couponId }: CouponCodesListProps) {
|
|
|
25
25
|
retriever: (params) => StripeCouponCodeService.listByCoupon({ ...params, couponId }),
|
|
26
26
|
retrieverParams: {},
|
|
27
27
|
module: Modules.StripeCouponCode,
|
|
28
|
+
// Display only (call C8): the server's own order when no sort is chosen.
|
|
29
|
+
defaultSort: "createdAt",
|
|
28
30
|
});
|
|
29
31
|
|
|
30
32
|
// Switching "Apply automatically" on one code clears it on the others in the
|
|
@@ -31,6 +31,8 @@ export function CouponsList({ fullWidth }: CouponsListProps = {}) {
|
|
|
31
31
|
retriever: (params) => StripeCouponService.listCoupons(params),
|
|
32
32
|
retrieverParams: {},
|
|
33
33
|
module: Modules.StripeCoupon,
|
|
34
|
+
// Display only (call C8): the server's own order when no sort is chosen.
|
|
35
|
+
defaultSort: "-updatedAt",
|
|
34
36
|
});
|
|
35
37
|
|
|
36
38
|
// Renames and code changes happen on the detail page, so the list has to
|
|
@@ -18,6 +18,8 @@ export class StripeCouponCodeService extends AbstractService {
|
|
|
18
18
|
couponId: string;
|
|
19
19
|
next?: NextRef;
|
|
20
20
|
prev?: PreviousRef;
|
|
21
|
+
/** JSON:API `sort` (`name`, `-name`), forwarded to the API. */
|
|
22
|
+
sort?: string;
|
|
21
23
|
}): Promise<StripeCouponCodeInterface[]> {
|
|
22
24
|
const endpoint = new EndpointCreator({
|
|
23
25
|
endpoint: Modules.StripeCoupon,
|
|
@@ -25,6 +27,8 @@ export class StripeCouponCodeService extends AbstractService {
|
|
|
25
27
|
childEndpoint: Modules.StripeCouponCode,
|
|
26
28
|
});
|
|
27
29
|
|
|
30
|
+
if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
|
|
31
|
+
|
|
28
32
|
return this.callApi({
|
|
29
33
|
type: Modules.StripeCouponCode,
|
|
30
34
|
method: HttpMethod.GET,
|
|
@@ -10,11 +10,17 @@ export class StripeCouponService extends AbstractService {
|
|
|
10
10
|
/**
|
|
11
11
|
* List all coupons with their codes (admin)
|
|
12
12
|
*/
|
|
13
|
-
static async listCoupons(params?: {
|
|
13
|
+
static async listCoupons(params?: {
|
|
14
|
+
next?: NextRef;
|
|
15
|
+
prev?: PreviousRef;
|
|
16
|
+
sort?: string;
|
|
17
|
+
}): Promise<StripeCouponInterface[]> {
|
|
14
18
|
const endpoint = new EndpointCreator({
|
|
15
19
|
endpoint: Modules.StripeCoupon,
|
|
16
20
|
});
|
|
17
21
|
|
|
22
|
+
if (params?.sort) endpoint.addAdditionalParam("sort", params?.sort);
|
|
23
|
+
|
|
18
24
|
return this.callApi({
|
|
19
25
|
type: Modules.StripeCoupon,
|
|
20
26
|
method: HttpMethod.GET,
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { StripeCouponCodeFields } from "../../data/stripe-coupon-code.fields";
|
|
4
|
+
import { useStripeCouponCodeTableStructure } from "../useStripeCouponCodeTableStructure";
|
|
5
|
+
|
|
6
|
+
vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key, useLocale: () => "en" }));
|
|
7
|
+
vi.mock("../../../contexts/CouponContext", () => ({ useCouponContext: () => ({}) }));
|
|
8
|
+
|
|
9
|
+
/** Spec row A21: code, expiresAt, automatic, places (timesRedeemed); status is derived. Only an explicit `enableSorting: true` opts a column in (call C3). */
|
|
10
|
+
describe("useStripeCouponCodeTableStructure sorting", () => {
|
|
11
|
+
it("opts in exactly the row's sortable columns", () => {
|
|
12
|
+
const { result } = renderHook(() =>
|
|
13
|
+
useStripeCouponCodeTableStructure({ data: [], fields: Object.values(StripeCouponCodeFields) as any }),
|
|
14
|
+
);
|
|
15
|
+
const columns = result.current.columns as any[];
|
|
16
|
+
const sortable = columns.filter((column) => column.enableSorting === true).map((column) => column.id);
|
|
17
|
+
expect(sortable.sort()).toEqual(["automatic", "code", "expiresAt", "places"]);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("maps the columns whose key differs from the stored property (call C4)", () => {
|
|
21
|
+
const { result } = renderHook(() =>
|
|
22
|
+
useStripeCouponCodeTableStructure({ data: [], fields: Object.values(StripeCouponCodeFields) as any }),
|
|
23
|
+
);
|
|
24
|
+
const columns = result.current.columns as any[];
|
|
25
|
+
const sortFields = Object.fromEntries(
|
|
26
|
+
columns.filter((column) => column.meta?.sortFields).map((column) => [column.id, column.meta.sortFields]),
|
|
27
|
+
);
|
|
28
|
+
expect(sortFields).toEqual({ places: ["timesRedeemed"] });
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { StripeCouponFields } from "../../data/stripe-coupon.fields";
|
|
4
|
+
import { useStripeCouponTableStructure } from "../useStripeCouponTableStructure";
|
|
5
|
+
|
|
6
|
+
vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key, useLocale: () => "en" }));
|
|
7
|
+
|
|
8
|
+
/** Spec row A20: name, status (valid), places, discount; codes (a count) is not sortable. Only an explicit `enableSorting: true` opts a column in (call C3). */
|
|
9
|
+
describe("useStripeCouponTableStructure sorting", () => {
|
|
10
|
+
it("opts in exactly the row's sortable columns", () => {
|
|
11
|
+
const { result } = renderHook(() =>
|
|
12
|
+
useStripeCouponTableStructure({ data: [], fields: Object.values(StripeCouponFields) as any }),
|
|
13
|
+
);
|
|
14
|
+
const columns = result.current.columns as any[];
|
|
15
|
+
const sortable = columns.filter((column) => column.enableSorting === true).map((column) => column.id);
|
|
16
|
+
expect(sortable.sort()).toEqual(["discount", "name", "places", "status"]);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("maps the columns whose key differs from the stored property (call C4)", () => {
|
|
20
|
+
const { result } = renderHook(() =>
|
|
21
|
+
useStripeCouponTableStructure({ data: [], fields: Object.values(StripeCouponFields) as any }),
|
|
22
|
+
);
|
|
23
|
+
const columns = result.current.columns as any[];
|
|
24
|
+
const sortFields = Object.fromEntries(
|
|
25
|
+
columns.filter((column) => column.meta?.sortFields).map((column) => [column.id, column.meta.sortFields]),
|
|
26
|
+
);
|
|
27
|
+
expect(sortFields).toEqual({
|
|
28
|
+
discount: ["discountType", "percentOff", "amountOff"],
|
|
29
|
+
places: ["timesRedeemed", "maxRedemptions"],
|
|
30
|
+
status: ["valid"],
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -70,7 +70,7 @@ export const useStripeCouponCodeTableStructure: UseTableStructureHook<
|
|
|
70
70
|
const code: StripeCouponCodeInterface = row.original.jsonApiData;
|
|
71
71
|
return <span className="text-xs">{code.code}</span>;
|
|
72
72
|
},
|
|
73
|
-
enableSorting:
|
|
73
|
+
enableSorting: true,
|
|
74
74
|
enableHiding: false,
|
|
75
75
|
}),
|
|
76
76
|
[StripeCouponCodeFields.places]: () => ({
|
|
@@ -91,7 +91,8 @@ export const useStripeCouponCodeTableStructure: UseTableStructureHook<
|
|
|
91
91
|
</span>
|
|
92
92
|
);
|
|
93
93
|
},
|
|
94
|
-
|
|
94
|
+
meta: { sortFields: ["timesRedeemed"] },
|
|
95
|
+
enableSorting: true,
|
|
95
96
|
enableHiding: false,
|
|
96
97
|
}),
|
|
97
98
|
[StripeCouponCodeFields.expiresAt]: () => ({
|
|
@@ -106,7 +107,7 @@ export const useStripeCouponCodeTableStructure: UseTableStructureHook<
|
|
|
106
107
|
</span>
|
|
107
108
|
);
|
|
108
109
|
},
|
|
109
|
-
enableSorting:
|
|
110
|
+
enableSorting: true,
|
|
110
111
|
enableHiding: false,
|
|
111
112
|
}),
|
|
112
113
|
[StripeCouponCodeFields.automatic]: () => ({
|
|
@@ -121,7 +122,7 @@ export const useStripeCouponCodeTableStructure: UseTableStructureHook<
|
|
|
121
122
|
<span className="text-muted-foreground text-xs">—</span>
|
|
122
123
|
);
|
|
123
124
|
},
|
|
124
|
-
enableSorting:
|
|
125
|
+
enableSorting: true,
|
|
125
126
|
enableHiding: false,
|
|
126
127
|
}),
|
|
127
128
|
[StripeCouponCodeFields.status]: () => ({
|
|
@@ -37,7 +37,7 @@ export const useStripeCouponTableStructure: UseTableStructureHook<StripeCouponIn
|
|
|
37
37
|
const coupon: StripeCouponInterface = row.original.jsonApiData;
|
|
38
38
|
return <Link href={generateUrl({ page: STRIPE_COUPON_DETAIL_PAGE_URL, id: coupon.id })}>{coupon.name}</Link>;
|
|
39
39
|
},
|
|
40
|
-
enableSorting:
|
|
40
|
+
enableSorting: true,
|
|
41
41
|
enableHiding: false,
|
|
42
42
|
}),
|
|
43
43
|
[StripeCouponFields.discount]: () => ({
|
|
@@ -48,7 +48,8 @@ export const useStripeCouponTableStructure: UseTableStructureHook<StripeCouponIn
|
|
|
48
48
|
const coupon: StripeCouponInterface = row.original.jsonApiData;
|
|
49
49
|
return <span className="text-xs">{formatCouponDiscount(coupon, t, fmt)}</span>;
|
|
50
50
|
},
|
|
51
|
-
|
|
51
|
+
meta: { sortFields: ["discountType", "percentOff", "amountOff"] },
|
|
52
|
+
enableSorting: true,
|
|
52
53
|
enableHiding: false,
|
|
53
54
|
}),
|
|
54
55
|
[StripeCouponFields.places]: () => ({
|
|
@@ -70,7 +71,8 @@ export const useStripeCouponTableStructure: UseTableStructureHook<StripeCouponIn
|
|
|
70
71
|
</span>
|
|
71
72
|
);
|
|
72
73
|
},
|
|
73
|
-
|
|
74
|
+
meta: { sortFields: ["timesRedeemed", "maxRedemptions"] },
|
|
75
|
+
enableSorting: true,
|
|
74
76
|
enableHiding: false,
|
|
75
77
|
}),
|
|
76
78
|
[StripeCouponFields.codes]: () => ({
|
|
@@ -96,7 +98,8 @@ export const useStripeCouponTableStructure: UseTableStructureHook<StripeCouponIn
|
|
|
96
98
|
<Badge variant="softGray">{t("billing.admin.coupons.status.ended")}</Badge>
|
|
97
99
|
);
|
|
98
100
|
},
|
|
99
|
-
|
|
101
|
+
meta: { sortFields: ["valid"] },
|
|
102
|
+
enableSorting: true,
|
|
100
103
|
enableHiding: false,
|
|
101
104
|
}),
|
|
102
105
|
};
|
|
@@ -50,6 +50,8 @@ export function PricesList({ productId, fullWidth }: PricesListProps) {
|
|
|
50
50
|
retriever: (params) => StripePriceService.listPrices({ ...params, productId }),
|
|
51
51
|
retrieverParams: {},
|
|
52
52
|
module: Modules.StripePrice,
|
|
53
|
+
// Display only (call C8): the server's own order when no sort is chosen.
|
|
54
|
+
defaultSort: "-createdAt",
|
|
53
55
|
});
|
|
54
56
|
|
|
55
57
|
useEffect(() => {
|
|
@@ -13,6 +13,8 @@ export class StripePriceService extends AbstractService {
|
|
|
13
13
|
active?: boolean;
|
|
14
14
|
next?: NextRef;
|
|
15
15
|
prev?: PreviousRef;
|
|
16
|
+
/** JSON:API `sort` (`name`, `-name`), forwarded to the API. */
|
|
17
|
+
sort?: string;
|
|
16
18
|
}): Promise<StripePriceInterface[]> {
|
|
17
19
|
const endpoint = new EndpointCreator({
|
|
18
20
|
endpoint: Modules.StripePrice,
|
|
@@ -25,6 +27,8 @@ export class StripePriceService extends AbstractService {
|
|
|
25
27
|
endpoint.addAdditionalParam("active", params.active.toString());
|
|
26
28
|
}
|
|
27
29
|
|
|
30
|
+
if (params?.sort) endpoint.addAdditionalParam("sort", params?.sort);
|
|
31
|
+
|
|
28
32
|
return this.callApi({
|
|
29
33
|
type: Modules.StripePrice,
|
|
30
34
|
method: HttpMethod.GET,
|
package/src/features/billing/stripe-price/hooks/__tests__/useStripePriceTableStructure.sort.spec.tsx
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { StripePriceFields } from "../../data/stripe-price.fields";
|
|
4
|
+
import { useStripePriceTableStructure } from "../useStripePriceTableStructure";
|
|
5
|
+
|
|
6
|
+
vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key, useLocale: () => "en" }));
|
|
7
|
+
|
|
8
|
+
/** Spec row A22: nickname, amount, interval, token, status. Only an explicit `enableSorting: true` opts a column in (call C3). */
|
|
9
|
+
describe("useStripePriceTableStructure sorting", () => {
|
|
10
|
+
it("opts in exactly the row's sortable columns", () => {
|
|
11
|
+
const { result } = renderHook(() =>
|
|
12
|
+
useStripePriceTableStructure({ data: [], fields: Object.values(StripePriceFields) as any }),
|
|
13
|
+
);
|
|
14
|
+
const columns = result.current.columns as any[];
|
|
15
|
+
const sortable = columns.filter((column) => column.enableSorting === true).map((column) => column.id);
|
|
16
|
+
expect(sortable.sort()).toEqual(["amount", "interval", "nickname", "status", "token"]);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("maps the columns whose key differs from the stored property (call C4)", () => {
|
|
20
|
+
const { result } = renderHook(() =>
|
|
21
|
+
useStripePriceTableStructure({ data: [], fields: Object.values(StripePriceFields) as any }),
|
|
22
|
+
);
|
|
23
|
+
const columns = result.current.columns as any[];
|
|
24
|
+
const sortFields = Object.fromEntries(
|
|
25
|
+
columns.filter((column) => column.meta?.sortFields).map((column) => [column.id, column.meta.sortFields]),
|
|
26
|
+
);
|
|
27
|
+
expect(sortFields).toEqual({ amount: ["unitAmount"], interval: ["recurringInterval"], status: ["active"] });
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -61,7 +61,7 @@ export const useStripePriceTableStructure: UseTableStructureHook<StripePriceInte
|
|
|
61
61
|
</Link>
|
|
62
62
|
);
|
|
63
63
|
},
|
|
64
|
-
enableSorting:
|
|
64
|
+
enableSorting: true,
|
|
65
65
|
enableHiding: false,
|
|
66
66
|
}),
|
|
67
67
|
[StripePriceFields.amount]: () => ({
|
|
@@ -77,7 +77,8 @@ export const useStripePriceTableStructure: UseTableStructureHook<StripePriceInte
|
|
|
77
77
|
</span>
|
|
78
78
|
);
|
|
79
79
|
},
|
|
80
|
-
|
|
80
|
+
meta: { sortFields: ["unitAmount"] },
|
|
81
|
+
enableSorting: true,
|
|
81
82
|
enableHiding: false,
|
|
82
83
|
}),
|
|
83
84
|
[StripePriceFields.interval]: () => ({
|
|
@@ -89,7 +90,8 @@ export const useStripePriceTableStructure: UseTableStructureHook<StripePriceInte
|
|
|
89
90
|
// Typography role 8 (UI chrome): single-line, plain text-xs.
|
|
90
91
|
return <span className="text-xs">{formatInterval(price, fmt)}</span>;
|
|
91
92
|
},
|
|
92
|
-
|
|
93
|
+
meta: { sortFields: ["recurringInterval"] },
|
|
94
|
+
enableSorting: true,
|
|
93
95
|
enableHiding: false,
|
|
94
96
|
}),
|
|
95
97
|
[StripePriceFields.token]: () => ({
|
|
@@ -100,7 +102,7 @@ export const useStripePriceTableStructure: UseTableStructureHook<StripePriceInte
|
|
|
100
102
|
const price: StripePriceInterface = row.original.jsonApiData;
|
|
101
103
|
return <span className="block text-xs tabular-nums text-end">{price.token ?? ""}</span>;
|
|
102
104
|
},
|
|
103
|
-
enableSorting:
|
|
105
|
+
enableSorting: true,
|
|
104
106
|
enableHiding: false,
|
|
105
107
|
}),
|
|
106
108
|
[StripePriceFields.status]: () => ({
|
|
@@ -123,7 +125,8 @@ export const useStripePriceTableStructure: UseTableStructureHook<StripePriceInte
|
|
|
123
125
|
</span>
|
|
124
126
|
);
|
|
125
127
|
},
|
|
126
|
-
|
|
128
|
+
meta: { sortFields: ["active"] },
|
|
129
|
+
enableSorting: true,
|
|
127
130
|
enableHiding: false,
|
|
128
131
|
}),
|
|
129
132
|
[StripePriceFields.actions]: () => ({
|
|
@@ -31,6 +31,8 @@ export function ProductsList({ fullWidth }: ProductsListProps = {}) {
|
|
|
31
31
|
retriever: (params) => StripeProductService.listProducts(params),
|
|
32
32
|
retrieverParams: {},
|
|
33
33
|
module: Modules.StripeProduct,
|
|
34
|
+
// Display only (call C8): the server's own order when no sort is chosen.
|
|
35
|
+
defaultSort: "name",
|
|
34
36
|
});
|
|
35
37
|
|
|
36
38
|
// Archiving and restoring happen on the detail page, so the list has to learn
|
|
@@ -9,6 +9,8 @@ export class StripeProductService extends AbstractService {
|
|
|
9
9
|
active?: boolean;
|
|
10
10
|
next?: NextRef;
|
|
11
11
|
prev?: PreviousRef;
|
|
12
|
+
/** JSON:API `sort` (`name`, `-name`), forwarded to the API. */
|
|
13
|
+
sort?: string;
|
|
12
14
|
}): Promise<StripeProductInterface[]> {
|
|
13
15
|
const endpoint = new EndpointCreator({
|
|
14
16
|
endpoint: Modules.StripeProduct,
|
|
@@ -18,6 +20,8 @@ export class StripeProductService extends AbstractService {
|
|
|
18
20
|
endpoint.addAdditionalParam("active", params.active.toString());
|
|
19
21
|
}
|
|
20
22
|
|
|
23
|
+
if (params?.sort) endpoint.addAdditionalParam("sort", params?.sort);
|
|
24
|
+
|
|
21
25
|
return this.callApi({
|
|
22
26
|
type: Modules.StripeProduct,
|
|
23
27
|
method: HttpMethod.GET,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { StripeProductFields } from "../../data/stripe-product.fields";
|
|
4
|
+
import { useStripeProductTableStructure } from "../useStripeProductTableStructure";
|
|
5
|
+
|
|
6
|
+
vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key, useLocale: () => "en" }));
|
|
7
|
+
|
|
8
|
+
/** Spec row A23: name, status (active); prices (a count) is not sortable. Only an explicit `enableSorting: true` opts a column in (call C3). */
|
|
9
|
+
describe("useStripeProductTableStructure sorting", () => {
|
|
10
|
+
it("opts in exactly the row's sortable columns", () => {
|
|
11
|
+
const { result } = renderHook(() =>
|
|
12
|
+
useStripeProductTableStructure({ data: [], fields: Object.values(StripeProductFields) as any }),
|
|
13
|
+
);
|
|
14
|
+
const columns = result.current.columns as any[];
|
|
15
|
+
const sortable = columns.filter((column) => column.enableSorting === true).map((column) => column.id);
|
|
16
|
+
expect(sortable.sort()).toEqual(["name", "status"]);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("maps the columns whose key differs from the stored property (call C4)", () => {
|
|
20
|
+
const { result } = renderHook(() =>
|
|
21
|
+
useStripeProductTableStructure({ data: [], fields: Object.values(StripeProductFields) as any }),
|
|
22
|
+
);
|
|
23
|
+
const columns = result.current.columns as any[];
|
|
24
|
+
const sortFields = Object.fromEntries(
|
|
25
|
+
columns.filter((column) => column.meta?.sortFields).map((column) => [column.id, column.meta.sortFields]),
|
|
26
|
+
);
|
|
27
|
+
expect(sortFields).toEqual({ status: ["active"] });
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -35,7 +35,7 @@ export const useStripeProductTableStructure: UseTableStructureHook<StripeProduct
|
|
|
35
35
|
const product: StripeProductInterface = row.original.jsonApiData;
|
|
36
36
|
return <Link href={generateUrl({ page: Modules.StripeProduct, id: product.id })}>{product.name}</Link>;
|
|
37
37
|
},
|
|
38
|
-
enableSorting:
|
|
38
|
+
enableSorting: true,
|
|
39
39
|
enableHiding: false,
|
|
40
40
|
}),
|
|
41
41
|
[StripeProductFields.status]: () => ({
|
|
@@ -50,7 +50,8 @@ export const useStripeProductTableStructure: UseTableStructureHook<StripeProduct
|
|
|
50
50
|
<Badge variant="softGray">{t("billing.admin.products.status.archived")}</Badge>
|
|
51
51
|
);
|
|
52
52
|
},
|
|
53
|
-
|
|
53
|
+
meta: { sortFields: ["active"] },
|
|
54
|
+
enableSorting: true,
|
|
54
55
|
enableHiding: false,
|
|
55
56
|
}),
|
|
56
57
|
[StripeProductFields.prices]: () => ({
|
|
@@ -24,6 +24,8 @@ export function BlogPostsList({ fullWidth }: BlogPostsListProps = {}) {
|
|
|
24
24
|
retriever: (params) => BlogPostService.findMany(params),
|
|
25
25
|
retrieverParams: {},
|
|
26
26
|
module: Modules.BlogPost,
|
|
27
|
+
// Display only (call C8): the server's own order when no sort is chosen.
|
|
28
|
+
defaultSort: "-updatedAt",
|
|
27
29
|
});
|
|
28
30
|
|
|
29
31
|
// Creating navigates to the new post's page (EditorSheet's default without onSuccess).
|
|
@@ -3,11 +3,13 @@ import { BLOG_ACTION } from "../types/blog.constants";
|
|
|
3
3
|
import { BlogPostInput, BlogPostInterface } from "./BlogPostInterface";
|
|
4
4
|
|
|
5
5
|
export class BlogPostService extends AbstractService {
|
|
6
|
-
static async findMany(params: { search?: string; next?: NextRef } = {}): Promise<BlogPostInterface[]> {
|
|
6
|
+
static async findMany(params: { search?: string; next?: NextRef; sort?: string } = {}): Promise<BlogPostInterface[]> {
|
|
7
7
|
const endpoint = new EndpointCreator({ endpoint: Modules.BlogPost });
|
|
8
8
|
|
|
9
9
|
if (params.search) endpoint.addAdditionalParam("search", params.search);
|
|
10
10
|
|
|
11
|
+
if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
|
|
12
|
+
|
|
11
13
|
return this.callApi<BlogPostInterface[]>({
|
|
12
14
|
type: Modules.BlogPost,
|
|
13
15
|
method: HttpMethod.GET,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { BlogPostFields } from "../../data";
|
|
4
|
+
import { useBlogPostTableStructure } from "../useBlogPostTableStructure";
|
|
5
|
+
|
|
6
|
+
vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key, useLocale: () => "en" }));
|
|
7
|
+
vi.mock("../useBlogCategoryNames", () => ({ useBlogCategoryNames: () => () => "" }));
|
|
8
|
+
|
|
9
|
+
/** Spec row A14: status, publishedAt. Only an explicit `enableSorting: true` opts a column in (call C3). */
|
|
10
|
+
describe("useBlogPostTableStructure sorting", () => {
|
|
11
|
+
it("opts in exactly the row's sortable columns", () => {
|
|
12
|
+
const { result } = renderHook(() =>
|
|
13
|
+
useBlogPostTableStructure({ data: [], fields: Object.values(BlogPostFields) as any }),
|
|
14
|
+
);
|
|
15
|
+
const columns = result.current.columns as any[];
|
|
16
|
+
const sortable = columns.filter((column) => column.enableSorting === true).map((column) => column.id);
|
|
17
|
+
expect(sortable.sort()).toEqual(["publishedAt", "status"]);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -62,7 +62,7 @@ export const useBlogPostTableStructure: UseTableStructureHook<BlogPostInterface,
|
|
|
62
62
|
}[post.status];
|
|
63
63
|
return <Badge variant={BLOG_STATUS_BADGE[post.status] ?? "softGray"}>{label ?? post.status}</Badge>;
|
|
64
64
|
},
|
|
65
|
-
enableSorting:
|
|
65
|
+
enableSorting: true,
|
|
66
66
|
enableHiding: false,
|
|
67
67
|
}),
|
|
68
68
|
[BlogPostFields.categories]: () => ({
|
|
@@ -87,7 +87,7 @@ export const useBlogPostTableStructure: UseTableStructureHook<BlogPostInterface,
|
|
|
87
87
|
if (!post.publishedAt) return <span className="text-muted-foreground text-xs">—</span>;
|
|
88
88
|
return <span className="text-xs tabular-nums">{dates.format(post.publishedAt, "dateMedium")}</span>;
|
|
89
89
|
},
|
|
90
|
-
enableSorting:
|
|
90
|
+
enableSorting: true,
|
|
91
91
|
enableHiding: false,
|
|
92
92
|
}),
|
|
93
93
|
[BlogPostFields.languages]: () => ({
|
|
@@ -26,6 +26,8 @@ export function CompaniesList({ fullWidth }: CompaniesListProps = {}) {
|
|
|
26
26
|
retriever: (params) => CompanyService.findMany(params),
|
|
27
27
|
retrieverParams: {},
|
|
28
28
|
module: Modules.Company,
|
|
29
|
+
// Display only (call C8): the server's own order when no sort is chosen.
|
|
30
|
+
defaultSort: "name",
|
|
29
31
|
});
|
|
30
32
|
|
|
31
33
|
const functions: ReactNode[] = [<CompanyEditor key="create-account" />];
|
|
@@ -11,11 +11,13 @@ export class CompanyService extends AbstractService {
|
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
static async findMany(params: { search?: string; next?: NextRef }): Promise<CompanyInterface[]> {
|
|
14
|
+
static async findMany(params: { search?: string; next?: NextRef; sort?: string }): Promise<CompanyInterface[]> {
|
|
15
15
|
const endpoint = new EndpointCreator({ endpoint: Modules.Company });
|
|
16
16
|
|
|
17
17
|
if (params.search) endpoint.addAdditionalParam("search", params.search);
|
|
18
18
|
|
|
19
|
+
if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
|
|
20
|
+
|
|
19
21
|
return this.callApi<CompanyInterface[]>({
|
|
20
22
|
type: Modules.Company,
|
|
21
23
|
method: HttpMethod.GET,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { CompanyFields } from "../../data";
|
|
4
|
+
import { useCompanyTableStructure } from "../useCompanyTableStructure";
|
|
5
|
+
|
|
6
|
+
vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key, useLocale: () => "en" }));
|
|
7
|
+
vi.mock("../../../user/contexts", () => ({ useCurrentUserContext: () => ({ currentUser: undefined }) }));
|
|
8
|
+
|
|
9
|
+
/** Spec row A24: name. Only an explicit `enableSorting: true` opts a column in (call C3). */
|
|
10
|
+
describe("useCompanyTableStructure sorting", () => {
|
|
11
|
+
it("opts in exactly the row's sortable columns", () => {
|
|
12
|
+
const { result } = renderHook(() =>
|
|
13
|
+
useCompanyTableStructure({ data: [], fields: Object.values(CompanyFields) as any }),
|
|
14
|
+
);
|
|
15
|
+
const columns = result.current.columns as any[];
|
|
16
|
+
const sortable = columns.filter((column) => column.enableSorting === true).map((column) => column.id);
|
|
17
|
+
expect(sortable.sort()).toEqual(["name"]);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -8,6 +8,8 @@ export class ContentService extends AbstractService {
|
|
|
8
8
|
fetchAll?: boolean;
|
|
9
9
|
next?: NextRef;
|
|
10
10
|
prev?: PreviousRef;
|
|
11
|
+
/** JSON:API `sort` (`name`, `-name`), forwarded to the API. */
|
|
12
|
+
sort?: string;
|
|
11
13
|
}): Promise<ContentInterface[]> {
|
|
12
14
|
const endpoint = new EndpointCreator({ endpoint: Modules.Content });
|
|
13
15
|
|
|
@@ -21,6 +23,8 @@ export class ContentService extends AbstractService {
|
|
|
21
23
|
if (Modules.Content.inclusions?.lists?.fields) endpoint.limitToFields(Modules.Content.inclusions.lists.fields);
|
|
22
24
|
if (Modules.Content.inclusions?.lists?.types) endpoint.limitToType(Modules.Content.inclusions.lists.types);
|
|
23
25
|
|
|
26
|
+
if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
|
|
27
|
+
|
|
24
28
|
return this.callApi({
|
|
25
29
|
type: Modules.Content,
|
|
26
30
|
method: HttpMethod.GET,
|
|
@@ -29,7 +33,12 @@ export class ContentService extends AbstractService {
|
|
|
29
33
|
});
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
static async findRelevant(params: {
|
|
36
|
+
static async findRelevant(params: {
|
|
37
|
+
id: string;
|
|
38
|
+
next?: NextRef;
|
|
39
|
+
prev?: PreviousRef;
|
|
40
|
+
sort?: string;
|
|
41
|
+
}): Promise<ContentInterface[]> {
|
|
33
42
|
const endpoint: EndpointCreator = new EndpointCreator({
|
|
34
43
|
endpoint: Modules.Content,
|
|
35
44
|
id: params.id,
|
|
@@ -39,6 +48,8 @@ export class ContentService extends AbstractService {
|
|
|
39
48
|
if (Modules.Content.inclusions?.lists?.fields) endpoint.limitToFields(Modules.Content.inclusions.lists.fields);
|
|
40
49
|
if (Modules.Content.inclusions?.lists?.types) endpoint.limitToType(Modules.Content.inclusions.lists.types);
|
|
41
50
|
|
|
51
|
+
if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
|
|
52
|
+
|
|
42
53
|
return this.callApi<ContentInterface[]>({
|
|
43
54
|
type: Modules.Content,
|
|
44
55
|
method: HttpMethod.GET,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
// Loads the core barrel first, as the app does: the hook reaches it through the components barrel,
|
|
4
|
+
// which on its own resolves a model module before its base class (circular import).
|
|
5
|
+
import "../../../../core";
|
|
6
|
+
import { ContentFields } from "../../data";
|
|
7
|
+
import { useContentTableStructure } from "../useContentTableStructure";
|
|
8
|
+
|
|
9
|
+
vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key, useLocale: () => "en" }));
|
|
10
|
+
|
|
11
|
+
/** Spec rows A7, A10: name. Only an explicit `enableSorting: true` opts a column in (call C3). */
|
|
12
|
+
describe("useContentTableStructure sorting", () => {
|
|
13
|
+
it("opts in exactly the row's sortable columns", () => {
|
|
14
|
+
const { result } = renderHook(() =>
|
|
15
|
+
useContentTableStructure({ data: [], fields: Object.values(ContentFields) as any }),
|
|
16
|
+
);
|
|
17
|
+
const columns = result.current.columns as any[];
|
|
18
|
+
const sortable = columns.filter((column) => column.enableSorting === true).map((column) => column.id);
|
|
19
|
+
expect(sortable.sort()).toEqual(["name"]);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -28,6 +28,8 @@ export function EmailAutomationRecipientsList() {
|
|
|
28
28
|
retriever: (params) => EmailAutomationService.findEnrollments(params),
|
|
29
29
|
retrieverParams: { automationId: automation?.id },
|
|
30
30
|
module: Modules.EmailAutomationEnrollment,
|
|
31
|
+
// Display only (call C8): the server's own order when no sort is chosen.
|
|
32
|
+
defaultSort: "-enrolledAt",
|
|
31
33
|
});
|
|
32
34
|
|
|
33
35
|
const handleStatusChange = (value: string | null) => {
|