@uxland/primary-shell 4.3.1 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/index.js +13397 -12875
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.umd.cjs +1748 -1506
  4. package/dist/index.umd.cjs.map +1 -1
  5. package/dist/primary/shell/src/UI/components/primaria-shell/primaria-shell.d.ts +4 -0
  6. package/dist/primary/shell/src/UI/components/quick-actions-menu/quick-actions-menu.d.ts +1 -0
  7. package/dist/primary/shell/src/UI/shared-components/index.d.ts +0 -1
  8. package/dist/primary/shell/src/api/api.d.ts +4 -2
  9. package/dist/primary/shell/src/api/http-client/http-client.d.ts +7 -1
  10. package/dist/primary/shell/src/api/interaction-service/confirmation-message.d.ts +3 -0
  11. package/dist/primary/shell/src/api/interaction-service/index.d.ts +1 -0
  12. package/dist/primary/shell/src/api/interaction-service/interaction-service-impl.d.ts +7 -0
  13. package/dist/primary/shell/src/api/interaction-service/interaction-service.d.ts +32 -0
  14. package/dist/primary/shell/src/api/notification-service/notification-service.d.ts +6 -0
  15. package/dist/primary/shell/src/api/notification-service/notification.service-impl.d.ts +9 -0
  16. package/dist/primary/shell/src/api/plugin-busy-manager/plugin-busy-list/component.d.ts +2 -4
  17. package/dist/primary/shell/src/api/plugin-busy-manager/plugin-busy-manager.d.ts +17 -1
  18. package/dist/primary/shell/src/api/region-manager/region-manager.d.ts +1 -0
  19. package/dist/primary/shell/src/events.d.ts +2 -0
  20. package/dist/primary/shell/src/features/bootstrapper.d.ts +1 -1
  21. package/dist/primary/shell/src/index.d.ts +1 -2
  22. package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/export-to-pdf/export-pdf-modal/export-pdf-modal.d.ts +1 -5
  23. package/dist/primary/shell/src/internal-plugins/activity-history/localization.d.ts +0 -1
  24. package/dist/primary/shell/src/locales.d.ts +4 -0
  25. package/dist/style.css +1 -1
  26. package/package.json +5 -2
  27. package/src/UI/components/clinical-monitoring/styles.css +4 -2
  28. package/src/UI/components/primaria-shell/primaria-shell.ts +33 -7
  29. package/src/UI/components/primaria-shell/styles.css +15 -2
  30. package/src/UI/components/primaria-shell/template.ts +7 -3
  31. package/src/UI/components/quick-actions-menu/quick-actions-menu.ts +13 -3
  32. package/src/UI/components/shell-header/template.ts +1 -1
  33. package/src/UI/shared-components/index.ts +0 -1
  34. package/src/api/api.ts +12 -9
  35. package/src/api/http-client/http-client.test.ts +188 -76
  36. package/src/api/http-client/http-client.ts +62 -11
  37. package/src/api/interaction-service/confirmation-message.tsx +5 -0
  38. package/src/api/interaction-service/index.ts +1 -0
  39. package/src/api/interaction-service/interaction-service-impl.tsx +203 -0
  40. package/src/api/interaction-service/interaction-service.ts +46 -0
  41. package/src/api/interaction-service/modal-styles.css +69 -0
  42. package/src/api/notification-service/notification-service.ts +6 -0
  43. package/src/api/notification-service/notification.service-impl.ts +45 -0
  44. package/src/api/plugin-busy-manager/plugin-busy-list/component.ts +3 -4
  45. package/src/api/plugin-busy-manager/plugin-busy-list/template.ts +1 -1
  46. package/src/api/plugin-busy-manager/plugin-busy-manager.test.ts +93 -31
  47. package/src/api/plugin-busy-manager/plugin-busy-manager.ts +44 -1
  48. package/src/api/region-manager/region-manager.ts +5 -0
  49. package/src/disposer.ts +1 -1
  50. package/src/events.ts +2 -0
  51. package/src/features/bootstrapper.ts +6 -3
  52. package/src/features/exit/handler.ts +7 -12
  53. package/src/features/get-user-info/handler.ts +3 -4
  54. package/src/index.ts +1 -2
  55. package/src/internal-plugins/activity-history/activity-history-item/export-to-pdf/export-pdf-modal/export-pdf-modal.ts +1 -10
  56. package/src/internal-plugins/activity-history/activity-history-item/export-to-pdf/export-pdf-modal/template.ts +0 -7
  57. package/src/internal-plugins/activity-history/activity-history-item/export-to-pdf/handler.ts +8 -8
  58. package/src/internal-plugins/activity-history/activity-history-item/list/UI/main-view/template.ts +1 -1
  59. package/src/internal-plugins/activity-history/activity-history-item/search/handler.ts +5 -3
  60. package/src/internal-plugins/activity-history/localization.ts +0 -1
  61. package/src/locales.ts +5 -0
  62. package/dist/primary/shell/src/UI/shared-components/primaria-interaction/components/dialog-component.d.ts +0 -19
  63. package/dist/primary/shell/src/UI/shared-components/primaria-interaction/components/notifier-component.d.ts +0 -12
  64. package/dist/primary/shell/src/UI/shared-components/primaria-interaction/confirm-mixin.d.ts +0 -11
  65. package/dist/primary/shell/src/UI/shared-components/primaria-interaction/confirm.d.ts +0 -3
  66. package/dist/primary/shell/src/UI/shared-components/primaria-interaction/index.d.ts +0 -5
  67. package/dist/primary/shell/src/UI/shared-components/primaria-interaction/notify.d.ts +0 -4
  68. package/dist/primary/shell/src/UI/shared-components/primaria-interaction/open-dialog.d.ts +0 -3
  69. package/dist/primary/shell/src/UI/shared-components/primaria-interaction/shared.d.ts +0 -3
  70. package/dist/primary/shell/src/UI/shared-components/primaria-interaction/typings.d.ts +0 -40
  71. package/dist/primary/shell/src/api/interaction-manager/interaction.d.ts +0 -9
  72. package/src/UI/shared-components/primaria-interaction/components/dialog-component-styles.css +0 -104
  73. package/src/UI/shared-components/primaria-interaction/components/dialog-component.ts +0 -138
  74. package/src/UI/shared-components/primaria-interaction/components/notifier-component-styles.css +0 -136
  75. package/src/UI/shared-components/primaria-interaction/components/notifier-component.ts +0 -69
  76. package/src/UI/shared-components/primaria-interaction/confirm-mixin.ts +0 -35
  77. package/src/UI/shared-components/primaria-interaction/confirm.ts +0 -9
  78. package/src/UI/shared-components/primaria-interaction/index.ts +0 -5
  79. package/src/UI/shared-components/primaria-interaction/notify.ts +0 -153
  80. package/src/UI/shared-components/primaria-interaction/open-dialog.ts +0 -8
  81. package/src/UI/shared-components/primaria-interaction/shared.ts +0 -29
  82. package/src/UI/shared-components/primaria-interaction/typings.ts +0 -46
  83. package/src/api/interaction-manager/interaction.ts +0 -26
