@uxland/primary-shell 5.3.10 → 5.3.11

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 (23) hide show
  1. package/dist/index.js +42 -41
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.umd.cjs +4 -4
  4. package/dist/index.umd.cjs.map +1 -1
  5. package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/domain/validation/diagnostics/are-same-diagnostics.d.ts +3 -0
  6. package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/domain/validation/diagnostics/are-same-diagnostics.test.d.ts +1 -0
  7. package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/domain/validation/diagnostics/has-valid-diagnostics.d.ts +4 -0
  8. package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/domain/validation/diagnostics/has-valid-diagnostics.test.d.ts +1 -0
  9. package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/domain/validation/is-valid-professional.d.ts +1 -0
  10. package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/domain/validation/utils.d.ts +4 -0
  11. package/package.json +1 -1
  12. package/src/internal-plugins/activity-history/activity-history-item/domain/validation/diagnostics/are-same-diagnostics.test.ts +48 -0
  13. package/src/internal-plugins/activity-history/activity-history-item/domain/validation/diagnostics/are-same-diagnostics.ts +15 -0
  14. package/src/internal-plugins/activity-history/activity-history-item/domain/validation/diagnostics/has-valid-diagnostics.test.ts +79 -0
  15. package/src/internal-plugins/activity-history/activity-history-item/domain/validation/diagnostics/has-valid-diagnostics.ts +20 -0
  16. package/src/internal-plugins/activity-history/activity-history-item/domain/validation/is-valid-basic-history-item.ts +1 -11
  17. package/src/internal-plugins/activity-history/activity-history-item/domain/validation/is-valid-full-history-item.ts +3 -39
  18. package/src/internal-plugins/activity-history/activity-history-item/domain/validation/is-valid-professional.ts +16 -0
  19. package/src/internal-plugins/activity-history/activity-history-item/domain/validation/utils.ts +11 -0
  20. package/src/internal-plugins/activity-history/activity-history-item/filter/UI/activity-history-filters/template.ts +1 -1
  21. package/src/internal-plugins/activity-history/activity-history-item/filter/custom-filters/add-custom-filters/reducer.ts +5 -1
  22. package/src/internal-plugins/activity-history/activity-history-item/list/group-history-items/group-history-items.test.ts +43 -2
  23. package/src/internal-plugins/activity-history/activity-history-item/list/group-history-items/group-history-items.ts +40 -44
@@ -0,0 +1,3 @@
1
+ import { IActivityHistoryDiagnostic } from '../../model';
2
+
3
+ export declare const areSameDiagnostics: (diag1: IActivityHistoryDiagnostic[], diag2: IActivityHistoryDiagnostic[]) => boolean;
@@ -0,0 +1,4 @@
1
+ import { IActivityHistoryDiagnostic } from '../../model';
2
+
3
+ export declare const hasValidDiagnostic: (diagnostic: IActivityHistoryDiagnostic) => boolean;
4
+ export declare const hasValidDiagnostics: (diagnostics: IActivityHistoryDiagnostic[]) => boolean;
@@ -0,0 +1 @@
1
+ export declare const hasValidProfessional: (professional: any) => any;
@@ -0,0 +1,4 @@
1
+ export declare const isString: (value: any) => boolean;
2
+ export declare const isBoolean: (value: any) => value is boolean;
3
+ export declare const isArray: (value: any) => value is any[];
4
+ export declare const isValidDateString: (value: any) => boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uxland/primary-shell",
3
- "version": "5.3.10",
3
+ "version": "5.3.11",
4
4
  "description": "Primaria Shell",
5
5
  "author": "UXLand <dev@uxland.es>",
6
6
  "homepage": "https://github.com/uxland/harmonix/tree/app#readme",
