@uxland/primary-shell 5.3.8 → 5.3.10

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 (28) hide show
  1. package/dist/index.js +1896 -1893
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.umd.cjs +26 -26
  4. package/dist/index.umd.cjs.map +1 -1
  5. package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/domain/validation/is-valid-basic-history-item.d.ts +5 -0
  6. package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/domain/validation/is-valid-full-history-item.d.ts +5 -0
  7. package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/domain/validation/is-valid-full-history-item.test.d.ts +1 -0
  8. package/dist/primary/shell/src/internal-plugins/activity-history/utils/get-unique-values-by-prop-path.d.ts +1 -1
  9. package/package.json +1 -1
  10. package/src/UI/components/primaria-shell/shell-header/styles.css +1 -0
  11. package/src/UI/internal-views/upper-nav-views.ts +0 -8
  12. package/src/internal-plugins/activity-history/activity-history-item/add/add-history-item/handler.ts +2 -2
  13. package/src/internal-plugins/activity-history/activity-history-item/add/add-history-items/handler.ts +2 -2
  14. package/src/internal-plugins/activity-history/activity-history-item/domain/business-rules.ts +2 -2
  15. package/src/internal-plugins/activity-history/activity-history-item/domain/validation/is-valid-basic-history-item.test.ts +63 -0
  16. package/src/internal-plugins/activity-history/activity-history-item/domain/validation/is-valid-basic-history-item.ts +43 -0
  17. package/src/internal-plugins/activity-history/activity-history-item/domain/{is-valid-history-item/is-valid-history-item.test.ts → validation/is-valid-full-history-item.test.ts} +11 -11
  18. package/src/internal-plugins/activity-history/activity-history-item/domain/{is-valid-history-item/is-valid-history-item.ts → validation/is-valid-full-history-item.ts} +7 -8
  19. package/src/internal-plugins/activity-history/activity-history-item/filter/diagnostic-filters/handle-add-diagnostics-options-from-item.ts +1 -0
  20. package/src/internal-plugins/activity-history/activity-history-item/list/UI/timeline/template.ts +22 -12
  21. package/src/internal-plugins/activity-history/activity-history-item/list/group-history-items/group-history-items.test.ts +71 -215
  22. package/src/internal-plugins/activity-history/activity-history-item/list/group-history-items/group-history-items.ts +26 -3
  23. package/src/internal-plugins/activity-history/activity-history-item/selectors.ts +1 -0
  24. package/src/internal-plugins/activity-history/activity-history-item/update/handler.ts +2 -2
  25. package/src/internal-plugins/activity-history/utils/get-unique-values-by-prop-path.test.ts +55 -30
  26. package/src/internal-plugins/activity-history/utils/get-unique-values-by-prop-path.ts +30 -10
  27. package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/domain/is-valid-history-item/is-valid-history-item.d.ts +0 -5
  28. /package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/domain/{is-valid-history-item/is-valid-history-item.test.d.ts → validation/is-valid-basic-history-item.test.d.ts} +0 -0
@@ -1,238 +1,94 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import { groupActivityHistoryItems } from "./group-history-items";
3
2
  import { IActivityHistoryItem } from "../../domain/model";
