@carlonicora/nextjs-jsonapi 6.32.2 → 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.
Files changed (227) hide show
  1. package/dist/{BlockNoteEditor-KRNC6EAX.mjs → BlockNoteEditor-DCVT7YHE.mjs} +4 -4
  2. package/dist/{BlockNoteEditor-VXSVY6OY.js → BlockNoteEditor-ZVK5MD34.js} +24 -24
  3. package/dist/{BlockNoteEditor-VXSVY6OY.js.map → BlockNoteEditor-ZVK5MD34.js.map} +1 -1
  4. package/dist/{BlockNoteViewer-RQBYHBQL.js → BlockNoteViewer-2KPGZSTJ.js} +3 -3
  5. package/dist/{BlockNoteViewer-RQBYHBQL.js.map → BlockNoteViewer-2KPGZSTJ.js.map} +1 -1
  6. package/dist/{BlockNoteViewer-NVSR67X5.mjs → BlockNoteViewer-XDAC6AL7.mjs} +2 -2
  7. package/dist/billing/index.js +477 -469
  8. package/dist/billing/index.js.map +1 -1
  9. package/dist/billing/index.mjs +15 -7
  10. package/dist/billing/index.mjs.map +1 -1
  11. package/dist/{chunk-5H25LM2Z.js → chunk-576XHOYH.js} +5 -5
  12. package/dist/{chunk-5H25LM2Z.js.map → chunk-576XHOYH.js.map} +1 -1
  13. package/dist/{chunk-NPGX2GJD.js → chunk-CI64OTRX.js} +98 -75
  14. package/dist/chunk-CI64OTRX.js.map +1 -0
  15. package/dist/{chunk-AEBZ67JJ.mjs → chunk-ETU3AW2X.mjs} +2 -2
  16. package/dist/{chunk-4JVLLBRS.mjs → chunk-GUNH3THR.mjs} +24 -1
  17. package/dist/chunk-GUNH3THR.mjs.map +1 -0
  18. package/dist/{chunk-GPVLFNLB.js → chunk-KH4Y4FU4.js} +1411 -1187
  19. package/dist/chunk-KH4Y4FU4.js.map +1 -0
  20. package/dist/{chunk-PNCO3HMJ.js → chunk-TCAOU6NH.js} +3 -3
  21. package/dist/{chunk-PNCO3HMJ.js.map → chunk-TCAOU6NH.js.map} +1 -1
  22. package/dist/{chunk-4SXHRANW.mjs → chunk-V4NPMUZ2.mjs} +2627 -2403
  23. package/dist/chunk-V4NPMUZ2.mjs.map +1 -0
  24. package/dist/{chunk-AMQFKCAX.mjs → chunk-WPFEPIKP.mjs} +2 -2
  25. package/dist/client/index.js +6 -4
  26. package/dist/client/index.js.map +1 -1
  27. package/dist/client/index.mjs +5 -3
  28. package/dist/components/index.js +12 -4
  29. package/dist/components/index.js.map +1 -1
  30. package/dist/components/index.mjs +11 -3
  31. package/dist/components/tables/ContentListTable.d.ts +14 -0
  32. package/dist/components/tables/ContentListTable.d.ts.map +1 -1
  33. package/dist/components/tables/SortableHeader.d.ts +17 -0
  34. package/dist/components/tables/SortableHeader.d.ts.map +1 -0
  35. package/dist/components/tables/cells/cell.date.d.ts +4 -0
  36. package/dist/components/tables/cells/cell.date.d.ts.map +1 -1
  37. package/dist/components/tables/cells/cell.dateTime.d.ts +4 -0
  38. package/dist/components/tables/cells/cell.dateTime.d.ts.map +1 -1
  39. package/dist/components/tables/cells/cell.link.d.ts +4 -0
  40. package/dist/components/tables/cells/cell.link.d.ts.map +1 -1
  41. package/dist/components/tables/cells/cell.text.d.ts +4 -0
  42. package/dist/components/tables/cells/cell.text.d.ts.map +1 -1
  43. package/dist/components/tables/index.d.ts +2 -0
  44. package/dist/components/tables/index.d.ts.map +1 -1
  45. package/dist/components/tables/sorting.d.ts +24 -0
  46. package/dist/components/tables/sorting.d.ts.map +1 -0
  47. package/dist/contexts/index.js +4 -4
  48. package/dist/contexts/index.mjs +3 -3
  49. package/dist/core/index.js +2 -2
  50. package/dist/core/index.mjs +1 -1
  51. package/dist/email-automations/index.js +120 -115
  52. package/dist/email-automations/index.js.map +1 -1
  53. package/dist/email-automations/index.mjs +18 -13
  54. package/dist/email-automations/index.mjs.map +1 -1
  55. package/dist/features/analytics/index.js +114 -114
  56. package/dist/features/analytics/index.mjs +3 -3
  57. package/dist/features/analytics/server.js +4 -4
  58. package/dist/features/analytics/server.mjs +2 -2
  59. package/dist/features/billing/stripe-coupon/components/lists/CouponCodesList.d.ts.map +1 -1
  60. package/dist/features/billing/stripe-coupon/components/lists/CouponsList.d.ts.map +1 -1
  61. package/dist/features/billing/stripe-coupon/data/stripe-coupon-code.service.d.ts +2 -0
  62. package/dist/features/billing/stripe-coupon/data/stripe-coupon-code.service.d.ts.map +1 -1
  63. package/dist/features/billing/stripe-coupon/data/stripe-coupon.service.d.ts +1 -0
  64. package/dist/features/billing/stripe-coupon/data/stripe-coupon.service.d.ts.map +1 -1
  65. package/dist/features/billing/stripe-coupon/hooks/useStripeCouponCodeTableStructure.d.ts.map +1 -1
  66. package/dist/features/billing/stripe-coupon/hooks/useStripeCouponTableStructure.d.ts.map +1 -1
  67. package/dist/features/billing/stripe-price/components/lists/PricesList.d.ts.map +1 -1
  68. package/dist/features/billing/stripe-price/data/stripe-price.service.d.ts +2 -0
  69. package/dist/features/billing/stripe-price/data/stripe-price.service.d.ts.map +1 -1
  70. package/dist/features/billing/stripe-price/hooks/useStripePriceTableStructure.d.ts.map +1 -1
  71. package/dist/features/billing/stripe-product/components/lists/ProductsList.d.ts.map +1 -1
  72. package/dist/features/billing/stripe-product/data/stripe-product.service.d.ts +2 -0
  73. package/dist/features/billing/stripe-product/data/stripe-product.service.d.ts.map +1 -1
  74. package/dist/features/billing/stripe-product/hooks/useStripeProductTableStructure.d.ts.map +1 -1
  75. package/dist/features/blog/components/admin/BlogPostsList.d.ts.map +1 -1
  76. package/dist/features/blog/data/BlogPostService.d.ts +1 -0
  77. package/dist/features/blog/data/BlogPostService.d.ts.map +1 -1
  78. package/dist/features/blog/index.js +134 -132
  79. package/dist/features/blog/index.js.map +1 -1
  80. package/dist/features/blog/index.mjs +8 -6
  81. package/dist/features/blog/index.mjs.map +1 -1
  82. package/dist/features/blog/server.js +7 -7
  83. package/dist/features/blog/server.mjs +1 -1
  84. package/dist/features/company/components/lists/CompaniesList.d.ts.map +1 -1
  85. package/dist/features/company/data/company.service.d.ts +1 -0
  86. package/dist/features/company/data/company.service.d.ts.map +1 -1
  87. package/dist/features/content/data/content.service.d.ts +3 -0
  88. package/dist/features/content/data/content.service.d.ts.map +1 -1
  89. package/dist/features/email-automation/components/lists/EmailAutomationRecipientsList.d.ts.map +1 -1
  90. package/dist/features/email-automation/components/lists/EmailAutomationsList.d.ts.map +1 -1
  91. package/dist/features/email-automation/data/EmailAutomationService.d.ts +3 -0
  92. package/dist/features/email-automation/data/EmailAutomationService.d.ts.map +1 -1
  93. package/dist/features/email-automation/hooks/useEmailAutomationTableStructure.d.ts.map +1 -1
  94. package/dist/features/email-blast/components/lists/EmailBlastsList.d.ts.map +1 -1
  95. package/dist/features/email-blast/data/EmailBlastService.d.ts +1 -0
  96. package/dist/features/email-blast/data/EmailBlastService.d.ts.map +1 -1
  97. package/dist/features/email-blast/hooks/useEmailBlastTableStructure.d.ts.map +1 -1
  98. package/dist/features/help/index.js +37 -37
  99. package/dist/features/help/index.mjs +3 -3
  100. package/dist/features/how-to/components/lists/HowToList.d.ts.map +1 -1
  101. package/dist/features/how-to/data/HowToService.d.ts +2 -0
  102. package/dist/features/how-to/data/HowToService.d.ts.map +1 -1
  103. package/dist/features/notification/components/lists/NotificationsList.d.ts.map +1 -1
  104. package/dist/features/notification/data/notification.service.d.ts +2 -0
  105. package/dist/features/notification/data/notification.service.d.ts.map +1 -1
  106. package/dist/features/referral/components/containers/ReferralsAdminContainer.d.ts.map +1 -1
  107. package/dist/features/referral/data/ReferralAdminService.d.ts +1 -0
  108. package/dist/features/referral/data/ReferralAdminService.d.ts.map +1 -1
  109. package/dist/features/referral/hooks/useReferralAdminReferralTableStructure.d.ts.map +1 -1
  110. package/dist/features/role/components/lists/RolesList.d.ts.map +1 -1
  111. package/dist/features/role/components/lists/UserRolesList.d.ts.map +1 -1
  112. package/dist/features/role/data/role.service.d.ts +3 -0
  113. package/dist/features/role/data/role.service.d.ts.map +1 -1
  114. package/dist/features/tokenusage/index.js +114 -114
  115. package/dist/features/tokenusage/index.mjs +3 -3
  116. package/dist/features/user/components/lists/AdminUsersList.d.ts.map +1 -1
  117. package/dist/features/user/components/lists/CompanyUsersList.d.ts.map +1 -1
  118. package/dist/features/user/components/lists/PlatformUsersList.d.ts.map +1 -1
  119. package/dist/features/user/components/lists/RoleUsersList.d.ts.map +1 -1
  120. package/dist/features/user/components/lists/UsersListByContentIds.d.ts.map +1 -1
  121. package/dist/features/user/data/user.service.d.ts +11 -0
  122. package/dist/features/user/data/user.service.d.ts.map +1 -1
  123. package/dist/features/waitlist/components/lists/WaitlistList.d.ts.map +1 -1
  124. package/dist/features/waitlist/data/WaitlistService.d.ts +2 -0
  125. package/dist/features/waitlist/data/WaitlistService.d.ts.map +1 -1
  126. package/dist/hooks/index.d.ts +1 -0
  127. package/dist/hooks/index.d.ts.map +1 -1
  128. package/dist/hooks/useDataListRetriever.d.ts +13 -0
  129. package/dist/hooks/useDataListRetriever.d.ts.map +1 -1
  130. package/dist/hooks/useListSortParam.d.ts +23 -0
  131. package/dist/hooks/useListSortParam.d.ts.map +1 -0
  132. package/dist/index.js +3 -3
  133. package/dist/index.mjs +2 -2
  134. package/dist/server/index.js +3 -3
  135. package/dist/server/index.mjs +2 -2
  136. package/package.json +1 -1
  137. package/src/components/tables/ContentListTable.tsx +108 -10
  138. package/src/components/tables/SortableHeader.tsx +43 -0
  139. package/src/components/tables/__tests__/ContentListTable.rendering.spec.tsx +1 -0
  140. package/src/components/tables/__tests__/ContentListTable.sort.spec.tsx +273 -0
  141. package/src/components/tables/__tests__/sorting.spec.ts +61 -0
  142. package/src/components/tables/cells/cell.date.tsx +10 -2
  143. package/src/components/tables/cells/cell.dateTime.tsx +10 -2
  144. package/src/components/tables/cells/cell.link.tsx +6 -1
  145. package/src/components/tables/cells/cell.text.tsx +11 -2
  146. package/src/components/tables/index.ts +2 -0
  147. package/src/components/tables/sorting.ts +41 -0
  148. package/src/features/billing/stripe-coupon/components/lists/CouponCodesList.tsx +2 -0
  149. package/src/features/billing/stripe-coupon/components/lists/CouponsList.tsx +2 -0
  150. package/src/features/billing/stripe-coupon/data/stripe-coupon-code.service.ts +4 -0
  151. package/src/features/billing/stripe-coupon/data/stripe-coupon.service.ts +7 -1
  152. package/src/features/billing/stripe-coupon/hooks/__tests__/useStripeCouponCodeTableStructure.sort.spec.tsx +30 -0
  153. package/src/features/billing/stripe-coupon/hooks/__tests__/useStripeCouponTableStructure.sort.spec.tsx +33 -0
  154. package/src/features/billing/stripe-coupon/hooks/useStripeCouponCodeTableStructure.tsx +5 -4
  155. package/src/features/billing/stripe-coupon/hooks/useStripeCouponTableStructure.tsx +7 -4
  156. package/src/features/billing/stripe-price/components/lists/PricesList.tsx +2 -0
  157. package/src/features/billing/stripe-price/data/stripe-price.service.ts +4 -0
  158. package/src/features/billing/stripe-price/hooks/__tests__/useStripePriceTableStructure.sort.spec.tsx +29 -0
  159. package/src/features/billing/stripe-price/hooks/useStripePriceTableStructure.tsx +8 -5
  160. package/src/features/billing/stripe-product/components/lists/ProductsList.tsx +2 -0
  161. package/src/features/billing/stripe-product/data/stripe-product.service.ts +4 -0
  162. package/src/features/billing/stripe-product/hooks/__tests__/useStripeProductTableStructure.sort.spec.tsx +29 -0
  163. package/src/features/billing/stripe-product/hooks/useStripeProductTableStructure.tsx +3 -2
  164. package/src/features/blog/components/admin/BlogPostsList.tsx +2 -0
  165. package/src/features/blog/data/BlogPostService.ts +3 -1
  166. package/src/features/blog/hooks/__tests__/useBlogPostTableStructure.sort.spec.tsx +19 -0
  167. package/src/features/blog/hooks/useBlogPostTableStructure.tsx +2 -2
  168. package/src/features/company/components/lists/CompaniesList.tsx +2 -0
  169. package/src/features/company/data/company.service.ts +3 -1
  170. package/src/features/company/hooks/__tests__/useCompanyTableStructure.sort.spec.tsx +19 -0
  171. package/src/features/company/hooks/useCompanyTableStructure.tsx +1 -1
  172. package/src/features/content/data/content.service.ts +12 -1
  173. package/src/features/content/hooks/__tests__/useContentTableStructure.sort.spec.tsx +21 -0
  174. package/src/features/content/hooks/useContentTableStructure.tsx +1 -1
  175. package/src/features/email-automation/components/lists/EmailAutomationRecipientsList.tsx +2 -0
  176. package/src/features/email-automation/components/lists/EmailAutomationsList.tsx +2 -0
  177. package/src/features/email-automation/data/EmailAutomationService.ts +9 -1
  178. package/src/features/email-automation/hooks/__tests__/useEmailAutomationEnrollmentTableStructure.sort.spec.tsx +21 -0
  179. package/src/features/email-automation/hooks/__tests__/useEmailAutomationTableStructure.sort.spec.tsx +29 -0
  180. package/src/features/email-automation/hooks/useEmailAutomationEnrollmentTableStructure.tsx +4 -4
  181. package/src/features/email-automation/hooks/useEmailAutomationTableStructure.tsx +5 -4
  182. package/src/features/email-blast/components/lists/EmailBlastsList.tsx +2 -0
  183. package/src/features/email-blast/data/EmailBlastService.ts +5 -1
  184. package/src/features/email-blast/hooks/__tests__/useEmailBlastTableStructure.sort.spec.tsx +29 -0
  185. package/src/features/email-blast/hooks/useEmailBlastTableStructure.tsx +16 -8
  186. package/src/features/how-to/components/lists/HowToList.tsx +2 -0
  187. package/src/features/how-to/data/HowToService.ts +4 -0
  188. package/src/features/how-to/hooks/__tests__/useHowToTableStructure.sort.spec.tsx +18 -0
  189. package/src/features/how-to/hooks/useHowToTableStructure.tsx +1 -1
  190. package/src/features/notification/components/lists/NotificationsList.tsx +2 -0
  191. package/src/features/notification/data/notification.service.ts +4 -0
  192. package/src/features/notification/hooks/__tests__/useNotificationTableStructure.sort.spec.tsx +19 -0
  193. package/src/features/notification/hooks/useNotificationTableStructure.tsx +1 -1
  194. package/src/features/referral/components/containers/ReferralsAdminContainer.tsx +4 -2
  195. package/src/features/referral/data/ReferralAdminService.ts +6 -1
  196. package/src/features/referral/hooks/__tests__/useReferralAdminReferralTableStructure.sort.spec.tsx +35 -0
  197. package/src/features/referral/hooks/useReferralAdminReferralTableStructure.tsx +10 -5
  198. package/src/features/role/components/lists/RolesList.tsx +2 -0
  199. package/src/features/role/components/lists/UserRolesList.tsx +2 -0
  200. package/src/features/role/data/role.service.ts +7 -1
  201. package/src/features/role/hooks/__tests__/useRoleTableStructure.sort.spec.tsx +16 -0
  202. package/src/features/role/hooks/useRoleTableStructure.tsx +2 -2
  203. package/src/features/user/components/lists/AdminUsersList.tsx +2 -0
  204. package/src/features/user/components/lists/CompanyUsersList.tsx +4 -0
  205. package/src/features/user/components/lists/PlatformUsersList.tsx +2 -0
  206. package/src/features/user/components/lists/RoleUsersList.tsx +2 -0
  207. package/src/features/user/components/lists/UsersListByContentIds.tsx +2 -0
  208. package/src/features/user/data/user.service.ts +28 -1
  209. package/src/features/user/hooks/__tests__/useUserTableStructure.sort.spec.tsx +16 -0
  210. package/src/features/user/hooks/useUserTableStructure.tsx +2 -2
  211. package/src/features/waitlist/components/lists/WaitlistList.tsx +2 -0
  212. package/src/features/waitlist/data/WaitlistService.ts +4 -0
  213. package/src/features/waitlist/hooks/__tests__/useWaitlistTableStructure.sort.spec.tsx +17 -0
  214. package/src/features/waitlist/hooks/useWaitlistTableStructure.tsx +3 -3
  215. package/src/hooks/__tests__/useDataListRetriever.sort.spec.tsx +114 -0
  216. package/src/hooks/__tests__/useListSortParam.spec.tsx +91 -0
  217. package/src/hooks/index.ts +1 -0
  218. package/src/hooks/useDataListRetriever.ts +44 -4
  219. package/src/hooks/useListSortParam.ts +53 -0
  220. package/dist/chunk-4JVLLBRS.mjs.map +0 -1
  221. package/dist/chunk-4SXHRANW.mjs.map +0 -1
  222. package/dist/chunk-GPVLFNLB.js.map +0 -1
  223. package/dist/chunk-NPGX2GJD.js.map +0 -1
  224. /package/dist/{BlockNoteEditor-KRNC6EAX.mjs.map → BlockNoteEditor-DCVT7YHE.mjs.map} +0 -0
  225. /package/dist/{BlockNoteViewer-NVSR67X5.mjs.map → BlockNoteViewer-XDAC6AL7.mjs.map} +0 -0
  226. /package/dist/{chunk-AEBZ67JJ.mjs.map → chunk-ETU3AW2X.mjs.map} +0 -0
  227. /package/dist/{chunk-AMQFKCAX.mjs.map → chunk-WPFEPIKP.mjs.map} +0 -0