@@ -0,0 +1,48 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { areSameDiagnostics } from "./are-same-diagnostics";
3
+
4
+ describe("areSameDiagnostics", () => {
5
+ it("returns true for identical diagnostics in same order", () => {
6
+ const d1 = [{ codi: "A" }, { codi: "B" }];
7
+ const d2 = [{ codi: "A" }, { codi: "B" }];
8
+ expect(areSameDiagnostics(d1, d2)).toBe(true);
9
+ });
10
+
11
+ it("returns true for identical diagnostics in different order", () => {
12
+ const d1 = [{ codi: "B" }, { codi: "A" }];
13
+ const d2 = [{ codi: "A" }, { codi: "B" }];
14
+ expect(areSameDiagnostics(d1, d2)).toBe(true);
15
+ });
16
+
17
+ it("returns false if one diagnostic has extra item", () => {
18
+ const d1 = [{ codi: "A" }, { codi: "B" }];
19
+ const d2 = [{ codi: "A" }];
20
+ expect(areSameDiagnostics(d1, d2)).toBe(false);
21
+ });
22
+
23
+ it("returns false if codes are different", () => {
24
+ const d1 = [{ codi: "A" }, { codi: "B" }];
25
+ const d2 = [{ codi: "A" }, { codi: "C" }];
26
+ expect(areSameDiagnostics(d1, d2)).toBe(false);
27
+ });
28
+
29
+ it("returns true for both empty arrays", () => {
30
+ expect(areSameDiagnostics([], [])).toBe(true);
31
+ });
32
+
33
+ it("returns false if one is empty and the other is not", () => {
34
+ expect(areSameDiagnostics([], [{ codi: "X" }])).toBe(false);
35
+ });
36
+
37
+ it("returns true for duplicated codes in both arrays", () => {
38
+ const d1 = [{ codi: "A" }, { codi: "A" }];
39
+ const d2 = [{ codi: "A" }, { codi: "A" }];
40
+ expect(areSameDiagnostics(d1, d2)).toBe(true);
41
+ });
42
+
43
+ it("returns false if duplicated codes differ in count", () => {
44
+ const d1 = [{ codi: "A" }, { codi: "A" }];
45
+ const d2 = [{ codi: "A" }];
46
+ expect(areSameDiagnostics(d1, d2)).toBe(false);
47
+ });
48
+ });
@@ -0,0 +1,15 @@
1
+ import { IActivityHistoryDiagnostic } from "../../model";
2
+
3
+ export const areSameDiagnostics = (
4
+ diag1: IActivityHistoryDiagnostic[],
5
+ diag2: IActivityHistoryDiagnostic[],
6
+ ): boolean => {
7
+ if (diag1.length !== diag2.length) {
8
+ return false;
9
+ }
10
+
11
+ const sortedDiag1 = diag1.map((d) => d.codi).sort();
12
+ const sortedDiag2 = diag2.map((d) => d.codi).sort();
13
+
14
+ return sortedDiag1.every((id, index) => id === sortedDiag2[index]);
15
+ };
@@ -0,0 +1,79 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { hasValidDiagnostic, hasValidDiagnostics } from "./has-valid-diagnostics";
3
+
4
+ interface IActivityHistoryDiagnostic {
5
+ codi?: any;
6
+ cataleg?: any;
7
+ descripcio?: any;
8
+ }
9
+
10
+ describe("hasValidDiagnostic", () => {
11
+ it("returns true for a valid diagnostic", () => {
12
+ const diag: IActivityHistoryDiagnostic = {
13
+ codi: "A123",
14
+ cataleg: "ICD-10",
15
+ descripcio: "Dolor lumbar",
16
+ };
17
+ expect(hasValidDiagnostic(diag)).toBe(true);
18
+ });
19
+
20
+ it("returns false for null", () => {
21
+ expect(hasValidDiagnostic(null as any)).toBe(false);
22
+ });
23
+
24
+ it("returns false for undefined", () => {
25
+ expect(hasValidDiagnostic(undefined as any)).toBe(false);
26
+ });
27
+
28
+ it("returns false if one property is missing", () => {
29
+ const diag: IActivityHistoryDiagnostic = {
30
+ codi: "B123",
31
+ cataleg: "CIM10",
32
+ // descripcio missing
33
+ };
34
+ expect(hasValidDiagnostic(diag)).toBe(false);
35
+ });
36
+
37
+ it("returns false if any property is not a string", () => {
38
+ const diag: IActivityHistoryDiagnostic = {
39
+ codi: 123,
40
+ cataleg: "ICD",
41
+ descripcio: "Texto",
42
+ };
43
+ expect(hasValidDiagnostic(diag)).toBe(false);
44
+ });
45
+ });
46
+
47
+ describe("hasValidDiagnostics", () => {
48
+ it("returns true for a valid array of diagnostics", () => {
49
+ const diagnostics = [
50
+ { codi: "A", cataleg: "X", descripcio: "D1" },
51
+ { codi: "B", cataleg: "Y", descripcio: "D2" },
52
+ ];
53
+ expect(hasValidDiagnostics(diagnostics)).toBe(true);
54
+ });
55
+
56
+ it("returns true for null input", () => {
57
+ expect(hasValidDiagnostics(null as any)).toBe(true);
58
+ });
59
+
60
+ it("returns true for undefined input", () => {
61
+ expect(hasValidDiagnostics(undefined as any)).toBe(true);
62
+ });
63
+
64
+ it("returns false if not all diagnostics are valid", () => {
65
+ const diagnostics = [
66
+ { codi: "A", cataleg: "X", descripcio: "D1" },
67
+ { codi: "", cataleg: 42, descripcio: null },
68
+ ];
69
+ expect(hasValidDiagnostics(diagnostics)).toBe(false);
70
+ });
71
+
72
+ it("returns false if diagnostics is not an array", () => {
73
+ expect(hasValidDiagnostics({} as any)).toBe(false);
74
+ });
75
+
76
+ it("returns true for empty array", () => {
77
+ expect(hasValidDiagnostics([])).toBe(true);
78
+ });
79
+ });
@@ -0,0 +1,20 @@
1
+ import { IActivityHistoryDiagnostic } from "../../model";
2
+
3
+ export const hasValidDiagnostic = (diagnostic: IActivityHistoryDiagnostic) => {
4
+ return (
5
+ diagnostic !== null &&
6
+ diagnostic !== undefined &&
7
+ typeof diagnostic.codi === "string" &&
8
+ typeof diagnostic.cataleg === "string" &&
9
+ typeof diagnostic.descripcio === "string"
10
+ );
11
+ };
12
+
13
+ // Comprueba las propiedades de un diagnóstico
14
+ export const hasValidDiagnostics = (diagnostics: IActivityHistoryDiagnostic[]) => {
15
+ if (diagnostics === null || diagnostics === undefined) {
16
+ return true;
17
+ }
18
+
19
+ return Array.isArray(diagnostics) && diagnostics.every(hasValidDiagnostic);
20
+ };
@@ -1,17 +1,7 @@
1
1
  import { IActivityHistoryItem } from "../model";
