@jskit-ai/users-web 0.1.59 → 0.1.61

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.
@@ -0,0 +1,114 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import {
4
+ resolveLookupSelectedValues,
5
+ createLookupOptionsFromItems,
6
+ mergeSelectedLookupOptions,
7
+ resolveLookupOptionLabel
8
+ } from "../src/client/composables/internal/crudListFilterLookupSupport.js";
9
+
10
+ const supplierFilter = Object.freeze({
11
+ key: "supplierContactId",
12
+ type: "recordIdMany",
13
+ label: "Supplier",
14
+ lookup: Object.freeze({
15
+ apiSuffix: "/contacts",
16
+ valueKey: "id",
17
+ labelKey: "name"
18
+ })
19
+ });
20
+
21
+ test("lookup filter support normalizes selected record ids for single and many filters", () => {
22
+ assert.deepEqual(
23
+ resolveLookupSelectedValues(supplierFilter, ["7", "4", "7", ""]),
24
+ ["7", "4"]
25
+ );
26
+
27
+ assert.deepEqual(
28
+ resolveLookupSelectedValues(
29
+ {
30
+ ...supplierFilter,
31
+ type: "recordId"
32
+ },
33
+ 12
34
+ ),
35
+ ["12"]
36
+ );
37
+ });
38
+
39
+ test("lookup filter support builds option labels from custom resolvers and fallback lookup labels", () => {
40
+ const options = createLookupOptionsFromItems(
41
+ [
42
+ {
43
+ id: 7,
44
+ firstName: "Pollen",
45
+ lastName: "Partners"
46
+ },
47
+ {
48
+ id: 4,
49
+ name: "Harbor Storage"
50
+ }
51
+ ],
52
+ supplierFilter,
53
+ (item = {}) => {
54
+ if (item.id === 7) {
55
+ return `${item.firstName} ${item.lastName}`;
56
+ }
57
+
58
+ return "";
59
+ }
60
+ );
61
+
62
+ assert.deepEqual(options, [
63
+ {
64
+ value: "7",
65
+ label: "Pollen Partners",
66
+ record: {
67
+ id: 7,
68
+ firstName: "Pollen",
69
+ lastName: "Partners"
70
+ }
71
+ },
72
+ {
73
+ value: "4",
74
+ label: "Harbor Storage",
75
+ record: {
76
+ id: 4,
77
+ name: "Harbor Storage"
78
+ }
79
+ }
80
+ ]);
81
+ });
82
+
83
+ test("lookup filter support merges cached selected labels and resolves fallback labels", () => {
84
+ const currentOptions = [
85
+ {
86
+ value: "7",
87
+ label: "Pollen Partners",
88
+ record: {
89
+ id: 7,
90
+ name: "Pollen Partners"
91
+ }
92
+ }
93
+ ];
94
+ const cachedOptions = new Map([
95
+ ["12", {
96
+ value: "12",
97
+ label: "North Shed",
98
+ record: {
99
+ id: 12,
100
+ name: "North Shed"
101
+ }
102
+ }]
103
+ ]);
104
+
105
+ const mergedOptions = mergeSelectedLookupOptions(
106
+ currentOptions,
107
+ ["12", "7"],
108
+ cachedOptions
109
+ );
110
+
111
+ assert.deepEqual(mergedOptions.map((option) => option.value), ["12", "7"]);
112
+ assert.equal(resolveLookupOptionLabel(mergedOptions, cachedOptions, "12", "Storage"), "North Shed");
113
+ assert.equal(resolveLookupOptionLabel(mergedOptions, cachedOptions, "55", "Storage"), "Storage 55");
114
+ });
@@ -0,0 +1,182 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+
4
+ test("useCrudListFilters manages values, query params, chips, and presets", async () => {
5
+ const { useCrudListFilters } = await import("@jskit-ai/users-web/client/composables/useCrudListFilters");
6
+
7
+ const filters = useCrudListFilters(
8
+ {
9
+ onlyStaff: {
10
+ type: "flag",
11
+ label: "Staff"
12
+ },
13
+ status: {
14
+ type: "enumMany",
15
+ label: "Status",
16
+ options: [
17
+ { value: "active", label: "Active" },
18
+ { value: "archived", label: "Archived" }
19
+ ]
20
+ },
21
+ supplierContactId: {
22
+ type: "recordIdMany",
23
+ label: "Supplier"
24
+ },
25
+ arrivalDate: {
26
+ type: "dateRange",
27
+ label: "Arrival Date"
28
+ }
29
+ },
30
+ {
31
+ labelResolvers: {
32
+ supplierContactId(value) {
33
+ return value === "7" ? "Pollen Partners" : "";
34
+ }
35
+ },
36
+ presets: [
37
+ {
38
+ key: "needs-staff-review",
39
+ label: "Needs Staff Review",
40
+ values: {
41
+ onlyStaff: true,
42
+ status: ["archived"]
43
+ }
44
+ }
45
+ ]
46
+ }
47
+ );
48
+
49
+ filters.values.onlyStaff = true;
50
+ filters.values.status = ["active"];
51
+ filters.values.supplierContactId = ["7"];
52
+ filters.values.arrivalDate.from = "2026-04-01";
53
+
54
+ assert.equal(filters.queryParams.onlyStaff.value, true);
55
+ assert.deepEqual(filters.queryParams.status.value, ["active"]);
56
+ assert.equal(filters.queryParams.arrivalDateFrom.value, "2026-04-01");
57
+ assert.equal(filters.hasActiveFilters.value, true);
58
+ assert.deepEqual(
59
+ filters.activeChips.value.map((chip) => chip.label),
60
+ [
61
+ "Staff",
62
+ "Status: Active",
63
+ "Supplier: Pollen Partners",
64
+ "Arrival Date: from 2026-04-01"
65
+ ]
66
+ );
67
+
68
+ filters.clearChip(filters.activeChips.value.find((chip) => chip.filterKey === "supplierContactId"));
69
+ assert.deepEqual(filters.values.supplierContactId, []);
70
+
71
+ filters.applyPreset("needs-staff-review");
72
+ assert.equal(filters.values.onlyStaff, true);
73
+ assert.deepEqual(filters.values.status, ["archived"]);
74
+ assert.equal(filters.values.arrivalDate.from, "");
75
+
76
+ filters.clearFilters();
77
+ assert.equal(filters.values.onlyStaff, false);
78
+ assert.deepEqual(filters.values.status, []);
79
+ assert.equal(filters.hasActiveFilters.value, false);
80
+ });
81
+
82
+ test("useCrudListFilters supports dynamic presets and preset matching", async () => {
83
+ const { useCrudListFilters } = await import("@jskit-ai/users-web/client/composables/useCrudListFilters");
84
+ let today = "2026-04-18";
85
+
86
+ const filters = useCrudListFilters(
87
+ {
88
+ status: {
89
+ type: "enumMany",
90
+ label: "Status",
91
+ options: [
92
+ { value: "active", label: "Active" },
93
+ { value: "archived", label: "Archived" }
94
+ ]
95
+ },
96
+ arrivalDate: {
97
+ type: "dateRange",
98
+ label: "Arrival Date"
99
+ }
100
+ },
101
+ {
102
+ presets: [
103
+ {
104
+ key: "today",
105
+ label: "Today",
106
+ resolveValues() {
107
+ return {
108
+ arrivalDate: {
109
+ from: today,
110
+ to: today
111
+ }
112
+ };
113
+ }
114
+ },
115
+ {
116
+ key: "all-dates",
117
+ label: "All Dates",
118
+ values: {
119
+ arrivalDate: {
120
+ from: "",
121
+ to: ""
122
+ }
123
+ }
124
+ }
125
+ ]
126
+ }
127
+ );
128
+
129
+ filters.values.status = ["archived"];
130
+ filters.applyPreset("today", { mode: "merge" });
131
+
132
+ assert.equal(filters.values.arrivalDate.from, "2026-04-18");
133
+ assert.equal(filters.values.arrivalDate.to, "2026-04-18");
134
+ assert.deepEqual(filters.values.status, ["archived"]);
135
+ assert.equal(filters.matchesPreset("today"), true);
136
+ assert.equal(filters.matchesPreset("all-dates"), false);
137
+
138
+ today = "2026-04-19";
139
+ assert.equal(filters.matchesPreset("today"), false);
140
+
141
+ filters.applyPreset("all-dates", { mode: "merge" });
142
+ assert.equal(filters.values.arrivalDate.from, "");
143
+ assert.equal(filters.values.arrivalDate.to, "");
144
+ assert.deepEqual(filters.values.status, ["archived"]);
145
+ assert.equal(filters.matchesPreset("all-dates"), true);
146
+ });
147
+
148
+ test("useCrudListFilters preset matching rejects extra enumMany values present in current state", async () => {
149
+ const { useCrudListFilters } = await import("@jskit-ai/users-web/client/composables/useCrudListFilters");
150
+
151
+ const filters = useCrudListFilters(
152
+ {
153
+ status: {
154
+ type: "enumMany",
155
+ label: "Status",
156
+ options: [
157
+ { value: "active", label: "Active" },
158
+ { value: "archived", label: "Archived" }
159
+ ]
160
+ }
161
+ },
162
+ {
163
+ presets: [
164
+ {
165
+ key: "archived-only",
166
+ label: "Archived Only",
167
+ values: {
168
+ status: ["archived"]
169
+ }
170
+ }
171
+ ]
172
+ }
173
+ );
174
+
175
+ filters.values.status = ["archived", "bogus"];
176
+
177
+ assert.equal(filters.matchesPreset("archived-only"), false);
178
+ assert.deepEqual(
179
+ filters.activeChips.value.map((chip) => chip.label),
180
+ ["Status: Archived", "Status: bogus"]
181
+ );
182
+ });