@@ -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: false,
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
- enableSorting: false,
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: false,
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: false,
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: false,
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
- enableSorting: false,
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
- enableSorting: false,
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
- enableSorting: false,
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,
@@ -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: false,
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
- enableSorting: false,
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
- enableSorting: false,
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: false,
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
- enableSorting: false,
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: false,
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
- enableSorting: false,
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: false,
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: false,
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
+ });
@@ -60,7 +60,7 @@ export const useCompanyTableStructure: UseTableStructureHook<CompanyInterface, C
60
60
  </Link>
61
61
  );
62
62
  },
63
- enableSorting: false,
63
+ enableSorting: true,
64
64
  enableHiding: false,
65
65
  }),
66
66
  [CompanyFields.createdAt]: () =>
@@ -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: { id: string; next?: NextRef; prev?: PreviousRef }): Promise<ContentInterface[]> {
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
+ });
@@ -58,7 +58,7 @@ export const useContentTableStructure = <U extends string = ContentFields>(
58
58
  </Tooltip>
59
59
  );
60
60
  },
61
- enableSorting: false,
61
+ enableSorting: true,
62
62
  enableHiding: false,
63
63
  }),
64
64
  [ContentFields.relevance]: () => ({
@@ -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) => {
@@ -31,6 +31,8 @@ export function EmailAutomationsList({ fullWidth }: EmailAutomationsListProps =
31
31
  retriever: (params) => EmailAutomationService.findMany(params),
32
32
  retrieverParams: {},
33
33
  module: Modules.EmailAutomation,
34
+ // Display only (call C8): the server's own order when no sort is chosen.
35
+ defaultSort: "-updatedAt",
34
36
  });
35
37
 
36
38
  // The trigger column shows the label the app declares, not the stored key.
@@ -8,7 +8,9 @@ import { EmailAutomationInput, EmailAutomationInterface } from "./EmailAutomatio
8
8
  import { EmailAutomationTriggerInterface } from "./EmailAutomationTriggerInterface";
9
9
 
10
10
  export class EmailAutomationService extends AbstractService {
11
- static async findMany(params: { next?: NextRef; previous?: PreviousRef } = {}): Promise<EmailAutomationInterface[]> {
11
+ static async findMany(
12
+ params: { next?: NextRef; previous?: PreviousRef; sort?: string } = {},
13
+ ): Promise<EmailAutomationInterface[]> {
12
14
  const endpoint = new EndpointCreator({ endpoint: Modules.EmailAutomation });
13
15
 
14
16
  if (Modules.EmailAutomation.inclusions?.lists?.fields)
@@ -16,6 +18,8 @@ export class EmailAutomationService extends AbstractService {
16
18
  if (Modules.EmailAutomation.inclusions?.lists?.types)
17
19
  endpoint.limitToType(Modules.EmailAutomation.inclusions.lists.types);
18
20
 
21
+ if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
22
+
19
23
  return this.callApi<EmailAutomationInterface[]>({
20
24
  type: Modules.EmailAutomation,
21
25
  method: HttpMethod.GET,
@@ -90,6 +94,8 @@ export class EmailAutomationService extends AbstractService {
90
94
  status?: string;
91
95
  next?: NextRef;
92
96
  previous?: PreviousRef;
97
+ /** JSON:API `sort` (`name`, `-name`), forwarded to the API. */
98
+ sort?: string;
93
99
  }): Promise<EmailAutomationEnrollmentInterface[]> {
94
100
  const endpoint = new EndpointCreator({
95
101
  endpoint: Modules.EmailAutomation,
@@ -99,6 +105,8 @@ export class EmailAutomationService extends AbstractService {
99
105
 
100
106
  if (params.status) endpoint.addAdditionalParam("status", params.status);
101
107
 
108
+ if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
109
+
102
110
  return this.callApi<EmailAutomationEnrollmentInterface[]>({
103
111
  type: Modules.EmailAutomationEnrollment,
104
112
  method: HttpMethod.GET,
@@ -0,0 +1,21 @@
1
+ import { renderHook } from "@testing-library/react";
2
+ import { describe, expect, it, vi } from "vitest";
3
+ import { EmailAutomationEnrollmentFields } from "../../data/email-automation-enrollment.fields";
4
+ import { useEmailAutomationEnrollmentTableStructure } from "../useEmailAutomationEnrollmentTableStructure";
5
+
6
+ vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key, useLocale: () => "en" }));
7
+
8
+ /** Spec row A4: email, name, status, nextSendAt; the computed step and sent count are not sortable. Only an explicit `enableSorting: true` opts a column in (call C3). */
9
+ describe("useEmailAutomationEnrollmentTableStructure sorting", () => {
10
+ it("opts in exactly the row's sortable columns", () => {
11
+ const { result } = renderHook(() =>
12
+ useEmailAutomationEnrollmentTableStructure({
13
+ data: [],
14
+ fields: Object.values(EmailAutomationEnrollmentFields) as any,
15
+ }),
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(["email", "name", "nextSendAt", "status"]);
20
+ });
21
+ });
@@ -0,0 +1,29 @@
1
+ import { renderHook } from "@testing-library/react";
2
+ import { describe, expect, it, vi } from "vitest";
3
+ import { EmailAutomationFields } from "../../data/email-automation.fields";
4
+ import { useEmailAutomationTableStructure } from "../useEmailAutomationTableStructure";
5
+
6
+ vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key, useLocale: () => "en" }));
7
+
8
+ /** Spec row A5: name, purpose, trigger, status (isActive); the counts stay unsortable. Only an explicit `enableSorting: true` opts a column in (call C3). */
9
+ describe("useEmailAutomationTableStructure sorting", () => {
10
+ it("opts in exactly the row's sortable columns", () => {
11
+ const { result } = renderHook(() =>
12
+ useEmailAutomationTableStructure({ data: [], fields: Object.values(EmailAutomationFields) 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", "purpose", "status", "trigger"]);
17
+ });
18
+
19
+ it("maps the columns whose key differs from the stored property (call C4)", () => {
20
+ const { result } = renderHook(() =>
21
+ useEmailAutomationTableStructure({ data: [], fields: Object.values(EmailAutomationFields) 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: ["isActive"] });
28
+ });
29
+ });
@@ -47,7 +47,7 @@ export const useEmailAutomationEnrollmentTableStructure: UseTableStructureHook<
47
47
  const enrollment: EmailAutomationEnrollmentInterface = row.original.jsonApiData;
48
48
  return <span className="text-sm">{enrollment.email}</span>;
49
49
  },
50
- enableSorting: false,
50
+ enableSorting: true,
51
51
  enableHiding: false,
52
52
  }),
53
53
  [EmailAutomationEnrollmentFields.name]: () => ({
@@ -58,7 +58,7 @@ export const useEmailAutomationEnrollmentTableStructure: UseTableStructureHook<
58
58
  const enrollment: EmailAutomationEnrollmentInterface = row.original.jsonApiData;
59
59
  return enrollment.name ? <span className="text-sm">{enrollment.name}</span> : empty;
60
60
  },
61
- enableSorting: false,
61
+ enableSorting: true,
62
62
  enableHiding: false,
63
63
  }),
64
64
  [EmailAutomationEnrollmentFields.status]: () => ({
@@ -76,7 +76,7 @@ export const useEmailAutomationEnrollmentTableStructure: UseTableStructureHook<
76
76
  </Badge>
77
77
  );
78
78
  },
79
- enableSorting: false,
79
+ enableSorting: true,
80
80
  enableHiding: false,
81
81
  }),
82
82
  [EmailAutomationEnrollmentFields.currentStep]: () => ({
@@ -103,7 +103,7 @@ export const useEmailAutomationEnrollmentTableStructure: UseTableStructureHook<
103
103
  </span>
104
104
  );
105
105
  },
106
- enableSorting: false,
106
+ enableSorting: true,
107
107
  enableHiding: false,
108
108
  }),
109
109
  [EmailAutomationEnrollmentFields.sentCount]: () => ({
@@ -59,7 +59,7 @@ export const useEmailAutomationTableStructure: UseTableStructureHook<
59
59
  const automation: EmailAutomationInterface = row.original.jsonApiData;
60
60
  return <Link href={generateUrl({ page: Modules.EmailAutomation, id: automation.id })}>{automation.name}</Link>;
61
61
  },
62
- enableSorting: false,
62
+ enableSorting: true,
63
63
  enableHiding: false,
64
64
  }),
65
65
  [EmailAutomationFields.purpose]: () => ({
@@ -78,7 +78,7 @@ export const useEmailAutomationTableStructure: UseTableStructureHook<
78
78
  </Badge>
79
79
  );
80
80
  },
81
- enableSorting: false,
81
+ enableSorting: true,
82
82
  enableHiding: false,
83
83
  }),
84
84
  [EmailAutomationFields.trigger]: () => ({
@@ -90,7 +90,7 @@ export const useEmailAutomationTableStructure: UseTableStructureHook<
90
90
  if (!automation.trigger) return <span className="text-muted-foreground text-xs">—</span>;
91
91
  return <span className="text-sm">{triggerLabels[automation.trigger] ?? automation.trigger}</span>;
92
92
  },
93
- enableSorting: false,
93
+ enableSorting: true,
94
94
  enableHiding: false,
95
95
  }),
96
96
  [EmailAutomationFields.status]: () => ({
@@ -105,7 +105,8 @@ export const useEmailAutomationTableStructure: UseTableStructureHook<
105
105
  <Badge variant="softGray">{t("emailAutomations.list.status.off")}</Badge>
106
106
  );
107
107
  },
108
- enableSorting: false,
108
+ meta: { sortFields: ["isActive"] },
109
+ enableSorting: true,
109
110
  enableHiding: false,
110
111
  }),
111
112
  [EmailAutomationFields.steps]: () =>