2
+ import { isString, isValidDateString } from "./utils";
2
3
 
3
4
  export const isValidBasicActivityHistoryItem = (obj: IActivityHistoryItem) => {
4
- // Función auxiliar para comprobar si una propiedad existe y es del tipo string válido
5
- const isString = (value: any) => typeof value === "string" && value.trim() !== "";
6
-
7
- // Función para comprobar si un string representa una fecha válida
8
- const isValidDateString = (value: any) => {
9
- if (typeof value !== "string" || value.trim() === "") return false;
10
-
11
- const date = new Date(value);
12
- return date instanceof Date && !Number.isNaN(date.getTime());
13
- };
14
-
15
5
  // Comprobación del objeto principal
16
6
  return (
17
7
  obj &&
@@ -1,46 +1,10 @@
1
1
  import { IActivityHistoryItem } from "../model";
2
+ import { hasValidDiagnostics } from "./diagnostics/has-valid-diagnostics";
2
3
  import { isValidBasicActivityHistoryItem } from "./is-valid-basic-history-item";
4
+ import { hasValidProfessional } from "./is-valid-professional";
5
+ import { isBoolean, isString } from "./utils";
3
6
 
4
7
  export const isValidFullActivityHistoryItem = (obj: IActivityHistoryItem) => {
5
- // Función auxiliar para comprobar si una propiedad existe y es del tipo esperado
6
- const isString = (value) => typeof value === "string" && value.trim() !== "";
7
- const isBoolean = (value) => typeof value === "boolean";
8
- const isArray = (value) => Array.isArray(value);
9
-
10
- // Comprueba las propiedades del profesional
11
- const hasValidProfessional = (professional) => {
12
- return (
13
- professional &&
14
- isString(professional.id) &&
15
- isString(professional.name) &&
16
- professional.speciality &&
17
- isString(professional.speciality.id) &&
18
- isString(professional.speciality.description) &&
19
- professional.role &&
20
- isString(professional.role.id) &&
21
- isString(professional.role.description)
22
- );
23
- };
24
-
25
- // Comprueba las propiedades de un diagnóstico
26
- const hasValidDiagnostics = (diagnostics) => {
27
- if (diagnostics === null || diagnostics === undefined) {
28
- return true;
29
- }
30
-
31
- return (
32
- Array.isArray(diagnostics) &&
33
- diagnostics.every(
34
- (diagnostic) =>
35
- diagnostic !== null &&
36
- diagnostic !== undefined &&
37
- typeof diagnostic.codi === "string" &&
38
- typeof diagnostic.cataleg === "string" &&
39
- typeof diagnostic.descripcio === "string",
40
- )
41
- );
42
- };
43
-
44
8
  // Comprueba las propiedades del centro, up, ep y servicio
45
9
  const hasValidCenter = (center) => center && isString(center.id) && isString(center.description);
46
10
  const hasValidUp = (up) => up && isString(up.id) && isString(up.description);
@@ -0,0 +1,16 @@
1
+ import { isString } from "./utils";
2
+
3
+ // Comprueba las propiedades del profesional
4
+ export const hasValidProfessional = (professional) => {
5
+ return (
6
+ professional &&
7
+ isString(professional.id) &&
8
+ isString(professional.name) &&
9
+ professional.speciality &&
10
+ isString(professional.speciality.id) &&
11
+ isString(professional.speciality.description) &&
12
+ professional.role &&
13
+ isString(professional.role.id) &&
14
+ isString(professional.role.description)
15
+ );
16
+ };
@@ -0,0 +1,11 @@
1
+ export const isString = (value) => typeof value === "string" && value.trim() !== "";
2
+ export const isBoolean = (value) => typeof value === "boolean";
3
+ export const isArray = (value) => Array.isArray(value);
4
+
5
+ // Función para comprobar si un string representa una fecha válida
6
+ export const isValidDateString = (value: any) => {
7
+ if (typeof value !== "string" || value.trim() === "") return false;
8
+
9
+ const date = new Date(value);
10
+ return date instanceof Date && !Number.isNaN(date.getTime());
11
+ };
@@ -65,7 +65,7 @@ export const template = (props: ActivityHistoryFilters) =>
65
65
  ${
66
66
  props.diagnosticFilter &&
67
67
  html`<div class="filter">
68
- <dss-input-dropdown icon="" multiple dropdownFixed type="default" .elements=${props.diagnosticFilter?.values} @onInputDropdownChange=${props._handleDiagnosticFilterChange} .selectedValue=${props.diagnosticFilterEnabledValues}>
68
+ <dss-input-dropdown icon="" multiple dropdownFixed type="default" .elements=${props.diagnosticFilter?.values} @onInputDropdownChange=${props._handleDiagnosticFilterChange} .selectedValue=${props.diagnosticFilterEnabledValues} selectorStyle=${"max-height: 450px"}>
69
69
  <label slot="label" for="diagnostic-filter">${props.diagnosticFilter.title}</label>
70
70
  <input id="diagnostic-filter" slot="input" type="text" class="dss-input" autocomplete="off"/>
71
71
  </dss-input-dropdown>
@@ -5,5 +5,9 @@ export function addCustomFiltersReducer(
5
5
  state,
6
6
  action: PayloadAction<IActivityHistoryCustomFilterGroup>,
7
7
  ) {
8
- state.filters = [...state.filters, action.payload];
8
+ const exists = state.filters.some((filter) => filter.id === action.payload.id);
9
+
10
+ if (!exists) {
11
+ state.filters = [...state.filters, action.payload];
12
+ }
9
13
  }
@@ -65,7 +65,13 @@ describe("groupActivityHistoryItems", () => {
65
65
 
66
66
  it("crea subgrupos con mismos diagnósticos", () => {
67
67
  const now = new Date();
68
- const diagnostics = [{ codi: "D1" }];
68
+ const diagnostics = [
69
+ {
70
+ codi: "A123",
71
+ cataleg: "ICD-10",
72
+ descripcio: "Dolor lumbar",
73
+ },
74
+ ];
69
75
  const items = [
70
76
  baseItem({ id: "1", date: now.toISOString(), diagnostics }),
71
77
  baseItem({ id: "2", date: new Date(now.getTime() + 1000).toISOString(), diagnostics }),
@@ -78,10 +84,45 @@ describe("groupActivityHistoryItems", () => {
78
84
  expect(result[0].items).toHaveLength(0);
79
85
  });
80
86
 
87
+ it("no crea subgrupos si alguno de los diagnosticos es invalido", () => {
88
+ const now = new Date();
89
+ const diagnostics1 = [
90
+ {
91
+ codi: "A123",
92
+ cataleg: "ICD-10",
93
+ descripcio: "Dolor lumbar",
94
+ },
95
+ ];
96
+ const diagnostics2 = [
97
+ {
98
+ codi: "A123",
99
+ },
100
+ ];
101
+ const items = [
102
+ baseItem({ id: "1", date: now.toISOString(), diagnostics1 }),
103
+ baseItem({ id: "2", date: new Date(now.getTime() + 1000).toISOString(), diagnostics2 }),
104
+ ];
105
+
106
+ const result = groupActivityHistoryItems(items);
107
+
108
+ expect(result[0].subGroups).toHaveLength(0);
109
+ expect(result[0].items).toHaveLength(2);
110
+ });
111
+
81
112
  it("deja fuera de subgrupos ítems sin diagnóstico", () => {
82
113
  const now = new Date();
83
114
  const items = [
84
- baseItem({ id: "1", date: now.toISOString(), diagnostics: [{ codi: "D1" }] }),
115
+ baseItem({
116
+ id: "1",
117
+ date: now.toISOString(),
118
+ diagnostics: [
119
+ {
120
+ codi: "A123",
121
+ cataleg: "ICD-10",
122
+ descripcio: "Dolor lumbar",
123
+ },
124
+ ],
125
+ }),
85
126
  baseItem({ id: "2", date: new Date(now.getTime() + 1000).toISOString() }),
86
127
  ];
87
128
 
@@ -2,8 +2,9 @@ import {
2
2
  IActivityHistoryItem,
3
3
  IActivityHistoryGroup,
4
4
  IActivityHistorySubGroup,
5
- IActivityHistoryDiagnostic,
6
5
  } from "../../domain/model";
6
+ import { areSameDiagnostics } from "../../domain/validation/diagnostics/are-same-diagnostics";
7
+ import { hasValidDiagnostics } from "../../domain/validation/diagnostics/has-valid-diagnostics";
7
8
 
8
9
  const isSameVisit = (item1: IActivityHistoryItem, item2: IActivityHistoryItem): boolean => {
9
10
  const sameProfessional =
@@ -26,19 +27,44 @@ const withinEightHours = (date1: string, date2: string): boolean => {
26
27
  return timeDifference <= 8 * 60 * 60 * 1000; // 8 hours in milliseconds
27
28
  };
28
29
 
29
- const areSameDiagnostics = (
30
- diag1: IActivityHistoryDiagnostic[],
31
- diag2: IActivityHistoryDiagnostic[],
32
- ): boolean => {
33
- if (diag1.length !== diag2.length) {
34
- return false;
35
- }
30
+ function groupByValidDiagnostics(groups: IActivityHistoryGroup[]) {
31
+ groups.forEach((group) => {
32
+ const subGroups: IActivityHistorySubGroup[] = [];
33
+ const remainingItems: IActivityHistoryItem[] = [];
34
+
35
+ group.items.forEach((item) => {
36
+ const diagnostics = item.diagnostics;
36
37
 
37
- const sortedDiag1 = diag1.map((d) => d.codi).sort();
38
- const sortedDiag2 = diag2.map((d) => d.codi).sort();
38
+ const allDiagnosticsValid = diagnostics?.length > 0 && hasValidDiagnostics(diagnostics);
39
39
 
40
- return sortedDiag1.every((id, index) => id === sortedDiag2[index]);
41
- };
40
+ if (allDiagnosticsValid) {
41
+ let addedToSubGroup = false;
42
+
43
+ for (const subGroup of subGroups) {
44
+ const firstSubGroupItem = subGroup.items[0];
45
+
46
+ if (areSameDiagnostics(firstSubGroupItem.diagnostics, diagnostics)) {
47
+ subGroup.items.push(item);
48
+ addedToSubGroup = true;
49
+ break;
50
+ }
51
+ }
52
+
53
+ if (!addedToSubGroup) {
54
+ subGroups.push({
55
+ idSubGroup: crypto.randomUUID(),
56
+ items: [item],
57
+ });
58
+ }
59
+ } else {
60
+ remainingItems.push(item);
61
+ }
62
+ });
63
+
64
+ group.items = remainingItems;
65
+ group.subGroups = subGroups;
66
+ });
67
+ }
42
68
 
43
69
  const groupActivityHistoryItems = (items: IActivityHistoryItem[]): IActivityHistoryGroup[] => {
44
70
  const groups: IActivityHistoryGroup[] = [];
@@ -85,7 +111,7 @@ const groupActivityHistoryItems = (items: IActivityHistoryItem[]): IActivityHist
85
111
  // Si no se pudo agrupar, crear un nuevo grupo
86
112
  if (!added) {
87
113
  groups.push({
88
- idGroup: Math.random().toString(36).substr(2, 9),
114
+ idGroup: crypto.randomUUID(),
89
115
  items: [item],
90
116
  sameVisit: false,
91
117
  });
@@ -93,38 +119,8 @@ const groupActivityHistoryItems = (items: IActivityHistoryItem[]): IActivityHist
93
119
  });
94
120
 
95
121
  // Crear subgrupos por diagnósticos iguales. Los items que no tienen diagnostico, se quedan fuera de subgrupos (remaining items)
96
- groups.forEach((group) => {
97
- const subGroups: IActivityHistorySubGroup[] = [];
98
- const remainingItems: IActivityHistoryItem[] = [];
99
122
 
100
- group.items.forEach((item) => {
101
- if (item.diagnostics?.length > 0) {
102
- let addedToSubGroup = false;
103
-
104
- for (const subGroup of subGroups) {
105
- const firstSubGroupItem = subGroup.items[0];
106
-
107
- if (areSameDiagnostics(firstSubGroupItem.diagnostics, item.diagnostics)) {
108
- subGroup.items.push(item);
109
- addedToSubGroup = true;
110
- break;
111
- }
112
- }
113
-
114
- if (!addedToSubGroup) {
115
- subGroups.push({
116
- idSubGroup: Math.random().toString(36).substr(2, 9),
117
- items: [item],
118
- });
119
- }
120
- } else {
121
- remainingItems.push(item);
122
- }
123
- });
124
-
125
- group.items = remainingItems;
126
- group.subGroups = subGroups;
127
- });
123
+ groupByValidDiagnostics(groups);
128
124
 
129
125
  return groups;
130
126
  };