3
+ import { groupActivityHistoryItems } from "./group-history-items";
4
+
5
+ const baseItem = (overrides: Partial<IActivityHistoryItem>): IActivityHistoryItem => ({
6
+ id: "1",
7
+ date: new Date().toISOString(),
8
+ professional: { id: "p1", role: { id: "r1" }, speciality: { id: "s1" } },
9
+ ep: { id: "ep1" },
10
+ up: { id: "up1" },
11
+ center: { id: "c1" },
12
+ service: { id: "sv1" },
13
+ diagnostics: [],
14
+ ...overrides,
15
+ });
16
+
4
17
  describe("groupActivityHistoryItems", () => {
5
- it("should group items by visit and then by diagnostics", () => {
6
- const items: IActivityHistoryItem[] = [
7
- {
8
- id: "1",
9
- date: "2024-07-04T08:00:00Z",
10
- professional: {
11
- name: "1",
12
- id: "1",
13
- role: { id: "1", description: "1" },
14
- speciality: { id: "1", description: "1" },
15
- },
16
- relevant: true,
17
- diagnostics: [{ id: "d1", description: "d1" }],
18
- center: { id: "c1", description: "c1" },
19
- up: { id: "up1", description: "up1" },
20
- ep: { id: "ep1", description: "ep1" },
21
- service: { id: "s1", description: "s1" },
22
- },
23
- {
24
- id: "2",
25
- date: "2024-07-04T10:00:00Z",
26
- professional: {
27
- name: "1",
28
- id: "1",
29
- role: { id: "1", description: "1" },
30
- speciality: { id: "1", description: "1" },
31
- },
32
- relevant: true,
33
- diagnostics: [{ id: "d1", description: "d1" }],
34
- center: { id: "c1", description: "c1" },
35
- up: { id: "up1", description: "up1" },
36
- ep: { id: "ep1", description: "ep1" },
37
- service: { id: "s1", description: "s1" },
38
- },
39
- {
40
- id: "3",
41
- date: "2024-07-04T08:00:00Z",
42
- professional: {
43
- name: "1",
44
- id: "2",
45
- role: { id: "2", description: "2" },
46
- speciality: { id: "2", description: "2" },
47
- },
48
- relevant: true,
49
- diagnostics: [{ id: "d2", description: "d2" }],
50
- center: { id: "c2", description: "c2" },
51
- up: { id: "up2", description: "up2" },
52
- ep: { id: "ep2", description: "ep2" },
53
- service: { id: "s2", description: "s2" },
54
- },
55
- {
56
- id: "4",
57
- date: "2024-07-04T08:00:00Z",
58
- professional: {
59
- name: "1",
60
- id: "1",
61
- role: { id: "1", description: "1" },
62
- speciality: { id: "1", description: "1" },
63
- },
64
- relevant: true,
65
- diagnostics: [],
66
- center: { id: "c1", description: "c1" },
67
- up: { id: "up1", description: "up1" },
68
- ep: { id: "ep1", description: "ep1" },
69
- service: { id: "s1", description: "s1" },
70
- },
18
+ it("agrupa en un solo grupo por misma visita y dentro de 8h", () => {
19
+ const now = new Date();
20
+ const items = [
21
+ baseItem({ id: "1", date: now.toISOString() }),
22
+ baseItem({ id: "2", date: new Date(now.getTime() + 2 * 3600 * 1000).toISOString() }),
71
23
  ];
72
24
 
73
25
  const result = groupActivityHistoryItems(items);
74
26
 
75
- expect(result.length).toBe(2);
76
-
77
- const group1 = result.find((g) => g.items.some((i) => i.id === "4"));
78
- const group2 = result.find((g) =>
79
- g.subGroups?.some((sg) => sg.items.some((i) => i.id === "3")),
80
- );
81
-
82
- expect(group1).toBeDefined();
83
- expect(group2).toBeDefined();
84
-
85
- if (group1) {
86
- expect(group1.items.length).toBe(1); // Only item with no diagnostics should be here
87
- expect(group1.items[0].id).toBe("4");
88
-
89
- expect(group1.subGroups?.length).toBe(1);
90
- const subGroup1 = group1.subGroups?.[0];
91
- expect(subGroup1?.items.length).toBe(2);
92
- expect(subGroup1?.items.some((i) => i.id === "1")).toBe(true);
93
- expect(subGroup1?.items.some((i) => i.id === "2")).toBe(true);
94
- }
95
-
96
- if (group2) {
97
- expect(group2.items.length).toBe(0); // No items should be here
98
- expect(group2.subGroups?.length).toBe(1);
99
- const subGroup2 = group2.subGroups?.[0];
100
- expect(subGroup2?.items.length).toBe(1);
101
- expect(subGroup2?.items.some((i) => i.id === "3")).toBe(true);
102
- }
27
+ expect(result).toHaveLength(1);
28
+ expect(result[0].sameVisit).toBe(true);
29
+ expect(result[0].items).toHaveLength(2);
103
30
  });
104
31
 
105
- it("should create separate groups for different visits", () => {
106
- const items: IActivityHistoryItem[] = [
107
- {
32
+ it("agrupa sin misma visita pero dentro de 8h", () => {
33
+ const now = new Date();
34
+ const items = [
35
+ baseItem({
108
36
  id: "1",
109
- date: "2024-07-04T08:00:00Z",
110
- professional: {
111
- name: "1",
112
- id: "1",
113
- role: { id: "1", description: "1" },
114
- speciality: { id: "1", description: "1" },
115
- },
116
- relevant: true,
117
- diagnostics: [{ id: "d1", description: "d1" }],
118
- center: { id: "c1", description: "c1" },
119
- up: { id: "up1", description: "up1" },
120
- ep: { id: "ep1", description: "ep1" },
121
- service: { id: "s1", description: "s1" },
122
- },
123
- {
37
+ date: now.toISOString(),
38
+ professional: { id: "p1", role: { id: "r1" }, speciality: { id: "s1" } },
39
+ }),
40
+ baseItem({
124
41
  id: "2",
125
- date: "2024-07-05T08:00:00Z",
126
- professional: {
127
- name: "1",
128
- id: "1",
129
- role: { id: "1", description: "1" },
130
- speciality: { id: "1", description: "1" },
131
- },
132
- relevant: true,
133
- diagnostics: [{ id: "d1", description: "d1" }],
134
- center: { id: "c1", description: "c1" },
135
- up: { id: "up1", description: "up1" },
136
- ep: { id: "ep1", description: "ep1" },
137
- service: { id: "s1", description: "s1" },
138
- },
42
+ date: new Date(now.getTime() + 2 * 3600 * 1000).toISOString(),
43
+ professional: { id: "p2", role: { id: "r2" }, speciality: { id: "s2" } },
44
+ }),
139
45
  ];
140
46
 
141
47
  const result = groupActivityHistoryItems(items);
142
48
 
143
- expect(result.length).toBe(2);
144
-
145
- const group1 = result.find((g) =>
146
- g.subGroups?.some((sg) => sg.items.some((i) => i.id === "1")),
147
- );
148
- const group2 = result.find((g) =>
149
- g.subGroups?.some((sg) => sg.items.some((i) => i.id === "2")),
150
- );
151
-
152
- expect(group1).toBeDefined();
153
- expect(group2).toBeDefined();
154
-
155
- if (group1) {
156
- expect(group1.items.length).toBe(0);
157
- expect(group1.subGroups?.length).toBe(1);
158
- const subGroup1 = group1.subGroups?.[0];
159
- expect(subGroup1?.items.length).toBe(1);
160
- expect(subGroup1?.items.some((i) => i.id === "1")).toBe(true);
161
- }
162
-
163
- if (group2) {
164
- expect(group2.items.length).toBe(0);
165
- expect(group2.subGroups?.length).toBe(1);
166
- const subGroup2 = group2.subGroups?.[0];
167
- expect(subGroup2?.items.length).toBe(1);
168
- expect(subGroup2?.items.some((i) => i.id === "2")).toBe(true);
169
- }
49
+ expect(result).toHaveLength(1);
50
+ expect(result[0].sameVisit).toBe(false);
51
+ expect(result[0].items).toHaveLength(2);
170
52
  });
171
53
 
172
- it("should not group items more than 8 hours apart", () => {
173
- const items: IActivityHistoryItem[] = [
174
- {
175
- id: "1",
176
- date: "2024-07-04T08:00:00Z",
177
- professional: {
178
- name: "1",
179
- id: "1",
180
- role: { id: "1", description: "1" },
181
- speciality: { id: "1", description: "1" },
182
- },
183
- relevant: true,
184
- diagnostics: [{ id: "d1", description: "d1" }],
185
- center: { id: "c1", description: "c1" },
186
- up: { id: "up1", description: "up1" },
187
- ep: { id: "ep1", description: "ep1" },
188
- service: { id: "s1", description: "s1" },
189
- },
190
- {
191
- id: "2",
192
- date: "2024-07-04T17:00:00Z",
193
- professional: {
194
- name: "1",
195
- id: "1",
196
- role: { id: "1", description: "1" },
197
- speciality: { id: "1", description: "1" },
198
- },
199
- relevant: true,
200
- diagnostics: [{ id: "d1", description: "d1" }],
201
- center: { id: "c1", description: "c1" },
202
- up: { id: "up1", description: "up1" },
203
- ep: { id: "ep1", description: "ep1" },
204
- service: { id: "s1", description: "s1" },
205
- },
54
+ it("no agrupa si hay diferencia de más de 8h", () => {
55
+ const now = new Date();
56
+ const items = [
57
+ baseItem({ id: "1", date: now.toISOString() }),
58
+ baseItem({ id: "2", date: new Date(now.getTime() + 9 * 3600 * 1000).toISOString() }),
59
+ ];
60
+
61
+ const result = groupActivityHistoryItems(items);
62
+
63
+ expect(result).toHaveLength(2);
64
+ });
65
+
66
+ it("crea subgrupos con mismos diagnósticos", () => {
67
+ const now = new Date();
68
+ const diagnostics = [{ codi: "D1" }];
69
+ const items = [
70
+ baseItem({ id: "1", date: now.toISOString(), diagnostics }),
71
+ baseItem({ id: "2", date: new Date(now.getTime() + 1000).toISOString(), diagnostics }),
72
+ ];
73
+
74
+ const result = groupActivityHistoryItems(items);
75
+
76
+ expect(result[0].subGroups).toHaveLength(1);
77
+ expect(result[0].subGroups![0].items).toHaveLength(2);
78
+ expect(result[0].items).toHaveLength(0);
79
+ });
80
+
81
+ it("deja fuera de subgrupos ítems sin diagnóstico", () => {
82
+ const now = new Date();
83
+ const items = [
84
+ baseItem({ id: "1", date: now.toISOString(), diagnostics: [{ codi: "D1" }] }),
85
+ baseItem({ id: "2", date: new Date(now.getTime() + 1000).toISOString() }),
206
86
  ];
207
87
 
208
88
  const result = groupActivityHistoryItems(items);
209
89
 
210
- expect(result.length).toBe(2);
211
-
212
- const group1 = result.find((g) =>
213
- g.subGroups?.some((sg) => sg.items.some((i) => i.id === "1")),
214
- );
215
- const group2 = result.find((g) =>
216
- g.subGroups?.some((sg) => sg.items.some((i) => i.id === "2")),
217
- );
218
-
219
- expect(group1).toBeDefined();
220
- expect(group2).toBeDefined();
221
-
222
- if (group1) {
223
- expect(group1.items.length).toBe(0);
224
- expect(group1.subGroups?.length).toBe(1);
225
- const subGroup1 = group1.subGroups?.[0];
226
- expect(subGroup1?.items.length).toBe(1);
227
- expect(subGroup1?.items.some((i) => i.id === "1")).toBe(true);
228
- }
229
-
230
- if (group2) {
231
- expect(group2.items.length).toBe(0);
232
- expect(group2.subGroups?.length).toBe(1);
233
- const subGroup2 = group2.subGroups?.[0];
234
- expect(subGroup2?.items.length).toBe(1);
235
- expect(subGroup2?.items.some((i) => i.id === "2")).toBe(true);
236
- }
90
+ expect(result[0].subGroups).toHaveLength(1);
91
+ expect(result[0].subGroups![0].items).toHaveLength(1);
92
+ expect(result[0].items).toHaveLength(1);
237
93
  });
238
94
  });
@@ -8,8 +8,8 @@ import {
8
8
  const isSameVisit = (item1: IActivityHistoryItem, item2: IActivityHistoryItem): boolean => {
9
9
  const sameProfessional =
10
10
  item1.professional?.id === item2.professional?.id &&
11
- item1.professional?.role.id === item2.professional?.role.id &&
12
- item1.professional?.speciality.id === item2.professional?.speciality.id;
11
+ item1.professional?.role?.id === item2.professional?.role?.id &&
12
+ item1.professional?.speciality?.id === item2.professional?.speciality?.id;
13
13
  const sameLocation =
14
14
  item1.ep?.id === item2.ep?.id &&
15
15
  item1.up?.id === item2.up?.id &&
@@ -21,7 +21,7 @@ const isSameVisit = (item1: IActivityHistoryItem, item2: IActivityHistoryItem):
21
21
  return sameProfessional && sameLocation && sameDay;
22
22
  };
23
23
 
24
- const withinEightHours = (date1: Date, date2: Date): boolean => {
24
+ const withinEightHours = (date1: string, date2: string): boolean => {
25
25
  const timeDifference = Math.abs(new Date(date1).getTime() - new Date(date2).getTime());
26
26
  return timeDifference <= 8 * 60 * 60 * 1000; // 8 hours in milliseconds
27
27
  };
@@ -46,6 +46,7 @@ const groupActivityHistoryItems = (items: IActivityHistoryItem[]): IActivityHist
46
46
  items?.forEach((item) => {
47
47
  let added = false;
48
48
 
49
+ // Primer intento: agrupar por misma visita y dentro de 8 horas
49
50
  for (const group of groups) {
50
51
  const firstItem = group.items[0];
51
52
  const lastItem = group.items[group.items.length - 1];
@@ -56,15 +57,37 @@ const groupActivityHistoryItems = (items: IActivityHistoryItem[]): IActivityHist
56
57
  withinEightHours(lastItem.date, item.date)
57
58
  ) {
58
59
  group.items.push(item);
60
+ group.sameVisit = true; // marcamos el grupo como de "misma visita"
59
61
  added = true;
60
62
  break;
61
63
  }
62
64
  }
63
65
 
66
+ // Segundo intento: agrupar solo por 8h, pero solo en grupos que NO son "misma visita"
67
+ if (!added) {
68
+ for (const group of groups) {
69
+ if (group.sameVisit) continue; // evitamos mezclar visitas
70
+
71
+ const firstItem = group.items[0];
72
+ const lastItem = group.items[group.items.length - 1];
73
+
74
+ if (
75
+ withinEightHours(firstItem.date, item.date) &&
76
+ withinEightHours(lastItem.date, item.date)
77
+ ) {
78
+ group.items.push(item);
79
+ added = true;
80
+ break;
81
+ }
82
+ }
83
+ }
84
+
85
+ // Si no se pudo agrupar, crear un nuevo grupo
64
86
  if (!added) {
65
87
  groups.push({
66
88
  idGroup: Math.random().toString(36).substr(2, 9),
67
89
  items: [item],
90
+ sameVisit: false,
68
91
  });
69
92
  }
70
93
  });
@@ -73,6 +73,7 @@ export const customFilterGroupsWithOptionsSelector = createSelector(
73
73
  collection.items,
74
74
  f.propPathValue,
75
75
  f.propsPathDescription,
76
+ f.title
76
77
  )
77
78
  : []) || [],
78
79
  };
@@ -1,12 +1,12 @@
1
1
  import { StoreBaseHandler } from "../../infrastructure/base-handlers";
2
- import { ensureAreValidActivityHistoryItems } from "../domain/is-valid-history-item/is-valid-history-item";
2
+ import { ensureAreValidBasicActivityHistoryItems } from "../domain/validation/is-valid-basic-history-item";
3
3
  import { updateActivityHistoryItem } from "./action";
4
4
  import { UpdateHistoryItemPayload } from "./request";
5
5
 
6
6
  export class UpdateHistoryItemHandler extends StoreBaseHandler {
7
7
  async handle(payload: UpdateHistoryItemPayload) {
8
8
  try {
9
- ensureAreValidActivityHistoryItems([payload.item]);
9
+ ensureAreValidBasicActivityHistoryItems([payload.item]);
10
10
  this.store.dispatch(
11
11
  updateActivityHistoryItem({
12
12
  id: payload.entityId,
@@ -1,61 +1,86 @@
1
1
  import { describe, it, expect } from "vitest";
2
2
  import { getUniqueObjects } from "./get-unique-values-by-prop-path";
3
3
 
4
+
4
5
  describe("getUniqueObjects", () => {
5
- it("should return unique objects by id, keeping the last occurrence", () => {
6
+ const filterTitle = "Centre";
7
+
8
+ it("should return unique objects with correct id and title", () => {
6
9
  const items = [
7
- { data: { id: "1", label: "Item 1" } },
8
- { data: { id: "2", label: "Item 2" } },
9
- { data: { id: "1", label: "Item 1 (duplicate)" } }, // <- este debe sobrescribir
10
+ { a: { b: "1" }, c: { d: "Hospital A" } },
11
+ { a: { b: "2" }, c: { d: "Hospital B" } },
12
+ { a: { b: "1" }, c: { d: "Hospital A" } }, // duplicated
10
13
  ];
11
14
 
12
- const result = getUniqueObjects(items, ["data", "id"], ["data", "label"]);
15
+ const result = getUniqueObjects(items, ["a", "b"], ["c", "d"], filterTitle);
13
16
 
14
17
  expect(result).toEqual([
15
- { id: "1", title: "Item 1 (duplicate)" }, // 👈 el último
16
- { id: "2", title: "Item 2" },
18
+ { id: "1", title: "Hospital A" },
19
+ { id: "2", title: "Hospital B" },
17
20
  ]);
18
21
  });
19
22
 
20
- it("should skip items with missing id or title", () => {
23
+ it("should sort the results alphabetically by title", () => {
21
24
  const items = [
22
- { data: { id: "1", label: "Valid" } },
23
- { data: { id: 2, label: "Invalid ID" } },
24
- { data: { id: "3", label: null } },
25
+ { a: { b: "2" }, c: { d: "Zoo" } },
26
+ { a: { b: "1" }, c: { d: "Apple" } },
27
+ { a: { b: "3" }, c: { d: "Library" } },
25
28
  ];
26
29
 
27
- const result = getUniqueObjects(items, ["data", "id"], ["data", "label"]);
30
+ const result = getUniqueObjects(items, ["a", "b"], ["c", "d"], filterTitle);
28
31
 
29
- expect(result).toEqual([{ id: "1", title: "Valid" }]);
32
+ expect(result).toEqual([
33
+ { id: "1", title: "Apple" },
34
+ { id: "3", title: "Library" },
35
+ { id: "2", title: "Zoo" },
36
+ ]);
30
37
  });
31
38
 
32
- it("should return an empty array if input is empty", () => {
33
- const result = getUniqueObjects([], ["a"], ["b"]);
34
- expect(result).toEqual([]);
39
+ it("should add 'Sense centre' for items with missing id or title", () => {
40
+ const items = [
41
+ { a: { b: "" }, c: { d: "Hospital A" } },
42
+ { a: { b: "1" }, c: { d: null } },
43
+ { a: { b: null }, c: { d: "" } },
44
+ ];
45
+
46
+ const result = getUniqueObjects(items, ["a", "b"], ["c", "d"], filterTitle);
47
+
48
+ expect(result).toHaveLength(1);
49
+ expect(result[0].title).toBe("Sense centre");
50
+ expect(result[0].id).toBe(""); // depende de implementación, aquí "" está permitido
35
51
  });
36
52
 
37
- it("should handle deeply nested paths", () => {
53
+ it("should put 'Sense centre' entry at the end", () => {
38
54
  const items = [
39
- { a: { b: { c: { id: "x1", name: "Deep 1" } } } },
40
- { a: { b: { c: { id: "x2", name: "Deep 2" } } } },
55
+ { a: { b: "1" }, c: { d: "Beta" } },
56
+ { a: { b: null }, c: { d: null } },
57
+ { a: { b: "2" }, c: { d: "Alpha" } },
41
58
  ];
42
59
 
43
- const result = getUniqueObjects(items, ["a", "b", "c", "id"], ["a", "b", "c", "name"]);
60
+ const result = getUniqueObjects(items, ["a", "b"], ["c", "d"], filterTitle);
44
61
 
45
- expect(result).toEqual([
46
- { id: "x1", title: "Deep 1" },
47
- { id: "x2", title: "Deep 2" },
48
- ]);
62
+ expect(result).toHaveLength(3);
63
+ expect(result[0].title).toBe("Alpha");
64
+ expect(result[1].title).toBe("Beta");
65
+ expect(result[2].title).toBe("Sense centre");
49
66
  });
50
67
 
51
- it("should handle invalid path safely", () => {
68
+ it("should handle deep nested paths", () => {
52
69
  const items = [
53
- { a: { b: { id: "1", name: "Good" } } },
54
- { x: { y: { id: "2", name: "Missing Path" } } },
70
+ { a: { b: { c: "10" } }, x: { y: { z: "Foo" } } },
71
+ { a: { b: { c: "20" } }, x: { y: { z: "Bar" } } },
55
72
  ];
56
73
 
57
- const result = getUniqueObjects(items, ["a", "b", "id"], ["a", "b", "name"]);
74
+ const result = getUniqueObjects(items, ["a", "b", "c"], ["x", "y", "z"], filterTitle);
75
+
76
+ expect(result).toEqual([
77
+ { id: "20", title: "Bar" },
78
+ { id: "10", title: "Foo" },
79
+ ]);
80
+ });
58
81
 
59
- expect(result).toEqual([{ id: "1", title: "Good" }]);
82
+ it("should return empty array for empty input", () => {
83
+ const result = getUniqueObjects([], ["a"], ["b"], filterTitle);
84
+ expect(result).toEqual([]);
60
85
  });
61
- });
86
+ });
@@ -2,15 +2,35 @@ export function getUniqueObjects<T>(
2
2
  items: T[],
3
3
  propPathId: string[],
4
4
  propPathDescription: string[],
5
+ filterTitle: string
5
6
  ): { id: string; title: string }[] {
6
- return Array.from(
7
- items
8
- .map((item) => ({
9
- id: propPathId.reduce((acc, key) => acc?.[key], item),
10
- title: propPathDescription.reduce((acc, key) => acc?.[key], item),
11
- }))
12
- .filter((obj) => typeof obj.id === "string" && typeof obj.title === "string")
13
- .reduce((map, obj) => map.set(obj.id, obj), new Map<string, { id: string; title: string }>())
14
- .values(),
7
+ const SENSE_TITLE = `Sense ${filterTitle.toLocaleLowerCase()}`;
8
+ const map = new Map<string, { id: string; title: string }>();
9
+ let hasSenseItem = false;
10
+
11
+ for (const item of items) {
12
+ const id = propPathId.reduce((acc, key) => acc?.[key], item);
13
+ const title = propPathDescription.reduce((acc, key) => acc?.[key], item);
14
+
15
+ const isValidId = typeof id === "string" && id.trim() !== "";
16
+ const isValidTitle = typeof title === "string" && title.trim() !== "";
17
+
18
+ if (isValidId && isValidTitle) {
19
+ if (!map.has(id)) {
20
+ map.set(id, { id, title });
21
+ }
22
+ } else {
23
+ hasSenseItem = true;
24
+ }
25
+ }
26
+
27
+ const result = Array.from(map.values()).sort((a, b) =>
28
+ a.title.localeCompare(b.title, "ca", { sensitivity: "base" })
15
29
  );
16
- }
30
+
31
+ if (hasSenseItem) {
32
+ result.push({ id: "", title: SENSE_TITLE });
33
+ }
34
+
35
+ return result;
36
+ }
@@ -1,5 +0,0 @@
1
- import { IActivityHistoryItem } from '../model';
2
-
3
- export declare const isValidActivityHistoryItem: (obj: IActivityHistoryItem) => any;
4
- export declare const ensureAreValidActivityHistoryItems: (items: IActivityHistoryItem[]) => IActivityHistoryItem[];
5
- export declare const validateAndFilterItems: (items: IActivityHistoryItem[]) => IActivityHistoryItem[];