@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.
- package/dist/{BlockNoteEditor-KRNC6EAX.mjs → BlockNoteEditor-DCVT7YHE.mjs} +4 -4
- package/dist/{BlockNoteEditor-VXSVY6OY.js → BlockNoteEditor-ZVK5MD34.js} +24 -24
- package/dist/{BlockNoteEditor-VXSVY6OY.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-GPVLFNLB.js → chunk-KH4Y4FU4.js} +1411 -1187
- 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-4SXHRANW.mjs → chunk-V4NPMUZ2.mjs} +2627 -2403
- 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/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/dist/chunk-4JVLLBRS.mjs.map +0 -1
- package/dist/chunk-4SXHRANW.mjs.map +0 -1
- package/dist/chunk-GPVLFNLB.js.map +0 -1
- package/dist/chunk-NPGX2GJD.js.map +0 -1
- /package/dist/{BlockNoteEditor-KRNC6EAX.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
|
@@ -32,6 +32,8 @@ export function EmailBlastsList({ fullWidth }: EmailBlastsListProps = {}) {
|
|
|
32
32
|
retriever: (params) => EmailBlastService.findMany(params),
|
|
33
33
|
retrieverParams: {},
|
|
34
34
|
module: Modules.EmailBlast,
|
|
35
|
+
// Display only (call C8): the server's own order when no sort is chosen.
|
|
36
|
+
defaultSort: "-updatedAt",
|
|
35
37
|
});
|
|
36
38
|
|
|
37
39
|
const functions: ReactNode[] = [<EmailBlastEditor key="create-email-blast" />];
|
|
@@ -4,13 +4,17 @@ import { EmailBlastAudienceInterface } from "./EmailBlastAudienceInterface";
|
|
|
4
4
|
import { EmailBlastInput, EmailBlastInterface } from "./EmailBlastInterface";
|
|
5
5
|
|
|
6
6
|
export class EmailBlastService extends AbstractService {
|
|
7
|
-
static async findMany(
|
|
7
|
+
static async findMany(
|
|
8
|
+
params: { next?: NextRef; previous?: PreviousRef; sort?: string } = {},
|
|
9
|
+
): Promise<EmailBlastInterface[]> {
|
|
8
10
|
const endpoint = new EndpointCreator({ endpoint: Modules.EmailBlast });
|
|
9
11
|
|
|
10
12
|
if (Modules.EmailBlast.inclusions?.lists?.fields)
|
|
11
13
|
endpoint.limitToFields(Modules.EmailBlast.inclusions.lists.fields);
|
|
12
14
|
if (Modules.EmailBlast.inclusions?.lists?.types) endpoint.limitToType(Modules.EmailBlast.inclusions.lists.types);
|
|
13
15
|
|
|
16
|
+
if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
|
|
17
|
+
|
|
14
18
|
return this.callApi<EmailBlastInterface[]>({
|
|
15
19
|
type: Modules.EmailBlast,
|
|
16
20
|
method: HttpMethod.GET,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { EmailBlastFields } from "../../data/email-blast.fields";
|
|
4
|
+
import { useEmailBlastTableStructure } from "../useEmailBlastTableStructure";
|
|
5
|
+
|
|
6
|
+
vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key, useLocale: () => "en" }));
|
|
7
|
+
|
|
8
|
+
/** Spec row A17: every column, the counts mapped to their stored property. Only an explicit `enableSorting: true` opts a column in (call C3). */
|
|
9
|
+
describe("useEmailBlastTableStructure sorting", () => {
|
|
10
|
+
it("opts in exactly the row's sortable columns", () => {
|
|
11
|
+
const { result } = renderHook(() =>
|
|
12
|
+
useEmailBlastTableStructure({ data: [], fields: Object.values(EmailBlastFields) 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(["failed", "purpose", "sent", "sentAt", "status", "subject", "unsubscribed"]);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("maps the columns whose key differs from the stored property (call C4)", () => {
|
|
20
|
+
const { result } = renderHook(() =>
|
|
21
|
+
useEmailBlastTableStructure({ data: [], fields: Object.values(EmailBlastFields) 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({ sent: ["sentCount"], failed: ["failedCount"], unsubscribed: ["unsubscribedCount"] });
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -33,7 +33,13 @@ export const useEmailBlastTableStructure: UseTableStructureHook<EmailBlastInterf
|
|
|
33
33
|
|
|
34
34
|
// Typography role 15 (numeric): tabular-nums, end-aligned. NEVER
|
|
35
35
|
// font-mono — no monospace font is loaded in these apps.
|
|
36
|
-
|
|
36
|
+
// `sortField` is the stored count the column shows (its id differs from the property).
|
|
37
|
+
const numericColumn = (
|
|
38
|
+
field: EmailBlastFields,
|
|
39
|
+
header: string,
|
|
40
|
+
read: (b: EmailBlastInterface) => number,
|
|
41
|
+
sortField: string,
|
|
42
|
+
) => ({
|
|
37
43
|
id: field,
|
|
38
44
|
accessorKey: field,
|
|
39
45
|
header: header,
|
|
@@ -41,7 +47,8 @@ export const useEmailBlastTableStructure: UseTableStructureHook<EmailBlastInterf
|
|
|
41
47
|
const blast: EmailBlastInterface = row.original.jsonApiData;
|
|
42
48
|
return <span className="block text-xs tabular-nums text-end">{read(blast) ?? 0}</span>;
|
|
43
49
|
},
|
|
44
|
-
|
|
50
|
+
meta: { sortFields: [sortField] },
|
|
51
|
+
enableSorting: true,
|
|
45
52
|
enableHiding: false,
|
|
46
53
|
});
|
|
47
54
|
|
|
@@ -54,7 +61,7 @@ export const useEmailBlastTableStructure: UseTableStructureHook<EmailBlastInterf
|
|
|
54
61
|
const blast: EmailBlastInterface = row.original.jsonApiData;
|
|
55
62
|
return <Link href={generateUrl({ page: Modules.EmailBlast, id: blast.id })}>{blast.subject}</Link>;
|
|
56
63
|
},
|
|
57
|
-
enableSorting:
|
|
64
|
+
enableSorting: true,
|
|
58
65
|
enableHiding: false,
|
|
59
66
|
}),
|
|
60
67
|
[EmailBlastFields.purpose]: () => ({
|
|
@@ -69,7 +76,7 @@ export const useEmailBlastTableStructure: UseTableStructureHook<EmailBlastInterf
|
|
|
69
76
|
<Badge variant="softGray">{t("emailPurpose.marketing")}</Badge>
|
|
70
77
|
);
|
|
71
78
|
},
|
|
72
|
-
enableSorting:
|
|
79
|
+
enableSorting: true,
|
|
73
80
|
enableHiding: false,
|
|
74
81
|
}),
|
|
75
82
|
[EmailBlastFields.status]: () => ({
|
|
@@ -80,7 +87,7 @@ export const useEmailBlastTableStructure: UseTableStructureHook<EmailBlastInterf
|
|
|
80
87
|
const blast: EmailBlastInterface = row.original.jsonApiData;
|
|
81
88
|
return <Badge variant={STATUS_VARIANTS[blast.status]}>{t(`emailBlasts.list.status.${blast.status}`)}</Badge>;
|
|
82
89
|
},
|
|
83
|
-
enableSorting:
|
|
90
|
+
enableSorting: true,
|
|
84
91
|
enableHiding: false,
|
|
85
92
|
}),
|
|
86
93
|
[EmailBlastFields.sentAt]: () => ({
|
|
@@ -92,18 +99,19 @@ export const useEmailBlastTableStructure: UseTableStructureHook<EmailBlastInterf
|
|
|
92
99
|
if (!blast.sentAt) return <span className="text-muted-foreground text-xs">—</span>;
|
|
93
100
|
return <span className="text-sm">{dates.format(blast.sentAt, "date")}</span>;
|
|
94
101
|
},
|
|
95
|
-
enableSorting:
|
|
102
|
+
enableSorting: true,
|
|
96
103
|
enableHiding: false,
|
|
97
104
|
}),
|
|
98
105
|
[EmailBlastFields.sent]: () =>
|
|
99
|
-
numericColumn(EmailBlastFields.sent, t("emailBlasts.list.columns.sent"), (b) => b.sentCount),
|
|
106
|
+
numericColumn(EmailBlastFields.sent, t("emailBlasts.list.columns.sent"), (b) => b.sentCount, "sentCount"),
|
|
100
107
|
[EmailBlastFields.failed]: () =>
|
|
101
|
-
numericColumn(EmailBlastFields.failed, t("emailBlasts.list.columns.failed"), (b) => b.failedCount),
|
|
108
|
+
numericColumn(EmailBlastFields.failed, t("emailBlasts.list.columns.failed"), (b) => b.failedCount, "failedCount"),
|
|
102
109
|
[EmailBlastFields.unsubscribed]: () =>
|
|
103
110
|
numericColumn(
|
|
104
111
|
EmailBlastFields.unsubscribed,
|
|
105
112
|
t("emailBlasts.list.columns.unsubscribed"),
|
|
106
113
|
(b) => b.unsubscribedCount,
|
|
114
|
+
"unsubscribedCount",
|
|
107
115
|
),
|
|
108
116
|
};
|
|
109
117
|
|
|
@@ -25,6 +25,8 @@ export default function HowToList({ fullWidth, extraFunctions }: HowToListProps)
|
|
|
25
25
|
module: Modules.HowTo,
|
|
26
26
|
retriever: (params) => HowToService.findMany(params),
|
|
27
27
|
retrieverParams: {},
|
|
28
|
+
// Display only (call C8): the server's own order when no sort is chosen.
|
|
29
|
+
defaultSort: "-updatedAt",
|
|
28
30
|
});
|
|
29
31
|
|
|
30
32
|
const functions: ReactNode[] = [
|
|
@@ -16,6 +16,8 @@ export class HowToService extends AbstractService {
|
|
|
16
16
|
fetchAll?: boolean;
|
|
17
17
|
next?: NextRef;
|
|
18
18
|
prev?: PreviousRef;
|
|
19
|
+
/** JSON:API `sort` (`name`, `-name`), forwarded to the API. */
|
|
20
|
+
sort?: string;
|
|
19
21
|
} = {},
|
|
20
22
|
): Promise<HowToInterface[]> {
|
|
21
23
|
const endpoint = new EndpointCreator({ endpoint: Modules.HowTo });
|
|
@@ -25,6 +27,8 @@ export class HowToService extends AbstractService {
|
|
|
25
27
|
if (Modules.HowTo.inclusions?.lists?.fields) endpoint.limitToFields(Modules.HowTo.inclusions.lists.fields);
|
|
26
28
|
if (Modules.HowTo.inclusions?.lists?.types) endpoint.limitToType(Modules.HowTo.inclusions.lists.types);
|
|
27
29
|
|
|
30
|
+
if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
|
|
31
|
+
|
|
28
32
|
return this.callApi({
|
|
29
33
|
type: Modules.HowTo,
|
|
30
34
|
method: HttpMethod.GET,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { HowToFields } from "../../data/HowToFields";
|
|
4
|
+
import { useHowToTableStructure } from "../useHowToTableStructure";
|
|
5
|
+
|
|
6
|
+
vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key, useLocale: () => "en" }));
|
|
7
|
+
|
|
8
|
+
/** Spec row A19: name. Only an explicit `enableSorting: true` opts a column in (call C3). */
|
|
9
|
+
describe("useHowToTableStructure sorting", () => {
|
|
10
|
+
it("opts in exactly the row's sortable columns", () => {
|
|
11
|
+
const { result } = renderHook(() =>
|
|
12
|
+
useHowToTableStructure({ data: [], fields: Object.values(HowToFields) 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"]);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -43,7 +43,7 @@ export const useHowToTableStructure: UseTableStructureHook<HowToInterface, HowTo
|
|
|
43
43
|
const howTo: HowToInterface = row.original.jsonApiData;
|
|
44
44
|
return <Link href={generateUrl({ page: Modules.HowTo, id: howTo.id })}>{howTo.name}</Link>;
|
|
45
45
|
},
|
|
46
|
-
enableSorting:
|
|
46
|
+
enableSorting: true,
|
|
47
47
|
enableHiding: false,
|
|
48
48
|
}),
|
|
49
49
|
[HowToFields.description]: () => ({
|
|
@@ -29,6 +29,8 @@ export function NotificationsList({ archived, headerControl, fullWidth }: Notifi
|
|
|
29
29
|
module: Modules.Notification,
|
|
30
30
|
retriever: (params) => NotificationService.findMany(params),
|
|
31
31
|
retrieverParams: { isArchived: archived },
|
|
32
|
+
// Display only (call C8): the server's own order when no sort is chosen.
|
|
33
|
+
defaultSort: "-createdAt",
|
|
32
34
|
});
|
|
33
35
|
|
|
34
36
|
/* Mark read what this list ACTUALLY SHOWS, on every page. Reading the shared
|
|
@@ -6,11 +6,15 @@ export class NotificationService extends AbstractService {
|
|
|
6
6
|
isArchived?: boolean;
|
|
7
7
|
next?: NextRef;
|
|
8
8
|
suppressGlobalError?: boolean;
|
|
9
|
+
/** JSON:API `sort` (`name`, `-name`), forwarded to the API. */
|
|
10
|
+
sort?: string;
|
|
9
11
|
}): Promise<NotificationInterface[]> {
|
|
10
12
|
const endpoint = new EndpointCreator({ endpoint: Modules.Notification });
|
|
11
13
|
|
|
12
14
|
if (params.isArchived) endpoint.addAdditionalParam("isArchived", "true");
|
|
13
15
|
|
|
16
|
+
if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
|
|
17
|
+
|
|
14
18
|
return this.callApi<NotificationInterface[]>({
|
|
15
19
|
type: Modules.Notification,
|
|
16
20
|
method: HttpMethod.GET,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { NotificationFields } from "../../data/notification.fields";
|
|
4
|
+
import { useNotificationTableStructure } from "../useNotificationTableStructure";
|
|
5
|
+
|
|
6
|
+
vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key, useLocale: () => "en" }));
|
|
7
|
+
vi.mock("../../contexts/NotificationContext", () => ({ useNotificationContext: () => ({}) }));
|
|
8
|
+
|
|
9
|
+
/** Spec row A6: createdAt. Only an explicit `enableSorting: true` opts a column in (call C3). */
|
|
10
|
+
describe("useNotificationTableStructure sorting", () => {
|
|
11
|
+
it("opts in exactly the row's sortable columns", () => {
|
|
12
|
+
const { result } = renderHook(() =>
|
|
13
|
+
useNotificationTableStructure({ data: [], fields: Object.values(NotificationFields) 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(["createdAt"]);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -103,9 +103,11 @@ export function ReferralsAdminContainer() {
|
|
|
103
103
|
|
|
104
104
|
const referrals: DataListRetriever<ReferralAdminReferralInterface> = useDataListRetriever({
|
|
105
105
|
module: Modules.ReferralAdminReferral,
|
|
106
|
-
retriever: (params: { period: ReferralAdminPeriod }) =>
|
|
107
|
-
ReferralAdminService.getReferrals({ period: params.period }),
|
|
106
|
+
retriever: (params: { period: ReferralAdminPeriod; sort?: string }) =>
|
|
107
|
+
ReferralAdminService.getReferrals({ period: params.period, sort: params.sort }),
|
|
108
108
|
retrieverParams: { period },
|
|
109
|
+
// Display only (call C8): the server's own order when no sort is chosen.
|
|
110
|
+
defaultSort: "-referredAt",
|
|
109
111
|
});
|
|
110
112
|
|
|
111
113
|
useRefetchOnChange(referrers as DataListRetriever<unknown>, "period", period);
|
|
@@ -38,10 +38,15 @@ export class ReferralAdminService extends AbstractService {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/** Every referral in the period, newest first. */
|
|
41
|
-
static async getReferrals(params: {
|
|
41
|
+
static async getReferrals(params: {
|
|
42
|
+
period: ReferralAdminPeriod;
|
|
43
|
+
sort?: string;
|
|
44
|
+
}): Promise<ReferralAdminReferralInterface[]> {
|
|
42
45
|
const endpoint = new EndpointCreator({ endpoint: Modules.ReferralAdminReferral });
|
|
43
46
|
endpoint.addAdditionalParam("period", params.period);
|
|
44
47
|
|
|
48
|
+
if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
|
|
49
|
+
|
|
45
50
|
return this.callApi<ReferralAdminReferralInterface[]>({
|
|
46
51
|
type: Modules.ReferralAdminReferral,
|
|
47
52
|
method: HttpMethod.GET,
|
package/src/features/referral/hooks/__tests__/useReferralAdminReferralTableStructure.sort.spec.tsx
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
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 "../../../../components";
|
|
7
|
+
import {
|
|
8
|
+
ReferralAdminReferralFields,
|
|
9
|
+
useReferralAdminReferralTableStructure,
|
|
10
|
+
} from "../useReferralAdminReferralTableStructure";
|
|
11
|
+
|
|
12
|
+
vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key, useLocale: () => "en" }));
|
|
13
|
+
|
|
14
|
+
/** Spec row A3 (Referrals): source, status, referredAt, tokensAwarded. Only an explicit `enableSorting: true` opts a column in (call C3). */
|
|
15
|
+
describe("useReferralAdminReferralTableStructure sorting", () => {
|
|
16
|
+
it("opts in exactly the row's sortable columns", () => {
|
|
17
|
+
const { result } = renderHook(() =>
|
|
18
|
+
useReferralAdminReferralTableStructure({ data: [], fields: Object.values(ReferralAdminReferralFields) as any }),
|
|
19
|
+
);
|
|
20
|
+
const columns = result.current.columns as any[];
|
|
21
|
+
const sortable = columns.filter((column) => column.enableSorting === true).map((column) => column.id);
|
|
22
|
+
expect(sortable.sort()).toEqual(["credits", "date", "source", "status"]);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("maps the columns whose key differs from the stored property (call C4)", () => {
|
|
26
|
+
const { result } = renderHook(() =>
|
|
27
|
+
useReferralAdminReferralTableStructure({ data: [], fields: Object.values(ReferralAdminReferralFields) as any }),
|
|
28
|
+
);
|
|
29
|
+
const columns = result.current.columns as any[];
|
|
30
|
+
const sortFields = Object.fromEntries(
|
|
31
|
+
columns.filter((column) => column.meta?.sortFields).map((column) => [column.id, column.meta.sortFields]),
|
|
32
|
+
);
|
|
33
|
+
expect(sortFields).toEqual({ date: ["referredAt"], credits: ["tokensAwarded"] });
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -81,11 +81,16 @@ export const useReferralAdminReferralTableStructure: UseTableStructureHook<
|
|
|
81
81
|
// A referral written before the source was recorded: unknown, not guessed.
|
|
82
82
|
return <span className="text-muted-foreground text-xs">—</span>;
|
|
83
83
|
},
|
|
84
|
-
enableSorting:
|
|
84
|
+
enableSorting: true,
|
|
85
85
|
enableHiding: false,
|
|
86
86
|
}),
|
|
87
87
|
[ReferralAdminReferralFields.date]: () =>
|
|
88
|
-
cellDate({
|
|
88
|
+
cellDate({
|
|
89
|
+
name: ReferralAdminReferralFields.date,
|
|
90
|
+
title: t("referral.admin.referrals.columns.date"),
|
|
91
|
+
sortable: true,
|
|
92
|
+
sortFields: ["referredAt"],
|
|
93
|
+
}),
|
|
89
94
|
[ReferralAdminReferralFields.status]: () => ({
|
|
90
95
|
id: ReferralAdminReferralFields.status,
|
|
91
96
|
header: t("referral.admin.referrals.columns.status"),
|
|
@@ -95,7 +100,7 @@ export const useReferralAdminReferralTableStructure: UseTableStructureHook<
|
|
|
95
100
|
) : (
|
|
96
101
|
<Badge variant="softYellow">{t("referral.admin.status.pending")}</Badge>
|
|
97
102
|
),
|
|
98
|
-
enableSorting:
|
|
103
|
+
enableSorting: true,
|
|
99
104
|
enableHiding: false,
|
|
100
105
|
}),
|
|
101
106
|
[ReferralAdminReferralFields.credits]: () => ({
|
|
@@ -109,8 +114,8 @@ export const useReferralAdminReferralTableStructure: UseTableStructureHook<
|
|
|
109
114
|
const award = decimal(entry.tokensAwarded, 0);
|
|
110
115
|
return <span className="block text-xs tabular-nums text-end">{`${award} + ${award}`}</span>;
|
|
111
116
|
},
|
|
112
|
-
meta: { className: "text-end" },
|
|
113
|
-
enableSorting:
|
|
117
|
+
meta: { className: "text-end", sortFields: ["tokensAwarded"] },
|
|
118
|
+
enableSorting: true,
|
|
114
119
|
enableHiding: false,
|
|
115
120
|
}),
|
|
116
121
|
};
|
|
@@ -15,6 +15,8 @@ export function RolesList() {
|
|
|
15
15
|
retriever: (params) => RoleService.findAllRoles(params),
|
|
16
16
|
retrieverParams: {},
|
|
17
17
|
module: Modules.Role,
|
|
18
|
+
// Display only (call C8): the server's own order when no sort is chosen.
|
|
19
|
+
defaultSort: "name",
|
|
18
20
|
});
|
|
19
21
|
|
|
20
22
|
return (
|
|
@@ -19,6 +19,8 @@ export function UserRolesList({ user }: UserRolesListProps) {
|
|
|
19
19
|
retriever: (params) => RoleService.findAllRolesByUser(params),
|
|
20
20
|
retrieverParams: { userId: user.id },
|
|
21
21
|
module: Modules.Role,
|
|
22
|
+
// Display only (call C8): the server's own order when no sort is chosen.
|
|
23
|
+
defaultSort: "name",
|
|
22
24
|
});
|
|
23
25
|
|
|
24
26
|
return (
|
|
@@ -10,11 +10,13 @@ export class RoleService extends AbstractService {
|
|
|
10
10
|
});
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
static async findAllRoles(params: { search?: string; next?: NextRef }): Promise<RoleInterface[]> {
|
|
13
|
+
static async findAllRoles(params: { search?: string; next?: NextRef; sort?: string }): Promise<RoleInterface[]> {
|
|
14
14
|
const endpoint = new EndpointCreator({ endpoint: Modules.Role });
|
|
15
15
|
|
|
16
16
|
if (params.search) endpoint.addAdditionalParam("search", params.search);
|
|
17
17
|
|
|
18
|
+
if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
|
|
19
|
+
|
|
18
20
|
return this.callApi({
|
|
19
21
|
type: Modules.Role,
|
|
20
22
|
method: HttpMethod.GET,
|
|
@@ -28,11 +30,15 @@ export class RoleService extends AbstractService {
|
|
|
28
30
|
search?: string;
|
|
29
31
|
next?: NextRef;
|
|
30
32
|
prev?: PreviousRef;
|
|
33
|
+
/** JSON:API `sort` (`name`, `-name`), forwarded to the API. */
|
|
34
|
+
sort?: string;
|
|
31
35
|
}): Promise<RoleInterface[]> {
|
|
32
36
|
const endpoint = new EndpointCreator({ endpoint: Modules.User, id: params.userId, childEndpoint: Modules.Role });
|
|
33
37
|
|
|
34
38
|
if (params.search) endpoint.addAdditionalParam("search", params.search);
|
|
35
39
|
|
|
40
|
+
if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
|
|
41
|
+
|
|
36
42
|
return this.callApi({
|
|
37
43
|
type: Modules.Role,
|
|
38
44
|
method: HttpMethod.GET,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { RoleFields } from "../../data";
|
|
4
|
+
import { useRoleTableStructure } from "../useRoleTableStructure";
|
|
5
|
+
|
|
6
|
+
vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key, useLocale: () => "en" }));
|
|
7
|
+
|
|
8
|
+
/** Spec rows A25, A26: name, description. Only an explicit `enableSorting: true` opts a column in (call C3). */
|
|
9
|
+
describe("useRoleTableStructure sorting", () => {
|
|
10
|
+
it("opts in exactly the row's sortable columns", () => {
|
|
11
|
+
const { result } = renderHook(() => useRoleTableStructure({ data: [], fields: Object.values(RoleFields) as any }));
|
|
12
|
+
const columns = result.current.columns as any[];
|
|
13
|
+
const sortable = columns.filter((column) => column.enableSorting === true).map((column) => column.id);
|
|
14
|
+
expect(sortable.sort()).toEqual(["description", "name"]);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -42,7 +42,7 @@ export const useRoleTableStructure: UseTableStructureHook<RoleInterface, RoleFie
|
|
|
42
42
|
const role = row.original.jsonApiData as RoleInterface;
|
|
43
43
|
return <Link href={generateUrl({ page: Modules.Role, id: role.id })}>{row.getValue("name")}</Link>;
|
|
44
44
|
},
|
|
45
|
-
enableSorting:
|
|
45
|
+
enableSorting: true,
|
|
46
46
|
enableHiding: false,
|
|
47
47
|
}),
|
|
48
48
|
[RoleFields.description]: () => ({
|
|
@@ -50,7 +50,7 @@ export const useRoleTableStructure: UseTableStructureHook<RoleInterface, RoleFie
|
|
|
50
50
|
accessorKey: "description",
|
|
51
51
|
header: t(`role.fields.description.label`),
|
|
52
52
|
cell: ({ row }: { row: Row<TableContent<RoleInterface>> }) => <>{row.getValue("description")}</>,
|
|
53
|
-
enableSorting:
|
|
53
|
+
enableSorting: true,
|
|
54
54
|
enableHiding: false,
|
|
55
55
|
}),
|
|
56
56
|
[RoleFields.createdAt]: () =>
|
|
@@ -21,6 +21,8 @@ function AdminUsersListInternal({ company }: AdminUsersListProps) {
|
|
|
21
21
|
retriever: (params) => UserService.findManyForAmin(params),
|
|
22
22
|
retrieverParams: { companyId: company.id },
|
|
23
23
|
module: Modules.User,
|
|
24
|
+
// Display only (call C8): the server's own order when no sort is chosen.
|
|
25
|
+
defaultSort: "name",
|
|
24
26
|
});
|
|
25
27
|
|
|
26
28
|
return (
|
|
@@ -24,6 +24,10 @@ export function CompanyUsersList({ isDeleted, fullWidth }: CompanyUsersListProps
|
|
|
24
24
|
retriever: (params) => UserService.findAllUsers(params),
|
|
25
25
|
retrieverParams: { companyId: company?.id, isDeleted: isDeleted },
|
|
26
26
|
module: Modules.User,
|
|
27
|
+
// The active and deleted lists can share a page (AllUsersListContainer tabs): each keeps its own sort.
|
|
28
|
+
sortKey: isDeleted ? `${Modules.User.name}-deleted` : undefined,
|
|
29
|
+
// Display only (call C8): the server's own order when no sort is chosen.
|
|
30
|
+
defaultSort: "name",
|
|
27
31
|
}) as DataListRetriever<UserInterface>;
|
|
28
32
|
|
|
29
33
|
useEffect(() => {
|
|
@@ -30,6 +30,8 @@ export function PlatformUsersList({ includeDeleted, fullWidth }: PlatformUsersLi
|
|
|
30
30
|
retriever: (params) => UserService.findMany(params),
|
|
31
31
|
retrieverParams: { includeDeleted: includeDeleted },
|
|
32
32
|
module: Modules.User,
|
|
33
|
+
// Display only (call C8): the server's own order when no sort is chosen.
|
|
34
|
+
defaultSort: "name",
|
|
33
35
|
}) as DataListRetriever<UserInterface>;
|
|
34
36
|
|
|
35
37
|
return (
|
|
@@ -19,6 +19,8 @@ export function RoleUsersList({ role }: RoleUsersListProps) {
|
|
|
19
19
|
retriever: (params) => UserService.findAllUsersByRole(params),
|
|
20
20
|
retrieverParams: { roleId: role.id },
|
|
21
21
|
module: Modules.User,
|
|
22
|
+
// Display only (call C8): the server's own order when no sort is chosen.
|
|
23
|
+
defaultSort: "name",
|
|
22
24
|
}) as DataListRetriever<UserInterface>;
|
|
23
25
|
|
|
24
26
|
return (
|
|
@@ -18,6 +18,8 @@ export function UsersListByContentIds({ contentIds }: UsersListByContentIdsProps
|
|
|
18
18
|
module: Modules.User,
|
|
19
19
|
retriever: (params) => UserService.findManyByContentIds(params),
|
|
20
20
|
retrieverParams: { contentIds: contentIds },
|
|
21
|
+
// Display only (call C8): the server's own order when no sort is chosen.
|
|
22
|
+
defaultSort: "name",
|
|
21
23
|
});
|
|
22
24
|
|
|
23
25
|
return (
|
|
@@ -33,6 +33,8 @@ export class UserService extends AbstractService {
|
|
|
33
33
|
includeDeleted?: boolean;
|
|
34
34
|
next?: NextRef;
|
|
35
35
|
prev?: PreviousRef;
|
|
36
|
+
/** JSON:API `sort` (`name`, `-name`), forwarded to the API. */
|
|
37
|
+
sort?: string;
|
|
36
38
|
}): Promise<UserInterface[]> {
|
|
37
39
|
const endpoint = new EndpointCreator({ endpoint: Modules.User });
|
|
38
40
|
|
|
@@ -42,6 +44,8 @@ export class UserService extends AbstractService {
|
|
|
42
44
|
if (params.fetchAll) endpoint.addAdditionalParam("fetchAll", "true");
|
|
43
45
|
if (params.search) endpoint.addAdditionalParam("search", params.search);
|
|
44
46
|
|
|
47
|
+
if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
|
|
48
|
+
|
|
45
49
|
return this.callApi({
|
|
46
50
|
type: Modules.User,
|
|
47
51
|
method: HttpMethod.GET,
|
|
@@ -55,6 +59,8 @@ export class UserService extends AbstractService {
|
|
|
55
59
|
search?: string;
|
|
56
60
|
next?: NextRef;
|
|
57
61
|
prev?: PreviousRef;
|
|
62
|
+
/** JSON:API `sort` (`name`, `-name`), forwarded to the API. */
|
|
63
|
+
sort?: string;
|
|
58
64
|
}): Promise<UserInterface[]> {
|
|
59
65
|
const endpoint = new EndpointCreator({ endpoint: Modules.User });
|
|
60
66
|
|
|
@@ -63,6 +69,8 @@ export class UserService extends AbstractService {
|
|
|
63
69
|
endpoint.addAdditionalParam("fetchAll", "true");
|
|
64
70
|
if (params.search) endpoint.addAdditionalParam("search", params.search);
|
|
65
71
|
|
|
72
|
+
if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
|
|
73
|
+
|
|
66
74
|
return this.callApi({
|
|
67
75
|
type: Modules.User,
|
|
68
76
|
method: HttpMethod.GET,
|
|
@@ -71,7 +79,12 @@ export class UserService extends AbstractService {
|
|
|
71
79
|
});
|
|
72
80
|
}
|
|
73
81
|
|
|
74
|
-
static async findRelevant(params: {
|
|
82
|
+
static async findRelevant(params: {
|
|
83
|
+
id: string;
|
|
84
|
+
next?: NextRef;
|
|
85
|
+
prev?: PreviousRef;
|
|
86
|
+
sort?: string;
|
|
87
|
+
}): Promise<UserInterface[]> {
|
|
75
88
|
const endpoint: EndpointCreator = new EndpointCreator({
|
|
76
89
|
endpoint: Modules.Content,
|
|
77
90
|
id: params.id,
|
|
@@ -81,6 +94,8 @@ export class UserService extends AbstractService {
|
|
|
81
94
|
if (Modules.User.inclusions?.lists?.fields) endpoint.limitToFields(Modules.User.inclusions.lists.fields);
|
|
82
95
|
if (Modules.User.inclusions?.lists?.types) endpoint.limitToType(Modules.User.inclusions.lists.types);
|
|
83
96
|
|
|
97
|
+
if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
|
|
98
|
+
|
|
84
99
|
return this.callApi<UserInterface[]>({
|
|
85
100
|
type: Modules.User,
|
|
86
101
|
method: HttpMethod.GET,
|
|
@@ -94,6 +109,8 @@ export class UserService extends AbstractService {
|
|
|
94
109
|
search?: string;
|
|
95
110
|
next?: NextRef;
|
|
96
111
|
prev?: PreviousRef;
|
|
112
|
+
/** JSON:API `sort` (`name`, `-name`), forwarded to the API. */
|
|
113
|
+
sort?: string;
|
|
97
114
|
}): Promise<UserInterface[]> {
|
|
98
115
|
if (!params.companyId) return [];
|
|
99
116
|
|
|
@@ -105,6 +122,8 @@ export class UserService extends AbstractService {
|
|
|
105
122
|
|
|
106
123
|
if (params.search) endpoint.addAdditionalParam("search", params.search);
|
|
107
124
|
|
|
125
|
+
if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
|
|
126
|
+
|
|
108
127
|
return this.callApi({
|
|
109
128
|
type: Modules.User,
|
|
110
129
|
method: HttpMethod.GET,
|
|
@@ -121,6 +140,8 @@ export class UserService extends AbstractService {
|
|
|
121
140
|
isDeleted?: boolean;
|
|
122
141
|
next?: NextRef;
|
|
123
142
|
prev?: PreviousRef;
|
|
143
|
+
/** JSON:API `sort` (`name`, `-name`), forwarded to the API. */
|
|
144
|
+
sort?: string;
|
|
124
145
|
}): Promise<UserInterface[]> {
|
|
125
146
|
const endpoint = new EndpointCreator({
|
|
126
147
|
endpoint: Modules.Company,
|
|
@@ -133,6 +154,8 @@ export class UserService extends AbstractService {
|
|
|
133
154
|
if (params.limitToRoles && params.limitToRoles.length > 0)
|
|
134
155
|
endpoint.addAdditionalParam("limitToRoles", params.limitToRoles.join(","));
|
|
135
156
|
|
|
157
|
+
if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
|
|
158
|
+
|
|
136
159
|
return this.callApi({
|
|
137
160
|
type: Modules.User,
|
|
138
161
|
method: HttpMethod.GET,
|
|
@@ -146,11 +169,15 @@ export class UserService extends AbstractService {
|
|
|
146
169
|
search?: string;
|
|
147
170
|
next?: NextRef;
|
|
148
171
|
prev?: PreviousRef;
|
|
172
|
+
/** JSON:API `sort` (`name`, `-name`), forwarded to the API. */
|
|
173
|
+
sort?: string;
|
|
149
174
|
}): Promise<UserInterface[]> {
|
|
150
175
|
const endpoint = new EndpointCreator({ endpoint: Modules.Role, id: params.roleId, childEndpoint: Modules.User });
|
|
151
176
|
|
|
152
177
|
if (params.search) endpoint.addAdditionalParam("search", params.search);
|
|
153
178
|
|
|
179
|
+
if (params.sort) endpoint.addAdditionalParam("sort", params.sort);
|
|
180
|
+
|
|
154
181
|
return this.callApi({
|
|
155
182
|
type: Modules.User,
|
|
156
183
|
method: HttpMethod.GET,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { UserFields } from "../../data";
|
|
4
|
+
import { useUserTableStructure } from "../useUserTableStructure";
|
|
5
|
+
|
|
6
|
+
vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key, useLocale: () => "en" }));
|
|
7
|
+
|
|
8
|
+
/** Spec rows A8, A9, A11, A12, A13, A15, A16: name, email. Only an explicit `enableSorting: true` opts a column in (call C3). */
|
|
9
|
+
describe("useUserTableStructure sorting", () => {
|
|
10
|
+
it("opts in exactly the row's sortable columns", () => {
|
|
11
|
+
const { result } = renderHook(() => useUserTableStructure({ data: [], fields: Object.values(UserFields) as any }));
|
|
12
|
+
const columns = result.current.columns as any[];
|
|
13
|
+
const sortable = columns.filter((column) => column.enableSorting === true).map((column) => column.id);
|
|
14
|
+
expect(sortable.sort()).toEqual(["email", "name"]);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -57,7 +57,7 @@ export const useUserTableStructure: UseTableStructureHook<UserInterface, UserFie
|
|
|
57
57
|
</Link>
|
|
58
58
|
);
|
|
59
59
|
},
|
|
60
|
-
enableSorting:
|
|
60
|
+
enableSorting: true,
|
|
61
61
|
enableHiding: false,
|
|
62
62
|
}),
|
|
63
63
|
[UserFields.email]: () => ({
|
|
@@ -65,7 +65,7 @@ export const useUserTableStructure: UseTableStructureHook<UserInterface, UserFie
|
|
|
65
65
|
accessorKey: "email",
|
|
66
66
|
header: t(`common.fields.email.label`),
|
|
67
67
|
cell: ({ row }: { row: Row<TableContent<UserInterface>> }) => <>{row.getValue("email")}</>,
|
|
68
|
-
enableSorting:
|
|
68
|
+
enableSorting: true,
|
|
69
69
|
enableHiding: false,
|
|
70
70
|
}),
|
|
71
71
|
// Only meaningful on a platform-wide list (PlatformUsersList), where users
|