@@ -2,28 +2,76 @@ import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
2
2
  import { TokenManager } from "../token-manager/token-manager";
3
3
  import { shellEvents } from "../../events";
4
4
  import { PrimariaBroker } from "../broker/primaria-broker";
5
+ import { jwtDecode } from "jwt-decode";
5
6
 
6
- export const createAxiosInstance = (tokenManager: TokenManager, broker: PrimariaBroker) => {
7
+ const mpidHeader = "x-catsalut-mpid";
8
+ const obtainMpid = (token: string) => {
9
+ const mpid = (jwtDecode(token) as any).access_info?.mpi_patient_id;
10
+ return mpid;
11
+ };
12
+
13
+ export class MissingMpidHeaderError extends Error {
14
+ constructor(message: string) {
15
+ super(message);
16
+ this.name = "MissingMpidHeaderError";
17
+ }
18
+ }
19
+
20
+ export class InvalidMpidHeaderError extends Error {
21
+ constructor(message: string) {
22
+ super(message);
23
+ this.name = "InvalidMpidHeaderError";
24
+ }
25
+ }
26
+ const validateMpidHeader = (response: AxiosResponse, tokenManager: TokenManager): boolean => {
27
+ const mpidHeaderValue = response.headers[mpidHeader];
28
+ if (!mpidHeaderValue) {
29
+ throw new MissingMpidHeaderError("Mpid header is missing");
30
+ }
31
+ if (mpidHeaderValue !== obtainMpid(tokenManager.getToken())) {
32
+ throw new InvalidMpidHeaderError("Mpid header value is invalid");
33
+ }
34
+ return true;
35
+ };
36
+
37
+ export const createAxiosInstance = (
38
+ tokenManager: TokenManager,
39
+ broker: PrimariaBroker,
40
+ validateMpid: boolean = false,
41
+ ) => {
7
42
  const instance = axios.create();
8
43
  instance.interceptors.request.use((config) => {
9
44
  config.headers.Authorization = `Bearer ${tokenManager.getToken()}`;
10
45
  return config;
11
46
  });
12
-
47
+
13
48
  instance.interceptors.response.use(
14
- (response: AxiosResponse) => response,
49
+ (response: AxiosResponse) => {
50
+ try {
51
+ if (validateMpid) validateMpidHeader(response, tokenManager);
52
+ return response;
53
+ } catch (error) {
54
+ if (error instanceof InvalidMpidHeaderError) {
55
+ broker.publish(shellEvents.mpidHeaderInvalid, {
56
+ request: response.config,
57
+ });
58
+ }
59
+ return Promise.reject(error);
60
+ }
61
+ },
15
62
  async (error) => {
16
63
  const originalRequest = error.config;
17
- if (error.response.status === 401 && !originalRequest._retry) {
64
+ if (error.response?.status === 401 && !originalRequest._retry) {
18
65
  originalRequest._retry = true;
19
- // Implement token refresh logic here and update the token in headers
20
66
  try {
21
- const newToken = await tokenManager.refreshToken();
22
- originalRequest.headers.Authorization = `Bearer ${newToken}`;
23
- return instance(originalRequest);
67
+ const newToken = await tokenManager.refreshToken();
68
+ originalRequest.headers.Authorization = `Bearer ${newToken}`;
69
+ return instance(originalRequest);
24
70
  } catch (refreshError) {
25
71
  console.error("Error refreshing token:", refreshError);
26
- broker.publish(shellEvents.refreshTokenFailed, {request: originalRequest});
72
+ broker.publish(shellEvents.refreshTokenFailed, {
73
+ request: originalRequest,
74
+ });
27
75
  return Promise.reject(error);
28
76
  }
29
77
  }
@@ -41,9 +89,12 @@ let instance;
41
89
  export interface HttpClient {
42
90
  request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
43
91
  }
44
- export const createHttpClient = (tokenManager: TokenManager, broker: PrimariaBroker): HttpClient => {
92
+ export const createHttpClient = (
93
+ tokenManager: TokenManager,
94
+ broker: PrimariaBroker,
95
+ ): HttpClient => {
45
96
  if (!instance) {
46
97
  instance = createAxiosInstance(tokenManager, broker);
47
98
  }
48
99
  return { request: instance.request };
49
- };
100
+ };
@@ -0,0 +1,5 @@
1
+ import { ConfirmationContentProps } from "./interaction-service.ts";
2
+
3
+ export const ConfirmationMessage = ({ data }: ConfirmationContentProps<string>) => {
4
+ return (<div>{data}</div>)
5
+ }
@@ -0,0 +1 @@
1
+ export * from "./interaction-service";
@@ -0,0 +1,203 @@
1
+ import {
2
+ ConfirmationContentProps,
3
+ ConfirmationOptions,
4
+ ConfirmationResult,
5
+ ConfirmationWithResultContentProps,
6
+ PrimariaInteractionService,
7
+ } from "./interaction-service";
8
+ import * as React from "react";
9
+ import { useEffect, useState, useRef } from "react";
10
+ import { createRoot } from "react-dom/client";
11
+ import { ConfirmationMessage } from "./confirmation-message.tsx";
12
+ import modalStyles from './modal-styles.css?raw';
13
+
14
+ const defaultOptions: ConfirmationOptions = {
15
+ title: "Confirmació",
16
+ showConfirmButton: true,
17
+ showCancelButton: true,
18
+ confirmButtonText: "Acceptar",
19
+ cancelButtonText: "Cancel·lar",
20
+ showCloseButton: true,
21
+ fullCustomization: false,
22
+ closeOnOutsideClick: false,
23
+ };
24
+
25
+ export class ParimariaInteractionServiceImpl extends PrimariaInteractionService {
26
+ confirm<TData = undefined, TResult = undefined>(
27
+ data: TData | undefined,
28
+ DialogComponent:
29
+ | React.ComponentType<ConfirmationContentProps<TData, TResult> | ConfirmationWithResultContentProps<TData, TResult>>
30
+ | (new () => HTMLElement),
31
+ options?: ConfirmationOptions | undefined,
32
+ ): Promise<ConfirmationResult<TResult | undefined>> {
33
+ const finalOptions: ConfirmationOptions = { ...defaultOptions, ...(options || {}) };
34
+ return new Promise((resolve) => {
35
+ const div = document.createElement("div");
36
+ document.body.appendChild(div);
37
+ const style = document.createElement("style");
38
+ style.textContent = modalStyles;
39
+
40
+ document.head.appendChild(style);
41
+
42
+ const DialogWrapper = () => {
43
+ const [result, setResult] = useState<ConfirmationResult<TResult | undefined>>();
44
+ const [isValid, setIsValid] = useState(true);
45
+ const dialogComponentRef = useRef(null);
46
+ const dialogRef = useRef<HTMLDivElement>(null);
47
+ const modalRef = useRef<HTMLDivElement>(null);
48
+
49
+ const isWebComponent =
50
+ typeof DialogComponent === "function" && DialogComponent.prototype instanceof HTMLElement;
51
+
52
+ useEffect(() => {
53
+ window.addEventListener('keydown', handleKeydown);
54
+ }, []);
55
+
56
+ const handleKeydown = (event: KeyboardEvent) => {
57
+ if (event.key === 'Escape') {
58
+ handleClose(false);
59
+ }
60
+ };
61
+ const handleOutsideClick = (event: Event) => {
62
+ if (dialogRef.current) {
63
+ if (event.target === modalRef.current && finalOptions.closeOnOutsideClick) {
64
+ handleClose(false);
65
+ }
66
+ }
67
+ };
68
+
69
+ useEffect(() => {
70
+ if (isWebComponent && dialogComponentRef.current) {
71
+ const parent = dialogComponentRef.current;
72
+
73
+ // ⚠ Aquí creamos la instancia del Web Component manualmente
74
+ const wcElement = new (DialogComponent as any)();
75
+
76
+ // Asignar propiedades manualmente
77
+ (wcElement as any).data = data;
78
+ (wcElement as any).setResult = setResult;
79
+ (wcElement as any).setIsValid = setIsValid;
80
+ (wcElement as any).confirm = () => isValid && handleClose(true);
81
+ (wcElement as any).confirmResult = (result) => isValid && handleCloseWithResult(result);
82
+ (wcElement as any).cancel = () => handleClose(false);
83
+
84
+ // Agregar el Web Component al DOM
85
+ parent.appendChild(wcElement);
86
+
87
+ return () => {
88
+ parent.removeChild(wcElement);
89
+ };
90
+ }
91
+ }, [DialogComponent, data]);
92
+
93
+ const handleClose = (confirmed: boolean) => {
94
+ resolvePromise(result, confirmed);
95
+ };
96
+
97
+ const handleCloseWithResult = (finalResult: TResult) => {
98
+ resolvePromise(finalResult, true);
99
+ };
100
+
101
+ const resolvePromise = (finalResult: TResult, confirmed: boolean) => {
102
+ setTimeout(() => {
103
+ const confirmationResult = {
104
+ result: confirmed ? finalResult : undefined,
105
+ confirmed: confirmed,
106
+ };
107
+ resolve(confirmationResult);
108
+ document.body.removeChild(div);
109
+ document.head.removeChild(style);
110
+ window.removeEventListener('keydown', handleKeydown);
111
+ }, 300);
112
+ };
113
+
114
+ const getConfirmButtonVariant = () => {
115
+ if (finalOptions.state === "error") return "error";
116
+ if (finalOptions.state === "success") return "success";
117
+ if (finalOptions.state === "info") return "primary";
118
+ if (finalOptions.state === "alert") return "warning";
119
+ return "primary";
120
+ };
121
+
122
+ const getCancelButtonVariant = () => {
123
+ if (finalOptions.state) return "alternative-dark";
124
+ return "secondary";
125
+ };
126
+
127
+ const _renderContent = () => {
128
+ return isWebComponent ? (
129
+ <div ref={dialogComponentRef} />
130
+ ) : (
131
+ <DialogComponent
132
+ {...{
133
+ data,
134
+ setResult,
135
+ setIsValid,
136
+ confirm: () => isValid && handleClose(true),
137
+ cancel: () => handleClose(false),
138
+ }}
139
+ />
140
+ );
141
+ };
142
+ return (
143
+ <div className="modal" ref={modalRef} onClick={handleOutsideClick} >
144
+ <div className="dialog" ref={dialogRef}>
145
+ {!finalOptions.fullCustomization && (
146
+ <div className="dss-dialog-header">
147
+ <div className="dss-dialog-header-title">{finalOptions.title}</div>
148
+ {finalOptions.showCloseButton && (
149
+ <dss-icon-button
150
+ class="dss-dialog-header-close"
151
+ size="md"
152
+ variant="neutral"
153
+ icon="close"
154
+ onClick={() => handleClose(false)}
155
+ ></dss-icon-button>
156
+ )}
157
+ </div>
158
+ )}
159
+ <div className={`modal-content ${finalOptions.fullCustomization ? 'full-customization' : ''}`}>
160
+ {_renderContent()}
161
+ </div>
162
+ {!finalOptions.fullCustomization && (
163
+ <div
164
+ className="dss-modal-footer"
165
+ hidden={!finalOptions.showCancelButton && !finalOptions.showConfirmButton}
166
+ >
167
+ {finalOptions.showCancelButton && (
168
+ <dss-button
169
+ label={finalOptions.cancelButtonText}
170
+ onClick={() => handleClose(false)}
171
+ size="md"
172
+ variant={getCancelButtonVariant()}
173
+ />
174
+ )}
175
+ {finalOptions.showConfirmButton && (
176
+ <dss-button
177
+ onClick={() => isValid && handleClose(true)}
178
+ label={finalOptions.confirmButtonText}
179
+ disabled={!isValid}
180
+ size="md"
181
+ variant={getConfirmButtonVariant()}
182
+ />
183
+ )}
184
+ </div>
185
+ )}
186
+ </div>
187
+ </div>
188
+ );
189
+ };
190
+
191
+
192
+ const root = createRoot(div);
193
+ root.render(<DialogWrapper />);
194
+ });
195
+ }
196
+
197
+ confirmMessage(
198
+ message: string,
199
+ options?: ConfirmationOptions | undefined,
200
+ ): Promise<ConfirmationResult> {
201
+ return this.confirm(message, ConfirmationMessage, options);
202
+ }
203
+ }
@@ -0,0 +1,46 @@
1
+ import * as React from "react";
2
+
3
+ export interface ConfirmationResult<T = undefined> {
4
+ confirmed: boolean;
5
+ result: T | undefined;
6
+ }
7
+
8
+ export interface ConfirmationContentProps<TData, TResult = undefined> {
9
+ data: TData | undefined;
10
+ setResult: (result: TResult) => void;
11
+ setIsValid: (isValid: boolean) => void;
12
+ confirm: () => void;
13
+ cancel: () => void;
14
+ }
15
+
16
+ export interface ConfirmationWithResultContentProps<TData, TResult = undefined> {
17
+ data: TData | undefined;
18
+ confirmResult: (result: TResult) => void;
19
+ cancel: () => void;
20
+ }
21
+
22
+ export interface ConfirmationOptions {
23
+ title?: string | undefined;
24
+ showConfirmButton?: boolean | undefined;
25
+ showCancelButton?: boolean | undefined;
26
+ confirmButtonText?: string | undefined;
27
+ cancelButtonText?: string | undefined;
28
+ showCloseButton?: boolean | undefined;
29
+ fullCustomization?: boolean | undefined;
30
+ closeOnOutsideClick?: boolean | undefined;
31
+ state?: "success" | "info" | "alert" | "error";
32
+ }
33
+
34
+ export abstract class PrimariaInteractionService {
35
+ abstract confirm<TData = undefined, TResult = undefined>(
36
+ data: TData | undefined,
37
+ dialogComponent:
38
+ | React.ComponentType<ConfirmationContentProps<TData, TResult> | ConfirmationWithResultContentProps<TData, TResult>>
39
+ | (new () => HTMLElement),
40
+ options?: ConfirmationOptions | undefined,
41
+ ): Promise<ConfirmationResult<TResult | undefined>>;
42
+ abstract confirmMessage(
43
+ message: string,
44
+ options?: ConfirmationOptions | undefined,
45
+ ): Promise<ConfirmationResult>;
46
+ }
@@ -0,0 +1,69 @@
1
+ .modal {
2
+ font-family: "Open Sans";
3
+ display: block;
4
+ position: fixed;
5
+ z-index: 400;
6
+ left: 0;
7
+ top: 0;
8
+ width: 100%;
9
+ height: 100%;
10
+ background-color: rgba(0, 0, 0, 0.65);
11
+ }
12
+ .dialog {
13
+ display: flex;
14
+ flex-direction: column;
15
+ position: absolute;
16
+ top: 50%;
17
+ left: 50%;
18
+ transform: translate(-50%, -50%);
19
+ z-index: 21;
20
+ background: #fff;
21
+ border-radius: 16px;
22
+ width: auto;
23
+ height: auto;
24
+ max-height: 95%;
25
+ min-width: 400px;
26
+ box-shadow: 0 8px 12px 6px rgba(0 0 0 / 5%), 0 4px 4px rgba(0 0 0 / 10%);
27
+ }
28
+ .dss-dialog-header {
29
+ position: relative;
30
+ display: flex;
31
+ flex-direction: row;
32
+ justify-content: center;
33
+ align-items: center;
34
+ padding: 24px;
35
+ border-top-left-radius: 16px;
36
+ border-top-right-radius: 16px;
37
+ }
38
+ .dss-dialog-header-title {
39
+ display: flex;
40
+ flex-direction: column;
41
+ justify-content: center;
42
+ align-items: center;
43
+ gap: var(--dss-spacing-xs);
44
+ font-size: 20px;
45
+ line-height: 30px;
46
+ font-weight: var(--font-bold);
47
+ color: var(--color-neutral-900);
48
+ }
49
+ .dss-dialog-header-close {
50
+ position: absolute;
51
+ top: var(--dss-spacing-lg);
52
+ right: calc(var(--dss-spacing-lg) - 4px);
53
+ }
54
+ .modal-content {
55
+ flex: 1;
56
+ padding: 0 24px;
57
+ }
58
+ .modal-content.full-customization {
59
+ padding: 0;
60
+ }
61
+ .dss-modal-footer {
62
+ display: flex;
63
+ justify-content: center;
64
+ align-items: center;
65
+ gap: 16px;
66
+ padding: 24px;
67
+ border-bottom-left-radius: 16px;
68
+ border-bottom-right-radius: 16px;
69
+ }
@@ -0,0 +1,6 @@
1
+ export abstract class PrimariaNotificationService {
2
+ abstract info(message: string): void;
3
+ abstract warning(message: string): void;
4
+ abstract error(message: string): void;
5
+ abstract success(message: string): void;
6
+ }
@@ -0,0 +1,45 @@
1
+ import { PrimariaNotificationService } from "./notification-service";
2
+
3
+ export class PrimariaNotificationServiceImpl extends PrimariaNotificationService {
4
+ private notify(
5
+ message: string,
6
+ state: "info" | "warning" | "error" | "success",
7
+ duration = 3000,
8
+ ): void {
9
+ // Crear el Web Component
10
+ const toast = document.createElement("dss-toast");
11
+ toast.setAttribute("isshow", "true");
12
+ toast.setAttribute("state", state);
13
+ toast.setAttribute("position", "bottom-left");
14
+ toast.setAttribute("text", message);
15
+ toast.setAttribute("hasicon", "true");
16
+ toast.setAttribute("duration", duration.toString());
17
+
18
+ // Agregar al body
19
+ document.body.appendChild(toast);
20
+
21
+ // Remover después del tiempo de duración
22
+ setTimeout(() => {
23
+ toast.setAttribute("isshow", "false");
24
+ setTimeout(() => {
25
+ toast.remove();
26
+ }, 300); // Espera un poco para animación de salida (ajustable)
27
+ }, duration);
28
+ }
29
+
30
+ info(message: string, duration?: number): void {
31
+ this.notify(message, "info", duration);
32
+ }
33
+
34
+ warning(message: string, duration?: number): void {
35
+ this.notify(message, "warning", duration);
36
+ }
37
+
38
+ error(message: string, duration?: number): void {
39
+ this.notify(message, "error", duration);
40
+ }
41
+
42
+ success(message: string, duration?: number): void {
43
+ this.notify(message, "success", duration);
44
+ }
45
+ }
@@ -1,12 +1,11 @@
1
1
  import { LitElement, css, html, unsafeCSS } from "lit";
2
2
  import { customElement } from "lit/decorators.js";
3
+ import { PluginBusyTask } from "../plugin-busy-manager";
3
4
  import styles from "./styles.css?inline";
4
5
  import { template } from "./template";
5
- import { confirmMixin } from "../../../UI/shared-components/primaria-interaction/confirm-mixin";
6
- import { PluginBusyTask } from "../plugin-busy-manager";
7
6
 
8
7
  @customElement("plugin-busy-list")
9
- export class PluginBusyList extends confirmMixin(LitElement) {
8
+ export class PluginBusyList extends LitElement {
10
9
  static styles = css`
11
10
  ${unsafeCSS(styles)}
12
11
  `;
@@ -15,5 +14,5 @@ export class PluginBusyList extends confirmMixin(LitElement) {
15
14
  return html`${template(this)}`;
16
15
  }
17
16
 
18
- model: { busyTasks: PluginBusyTask[] };
17
+ data: { busyTasks: PluginBusyTask[] };
19
18
  }
@@ -7,7 +7,7 @@ export const template = (props: PluginBusyList) => html`
7
7
  <div class="container">
8
8
  <div class="title">${translate("busyManager.title")}</div>
9
9
  <div class="list">
10
- ${props.model?.busyTasks?.map((item: PluginBusyTask) => html`<div class="plugin-busy-item">${item.taskDescription}</div>`)}
10
+ ${props.data?.busyTasks?.map((item: PluginBusyTask) => html`<div class="plugin-busy-item">${item.taskDescription}</div>`)}
11
11
  </div>
12
12
  </div>
13
13
  `;
@@ -1,49 +1,111 @@
1
- import { describe, it, expect, beforeEach } from "vitest";
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
2
  import { PluginBusyManagerImpl } from "./plugin-busy-manager";
3
+ import { shellEvents } from "../../events";
3
4
 
4
5
  describe("PluginBusyManagerImpl", () => {
6
+ let brokerMock: any;
5
7
  let manager: PluginBusyManagerImpl;
6
8
 
7
9
  beforeEach(() => {
8
- manager = new PluginBusyManagerImpl();
10
+ brokerMock = {
11
+ publish: vi.fn(),
12
+ };
13
+ manager = new PluginBusyManagerImpl(brokerMock);
9
14
  });
10
15
 
11
- it("should start with no busy plugins", () => {
12
- expect(manager.getBusyPluginTasks()).toEqual([]);
13
- expect(manager.isAnyPluginBusy()).toBe(false);
16
+ describe("addBusyPluginTask", () => {
17
+ it("should add a plugin busy task", () => {
18
+ const task = { taskId: "1", taskDescription: "Loading plugin" };
19
+ manager.addBusyPluginTask(task);
20
+ expect(manager.getBusyPluginTasks()).toContain(task);
21
+ });
14
22
  });
15
23
 
16
- it("should add a busy plugin", () => {
17
- const task = { taskId: "plugin1", taskDescription: "Description for plugin1" };
18
- manager.addBusyPluginTask(task);
19
- expect(manager.getBusyPluginTasks()).toContainEqual(task);
20
- expect(manager.isAnyPluginBusy()).toBe(true);
24
+ describe("removeBusyPluginTask", () => {
25
+ it("should remove a plugin busy task if it exists", () => {
26
+ const task = { taskId: "1", taskDescription: "Loading plugin" };
27
+ manager.addBusyPluginTask(task);
28
+ manager.removeBusyPluginTask("1");
29
+ expect(manager.getBusyPluginTasks()).not.toContain(task);
30
+ });
31
+
32
+ it("should do nothing if task does not exist", () => {
33
+ const task = { taskId: "1", taskDescription: "Loading plugin" };
34
+ manager.addBusyPluginTask(task);
35
+ manager.removeBusyPluginTask("non-existent");
36
+ expect(manager.getBusyPluginTasks()).toContain(task);
37
+ });
38
+ });
39
+
40
+ describe("addBusyQuickActionTask", () => {
41
+ it("should add a quick action busy task and emit event", () => {
42
+ const task = { taskId: "qa1" };
43
+ manager.addBusyQuickActionTask(task);
44
+ expect(brokerMock.publish).toHaveBeenCalledWith(shellEvents.quickActionBusyChanged, {
45
+ busy: true,
46
+ });
47
+ });
48
+ });
49
+
50
+ describe("removeBusyQuickActionTask", () => {
51
+ it("should remove a quick action task and emit event", () => {
52
+ const task = { taskId: "qa1" };
53
+ manager.addBusyQuickActionTask(task);
54
+ manager.removeBusyQuickActionTask("qa1");
55
+ expect(brokerMock.publish).toHaveBeenCalledWith(shellEvents.quickActionBusyChanged, {
56
+ busy: false,
57
+ });
58
+ });
59
+
60
+ it("should do nothing and not emit event if task does not exist", () => {
61
+ manager.removeBusyQuickActionTask("non-existent");
62
+ expect(brokerMock.publish).not.toHaveBeenCalled();
63
+ });
64
+ });
65
+
66
+ describe("isAnyPluginBusy", () => {
67
+ it("should return false when no plugin tasks exist", () => {
68
+ expect(manager.isAnyPluginBusy()).toBe(false);
69
+ });
70
+
71
+ it("should return true when plugin tasks exist", () => {
72
+ manager.addBusyPluginTask({ taskId: "1", taskDescription: "Busy" });
73
+ expect(manager.isAnyPluginBusy()).toBe(true);
74
+ });
75
+ });
76
+
77
+ describe("isAnyQuickActionBusy", () => {
78
+ it("should return false when no quick action tasks exist", () => {
79
+ expect(manager.isAnyQuickActionBusy()).toBe(false);
80
+ });
81
+
82
+ it("should return true when quick action tasks exist", () => {
83
+ manager.addBusyQuickActionTask({ taskId: "qa1" });
84
+ expect(manager.isAnyQuickActionBusy()).toBe(true);
85
+ });
21
86
  });
22
87
 
23
- it("should remove a busy plugin", () => {
24
- const task1 = { taskId: "plugin1", taskDescription: "Description for plugin1" };
25
- const task2 = { taskId: "plugin2", taskDescription: "Description for plugin2" };
26
- manager.addBusyPluginTask(task1);
27
- manager.addBusyPluginTask(task2);
28
- manager.removeBusyPluginTask("plugin1");
29
- expect(manager.getBusyPluginTasks()).not.toContainEqual(task1);
30
- expect(manager.getBusyPluginTasks()).toContainEqual(task2);
88
+ describe("clearAllBusyPlugins", () => {
89
+ it("should clear all plugin busy tasks", () => {
90
+ manager.addBusyPluginTask({ taskId: "1", taskDescription: "Busy" });
91
+ manager.clearAllBusyPlugins();
92
+ expect(manager.getBusyPluginTasks()).toHaveLength(0);
93
+ });
31
94
  });
32
95
 
33
- it("should do nothing if removing a non-existent plugin", () => {
34
- const task = { taskId: "plugin1", taskDescription: "Description for plugin1" };
35
- manager.addBusyPluginTask(task);
36
- manager.removeBusyPluginTask("nonexistent");
37
- expect(manager.getBusyPluginTasks()).toEqual([task]);
96
+ describe("clearAllBusyQuickActions", () => {
97
+ it("should clear all quick action busy tasks", () => {
98
+ manager.addBusyQuickActionTask({ taskId: "qa1" });
99
+ manager.clearAllBusyQuickActions();
100
+ expect(manager.isAnyQuickActionBusy()).toBe(false);
101
+ });
38
102
  });
39
103
 
40
- it("should clear all busy plugins", () => {
41
- const task1 = { taskId: "plugin1", taskDescription: "Description for plugin1" };
42
- const task2 = { taskId: "plugin2", taskDescription: "Description for plugin2" };
43
- manager.addBusyPluginTask(task1);
44
- manager.addBusyPluginTask(task2);
45
- manager.clearAllBusyPlugins();
46
- expect(manager.getBusyPluginTasks()).toEqual([]);
47
- expect(manager.isAnyPluginBusy()).toBe(false);
104
+ describe("getBusyPluginTasks", () => {
105
+ it("should return current plugin tasks", () => {
106
+ const task = { taskId: "1", taskDescription: "Loading plugin" };
107
+ manager.addBusyPluginTask(task);
108
+ expect(manager.getBusyPluginTasks()).toEqual([task]);
109
+ });
48
110
  });
49
111
  });