@uxland/primary-shell 4.2.0 → 4.3.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/index.js +1786 -1590
- package/dist/index.js.map +1 -1
- package/dist/index.umd.cjs +226 -208
- package/dist/index.umd.cjs.map +1 -1
- package/dist/primary/shell/src/UI/components/shell-header/shell-header.d.ts +1 -0
- package/dist/primary/shell/src/api/api.d.ts +2 -0
- package/dist/primary/shell/src/api/http-client/http-client.d.ts +3 -2
- package/dist/primary/shell/src/api/plugin-busy-manager/plugin-busy-list/component.d.ts +12 -0
- package/dist/primary/shell/src/api/plugin-busy-manager/plugin-busy-list/template.d.ts +3 -0
- package/dist/primary/shell/src/api/plugin-busy-manager/plugin-busy-manager.d.ts +19 -0
- package/dist/primary/shell/src/api/plugin-busy-manager/plugin-busy-manager.test.d.ts +1 -0
- package/dist/primary/shell/src/api/token-manager/token-manager.d.ts +1 -1
- package/dist/primary/shell/src/events.d.ts +1 -0
- package/dist/primary/shell/src/features/exit/bootstrapper.d.ts +2 -0
- package/dist/primary/shell/src/features/exit/handler.d.ts +10 -0
- package/dist/primary/shell/src/features/exit/request.d.ts +4 -0
- package/dist/primary/shell/src/handle-plugins.d.ts +2 -2
- package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/add/add-async-history-items/validate-add-async-items-command.d.ts +4 -0
- package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/add/add-history-items/reducer.d.ts +1 -1
- package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/domain/model.d.ts +3 -0
- package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/filter/UI/active-filters-badges/active-filters-badges.d.ts +4 -1
- package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/filter/UI/active-filters-header/active-filters-header.d.ts +1 -2
- package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/filter/common-filters/selectors.d.ts +152 -0
- package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/filter/utils.d.ts +2 -0
- package/dist/primary/shell/src/internal-plugins/activity-history/activity-history-item/list/UI/timeline/activity-history-timeline.d.ts +2 -0
- package/dist/primary/shell/src/internal-plugins/activity-history/handle-views.d.ts +4 -0
- package/dist/primary/shell/src/internal-plugins/activity-history/infrastructure/ioc/container.d.ts +1 -0
- package/dist/primary/shell/src/internal-plugins/activity-history/infrastructure/ioc/types.d.ts +0 -1
- package/dist/primary/shell/src/internal-plugins/activity-history/localization.d.ts +1 -0
- package/dist/primary/shell/src/internal-plugins/activity-history/main.d.ts +0 -1
- package/dist/primary/shell/src/locales.d.ts +4 -0
- package/package.json +1 -1
- package/src/UI/components/index.ts +3 -2
- package/src/UI/components/shell-header/shell-header.ts +5 -0
- package/src/UI/components/shell-header/template.ts +2 -2
- package/src/api/api.ts +10 -3
- package/src/api/broker/factory.ts +1 -1
- package/src/api/http-client/http-client.test.ts +60 -6
- package/src/api/http-client/http-client.ts +17 -10
- package/src/api/plugin-busy-manager/plugin-busy-list/component.ts +19 -0
- package/src/api/plugin-busy-manager/plugin-busy-list/styles.css +20 -0
- package/src/api/plugin-busy-manager/plugin-busy-list/template.ts +13 -0
- package/src/api/plugin-busy-manager/plugin-busy-manager.test.ts +49 -0
- package/src/api/plugin-busy-manager/plugin-busy-manager.ts +38 -0
- package/src/api/token-manager/token-manager.test.ts +0 -12
- package/src/api/token-manager/token-manager.ts +12 -7
- package/src/disposer.ts +0 -1
- package/src/events.ts +1 -0
- package/src/features/bootstrapper.ts +3 -0
- package/src/features/exit/bootstrapper.ts +17 -0
- package/src/features/exit/handler.ts +51 -0
- package/src/features/exit/request.ts +3 -0
- package/src/handle-plugins.ts +7 -6
- package/src/handle-views.ts +4 -1
- package/src/internal-plugins/activity-history/activity-history-item/add/add-async-history-items/handler.ts +2 -0
- package/src/internal-plugins/activity-history/activity-history-item/add/add-async-history-items/validate-add-async-items-command.ts +15 -0
- package/src/internal-plugins/activity-history/activity-history-item/add/add-history-items/reducer.ts +10 -7
- package/src/internal-plugins/activity-history/activity-history-item/domain/model.ts +4 -0
- package/src/internal-plugins/activity-history/activity-history-item/filter/UI/active-filters-badges/active-filters-badges.ts +13 -5
- package/src/internal-plugins/activity-history/activity-history-item/filter/UI/active-filters-badges/template.ts +26 -2
- package/src/internal-plugins/activity-history/activity-history-item/filter/UI/active-filters-header/active-filters-header.ts +5 -8
- package/src/internal-plugins/activity-history/activity-history-item/filter/UI/active-filters-header/template.ts +6 -1
- package/src/internal-plugins/activity-history/activity-history-item/filter/common-filters/selectors.ts +10 -0
- package/src/internal-plugins/activity-history/activity-history-item/filter/custom-filters/set-custom-filter-value/reducer.ts +7 -1
- package/src/internal-plugins/activity-history/activity-history-item/filter/utils.ts +9 -0
- package/src/internal-plugins/activity-history/activity-history-item/list/UI/timeline/activity-history-timeline.ts +30 -0
- package/src/internal-plugins/activity-history/activity-history-item/list/UI/timeline/template.ts +7 -4
- package/src/internal-plugins/activity-history/handle-views.ts +17 -0
- package/src/internal-plugins/activity-history/infrastructure/ioc/container.ts +5 -0
- package/src/internal-plugins/activity-history/infrastructure/ioc/types.ts +0 -1
- package/src/internal-plugins/activity-history/localization.ts +7 -8
- package/src/internal-plugins/activity-history/main.ts +4 -10
- package/src/locales.ts +8 -4
- package/dist/primary/shell/src/internal-plugins/activity-history/utils/get-locale-manager-dependency.d.ts +0 -1
- package/src/internal-plugins/activity-history/utils/get-locale-manager-dependency.ts +0 -7
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
|
|
2
2
|
import { TokenManager } from "../token-manager/token-manager";
|
|
3
|
+
import { shellEvents } from "../../events";
|
|
4
|
+
import { PrimariaBroker } from "../broker/primaria-broker";
|
|
3
5
|
|
|
4
|
-
export const createAxiosInstance = (tokenManager: TokenManager) => {
|
|
5
|
-
const instance = axios.create(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
export const createAxiosInstance = (tokenManager: TokenManager, broker: PrimariaBroker) => {
|
|
7
|
+
const instance = axios.create();
|
|
8
|
+
instance.interceptors.request.use((config) => {
|
|
9
|
+
config.headers.Authorization = `Bearer ${tokenManager.getToken()}`;
|
|
10
|
+
return config;
|
|
9
11
|
});
|
|
12
|
+
|
|
10
13
|
instance.interceptors.response.use(
|
|
11
14
|
(response: AxiosResponse) => response,
|
|
12
15
|
async (error) => {
|
|
@@ -14,9 +17,15 @@ export const createAxiosInstance = (tokenManager: TokenManager) => {
|
|
|
14
17
|
if (error.response.status === 401 && !originalRequest._retry) {
|
|
15
18
|
originalRequest._retry = true;
|
|
16
19
|
// Implement token refresh logic here and update the token in headers
|
|
20
|
+
try {
|
|
17
21
|
const newToken = await tokenManager.refreshToken();
|
|
18
22
|
originalRequest.headers.Authorization = `Bearer ${newToken}`;
|
|
19
23
|
return instance(originalRequest);
|
|
24
|
+
} catch (refreshError) {
|
|
25
|
+
console.error("Error refreshing token:", refreshError);
|
|
26
|
+
broker.publish(shellEvents.refreshTokenFailed, {request: originalRequest});
|
|
27
|
+
return Promise.reject(error);
|
|
28
|
+
}
|
|
20
29
|
}
|
|
21
30
|
return Promise.reject(error);
|
|
22
31
|
},
|
|
@@ -29,14 +38,12 @@ let instance;
|
|
|
29
38
|
|
|
30
39
|
//TODO: Test concurrent request that get intercepted by expired token and
|
|
31
40
|
// An option is to use subscribers to enque unauth requests and only refresh one token and retry the others with the new token
|
|
32
|
-
|
|
33
|
-
|
|
34
41
|
export interface HttpClient {
|
|
35
42
|
request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
|
|
36
43
|
}
|
|
37
|
-
export const createHttpClient = (tokenManager: TokenManager): HttpClient => {
|
|
44
|
+
export const createHttpClient = (tokenManager: TokenManager, broker: PrimariaBroker): HttpClient => {
|
|
38
45
|
if (!instance) {
|
|
39
|
-
instance = createAxiosInstance(tokenManager);
|
|
46
|
+
instance = createAxiosInstance(tokenManager, broker);
|
|
40
47
|
}
|
|
41
48
|
return { request: instance.request };
|
|
42
|
-
};
|
|
49
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { LitElement, css, html, unsafeCSS } from "lit";
|
|
2
|
+
import { customElement } from "lit/decorators.js";
|
|
3
|
+
import styles from "./styles.css?inline";
|
|
4
|
+
import { template } from "./template";
|
|
5
|
+
import { confirmMixin } from "../../../UI/shared-components/primaria-interaction/confirm-mixin";
|
|
6
|
+
import { PluginBusyTask } from "../plugin-busy-manager";
|
|
7
|
+
|
|
8
|
+
@customElement("plugin-busy-list")
|
|
9
|
+
export class PluginBusyList extends confirmMixin(LitElement) {
|
|
10
|
+
static styles = css`
|
|
11
|
+
${unsafeCSS(styles)}
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
render() {
|
|
15
|
+
return html`${template(this)}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
model: { busyTasks: PluginBusyTask[] };
|
|
19
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
.container{
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: 4px;
|
|
5
|
+
align-items: center;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.title{
|
|
9
|
+
font-size: 15px;
|
|
10
|
+
line-height: 24px;
|
|
11
|
+
font-weight: 600;
|
|
12
|
+
color: var(--color-red-600);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
.list{
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
gap: 8px;
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { html } from "lit";
|
|
2
|
+
import { PluginBusyList } from "./component";
|
|
3
|
+
import { PluginBusyTask } from "../plugin-busy-manager";
|
|
4
|
+
import { translate } from "../../../locales";
|
|
5
|
+
|
|
6
|
+
export const template = (props: PluginBusyList) => html`
|
|
7
|
+
<div class="container">
|
|
8
|
+
<div class="title">${translate("busyManager.title")}</div>
|
|
9
|
+
<div class="list">
|
|
10
|
+
${props.model?.busyTasks?.map((item: PluginBusyTask) => html`<div class="plugin-busy-item">${item.taskDescription}</div>`)}
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
`;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import { PluginBusyManagerImpl } from "./plugin-busy-manager";
|
|
3
|
+
|
|
4
|
+
describe("PluginBusyManagerImpl", () => {
|
|
5
|
+
let manager: PluginBusyManagerImpl;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
manager = new PluginBusyManagerImpl();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("should start with no busy plugins", () => {
|
|
12
|
+
expect(manager.getBusyPluginTasks()).toEqual([]);
|
|
13
|
+
expect(manager.isAnyPluginBusy()).toBe(false);
|
|
14
|
+
});
|
|
15
|
+
|
|
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);
|
|
21
|
+
});
|
|
22
|
+
|
|
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);
|
|
31
|
+
});
|
|
32
|
+
|
|
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]);
|
|
38
|
+
});
|
|
39
|
+
|
|
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);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface PluginBusyTask {
|
|
2
|
+
taskId: string;
|
|
3
|
+
taskDescription: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export abstract class PluginBusyManager {
|
|
7
|
+
abstract addBusyPluginTask(busyTask: PluginBusyTask): void;
|
|
8
|
+
abstract removeBusyPluginTask(taskId: string): any;
|
|
9
|
+
abstract clearAllBusyPlugins(): void;
|
|
10
|
+
abstract isAnyPluginBusy(): boolean;
|
|
11
|
+
abstract getBusyPluginTasks(): PluginBusyTask[];
|
|
12
|
+
}
|
|
13
|
+
export class PluginBusyManagerImpl implements PluginBusyManager {
|
|
14
|
+
private busyPluginTasks: PluginBusyTask[] = [];
|
|
15
|
+
|
|
16
|
+
public addBusyPluginTask(busyTask: PluginBusyTask): void {
|
|
17
|
+
this.busyPluginTasks.push(busyTask);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public removeBusyPluginTask(taskId: string): any {
|
|
21
|
+
const index = this.busyPluginTasks.findIndex((item) => item.taskId === taskId);
|
|
22
|
+
if (index > -1) {
|
|
23
|
+
this.busyPluginTasks.splice(index, 1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public isAnyPluginBusy(): boolean {
|
|
28
|
+
return this.busyPluginTasks.length > 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public clearAllBusyPlugins(): void {
|
|
32
|
+
this.busyPluginTasks = [];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public getBusyPluginTasks(): PluginBusyTask[] {
|
|
36
|
+
return this.busyPluginTasks;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -13,17 +13,5 @@ describe("Token Manager test", () => {
|
|
|
13
13
|
it("should return initial token", () => {
|
|
14
14
|
const tokenManager = createTokenManager();
|
|
15
15
|
expect(tokenManager.getToken()).toBe(access_token);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("should refresh token", async () => {
|
|
19
|
-
const tokenManager = createTokenManager();
|
|
20
|
-
const refreshPromise = tokenManager.refreshToken();
|
|
21
|
-
const oldToken = tokenManager.getToken();
|
|
22
|
-
const newToken = await refreshPromise;
|
|
23
|
-
const newToken2 = tokenManager.getToken();
|
|
24
|
-
expect(newToken).toBe("new-auth-token");
|
|
25
|
-
expect(newToken).toBe(newToken2);
|
|
26
|
-
expect(newToken).not.toBe(oldToken);
|
|
27
|
-
expect(oldToken).toBe(access_token);
|
|
28
16
|
});
|
|
29
17
|
});
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
|
|
1
3
|
export interface TokenManager {
|
|
2
4
|
getToken: () => string;
|
|
3
5
|
refreshToken: () => Promise<string>;
|
|
@@ -5,7 +7,7 @@ export interface TokenManager {
|
|
|
5
7
|
|
|
6
8
|
let token: string;
|
|
7
9
|
let refreshToken: string;
|
|
8
|
-
export class
|
|
10
|
+
export class TokenManagerImpl implements TokenManager {
|
|
9
11
|
getUrlParams = (): URLSearchParams => {
|
|
10
12
|
return new URLSearchParams(window.location.search);
|
|
11
13
|
};
|
|
@@ -14,7 +16,6 @@ export class TokenManagerSimulator implements TokenManager {
|
|
|
14
16
|
const searchString = this.getUrlParams();
|
|
15
17
|
token = searchString.get("access_token") || "";
|
|
16
18
|
refreshToken = searchString.get("refresh_token") || "";
|
|
17
|
-
|
|
18
19
|
return token;
|
|
19
20
|
};
|
|
20
21
|
|
|
@@ -23,16 +24,20 @@ export class TokenManagerSimulator implements TokenManager {
|
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
refreshToken = async () => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
const response = await axios.post('/api/token/refresh', {token: refreshToken});
|
|
28
|
+
const {access_token, refresh_token} = response.data;
|
|
29
|
+
if(!access_token){
|
|
30
|
+
throw new Error("Invalid refresh token response");
|
|
31
|
+
}
|
|
32
|
+
token = access_token;
|
|
33
|
+
refreshToken = refresh_token;
|
|
34
|
+
return token;
|
|
30
35
|
};
|
|
31
36
|
}
|
|
32
37
|
let tokenManager;
|
|
33
38
|
export const createTokenManager = () => {
|
|
34
39
|
if(tokenManager) return tokenManager;
|
|
35
|
-
tokenManager = new
|
|
40
|
+
tokenManager = new TokenManagerImpl();
|
|
36
41
|
tokenManager.initToken();
|
|
37
42
|
return tokenManager;
|
|
38
43
|
}
|
package/src/disposer.ts
CHANGED
package/src/events.ts
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { PrimariaApi, shellApi } from "../api/api";
|
|
2
2
|
import { container } from "../infrastructure/ioc/container";
|
|
3
3
|
import { TYPES } from "../infrastructure/ioc/types";
|
|
4
|
+
import { bootstrapExitShell, teardownExitShell } from "./exit/bootstrapper";
|
|
4
5
|
import { bootstrapGetUserInfo, teardownGetUserInfo } from "./get-user-info/bootstrapper";
|
|
5
6
|
import { GetUserInfo } from "./get-user-info/request";
|
|
6
7
|
|
|
7
8
|
export const bootstrapFeatures = (api: PrimariaApi) => {
|
|
8
9
|
container.bind(TYPES.primaryApi).toConstantValue(api);
|
|
9
10
|
bootstrapGetUserInfo();
|
|
11
|
+
bootstrapExitShell();
|
|
10
12
|
shellApi.broker.send(new GetUserInfo());
|
|
11
13
|
};
|
|
12
14
|
|
|
13
15
|
export const teardownFeatures = () => {
|
|
14
16
|
teardownGetUserInfo();
|
|
17
|
+
teardownExitShell();
|
|
15
18
|
container.unbindAll();
|
|
16
19
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { shellApi } from "../../api/api";
|
|
2
|
+
import { BrokerDisposableHandler } from "../../api/broker/primaria-broker";
|
|
3
|
+
import { container } from "../../infrastructure/ioc/container";
|
|
4
|
+
import { registerRequest } from "../utils";
|
|
5
|
+
import { ExitShellHandler } from "./handler";
|
|
6
|
+
import { ExitShell } from "./request";
|
|
7
|
+
|
|
8
|
+
let request: BrokerDisposableHandler;
|
|
9
|
+
|
|
10
|
+
export const bootstrapExitShell = () => {
|
|
11
|
+
teardownExitShell();
|
|
12
|
+
request = registerRequest(shellApi, container)(ExitShell, ExitShellHandler);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const teardownExitShell = () => {
|
|
16
|
+
request?.dispose();
|
|
17
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { CustomConfirmOptions, PrimariaApi } from "@uxland/primary-shell";
|
|
2
|
+
import { TYPES } from "../../infrastructure/ioc/types";
|
|
3
|
+
import { inject } from "inversify";
|
|
4
|
+
import { ExitShell } from "./request";
|
|
5
|
+
import { disposeShell, raiseCloseEvent } from "../../disposer";
|
|
6
|
+
import { disposePlugins } from "../../handle-plugins";
|
|
7
|
+
import { PluginBusyTask } from "../../api/plugin-busy-manager/plugin-busy-manager";
|
|
8
|
+
import { PluginBusyList } from "../../api/plugin-busy-manager/plugin-busy-list/component";
|
|
9
|
+
import { translate } from "../../locales";
|
|
10
|
+
|
|
11
|
+
export class ExitShellHandler {
|
|
12
|
+
constructor(@inject(TYPES.primaryApi) private api: PrimariaApi) {}
|
|
13
|
+
async handle(message: ExitShell): Promise<void> {
|
|
14
|
+
try {
|
|
15
|
+
const busyTasks = this.api.pluginBusyManager.getBusyPluginTasks();
|
|
16
|
+
if (busyTasks.length > 0) {
|
|
17
|
+
const { confirmed } = await this.askForClose(busyTasks);
|
|
18
|
+
if (!confirmed) return;
|
|
19
|
+
}
|
|
20
|
+
disposeShell();
|
|
21
|
+
|
|
22
|
+
// Per si un plugin tarda molt en fer dispose, màxim deixarem 5 segons, per no interrompre el tancar infinitament
|
|
23
|
+
await Promise.race([
|
|
24
|
+
disposePlugins(), // S'intenta executar un dispose normal
|
|
25
|
+
this.timeout(5000), // Si passen 5s, es segueix amb l'execució
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
raiseCloseEvent();
|
|
29
|
+
} catch (error) {
|
|
30
|
+
this.api.interactionManager.notify({
|
|
31
|
+
type: "error",
|
|
32
|
+
message: error.message,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private askForClose(busyTasks: PluginBusyTask[]) {
|
|
38
|
+
return this.api.interactionManager.customConfirm({
|
|
39
|
+
title: translate("actions.askExit"),
|
|
40
|
+
componentConstructor: PluginBusyList,
|
|
41
|
+
type: "danger",
|
|
42
|
+
model: { busyTasks },
|
|
43
|
+
acceptLabel: "Si",
|
|
44
|
+
cancelLabel: "No",
|
|
45
|
+
} as CustomConfirmOptions<{ busyTasks: PluginBusyTask[] }>);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private timeout(ms: number) {
|
|
49
|
+
return new Promise<void>((resolve) => setTimeout(resolve, ms));
|
|
50
|
+
}
|
|
51
|
+
}
|
package/src/handle-plugins.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { bootstrapPlugins as pluginBootstrapper } from "@uxland/harmonix";
|
|
2
|
-
import type { PluginDefinition, Plugin as PluginType } from "@uxland/harmonix";
|
|
2
|
+
import type { PluginDefinition, Plugin as PluginType, BootstrappedPlugin } from "@uxland/harmonix";
|
|
3
3
|
export type { PluginDefinition, PluginInfo } from "@uxland/harmonix";
|
|
4
4
|
import {
|
|
5
5
|
initialize as activityHistoryInitialize,
|
|
6
6
|
dispose as activityHistoryDispose,
|
|
7
7
|
} from "./internal-plugins/activity-history/main";
|
|
8
|
-
import { PrimariaApi, primariaApiFactory
|
|
8
|
+
import { PrimariaApi, primariaApiFactory } from "./api/api";
|
|
9
|
+
|
|
10
|
+
let bootstrappedPlugins = [] as BootstrappedPlugin[];
|
|
9
11
|
|
|
10
12
|
const internalPlugins: PluginDefinition[] = [
|
|
11
13
|
{
|
|
@@ -20,12 +22,11 @@ const internalPlugins: PluginDefinition[] = [
|
|
|
20
22
|
|
|
21
23
|
export const bootstrapPlugins = async (plugins: PluginDefinition[]) => {
|
|
22
24
|
const finalPlugins = internalPlugins.concat(plugins || []);
|
|
23
|
-
|
|
24
|
-
return bootstrappedPlugins as Plugin[];
|
|
25
|
+
bootstrappedPlugins = await pluginBootstrapper(finalPlugins, primariaApiFactory);
|
|
25
26
|
};
|
|
26
27
|
|
|
27
|
-
export const disposePlugins = async (
|
|
28
|
-
return Promise.all(
|
|
28
|
+
export const disposePlugins = async () => {
|
|
29
|
+
return Promise.all(bootstrappedPlugins.map((plugin: BootstrappedPlugin) => plugin.dispose()));
|
|
29
30
|
};
|
|
30
31
|
|
|
31
32
|
export type Plugin = PluginType<PrimariaApi>;
|
package/src/handle-views.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { shellApi } from "./api/api";
|
|
|
2
2
|
import { shellRegions } from "./api/region-manager/regions";
|
|
3
3
|
import { clinicalMonitoringId, pocTestEventsId } from "./constants";
|
|
4
4
|
import { shellEvents } from "./events";
|
|
5
|
+
import { ExitShell } from "./features/exit/request";
|
|
5
6
|
import { ClinicalMonitoring } from "./UI/components/clinical-monitoring/clinical-monitoring";
|
|
6
7
|
import { PocEventsEcap } from "./UI/components/poc-events-ecap/poc-events-ecap";
|
|
7
8
|
import { PrimariaNavItem } from "./UI/shared-components/primaria-nav-item/primaria-nav-item";
|
|
@@ -257,7 +258,9 @@ const registerUpperNavMenuViews = () => {
|
|
|
257
258
|
const menuItem = new PrimariaNavItem({
|
|
258
259
|
icon: "add_box",
|
|
259
260
|
label: "Pàgina inici",
|
|
260
|
-
callbackFn: () => {
|
|
261
|
+
callbackFn: () => {
|
|
262
|
+
shellApi.broker.send(new ExitShell());
|
|
263
|
+
},
|
|
261
264
|
});
|
|
262
265
|
return Promise.resolve(menuItem);
|
|
263
266
|
},
|
|
@@ -4,12 +4,14 @@ import { addBusyHistoryItem, removeBusyHistoryItem } from "../../list/handle-bus
|
|
|
4
4
|
import { addErrorHistoryItem } from "../../list/handle-error/actions";
|
|
5
5
|
import { addHistoryItemsCommand } from "../add-history-items/request";
|
|
6
6
|
import { InjectAsyncHistoryItemsPayload } from "./request";
|
|
7
|
+
import { validateAddAsyncCommand } from "./validate-add-async-items-command";
|
|
7
8
|
|
|
8
9
|
export class InjectAsyncHistoryItemsHandler extends BaseHandler {
|
|
9
10
|
async handle(payload: InjectAsyncHistoryItemsPayload) {
|
|
10
11
|
this.store.dispatch(addBusyHistoryItem(payload.entityId));
|
|
11
12
|
try {
|
|
12
13
|
const items = await payload.asyncDataProvider();
|
|
14
|
+
validateAddAsyncCommand(payload, items);
|
|
13
15
|
this.api.broker.send(addHistoryItemsCommand, {
|
|
14
16
|
entityId: payload.entityId,
|
|
15
17
|
items,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { IActivityHistoryItem } from "../../domain/model";
|
|
2
|
+
import { InjectAsyncHistoryItemsPayload } from "./request";
|
|
3
|
+
|
|
4
|
+
export const validateAddAsyncCommand = (payload: InjectAsyncHistoryItemsPayload, items: IActivityHistoryItem[]) => {
|
|
5
|
+
if(payload.entityId !== payload.filters.id)
|
|
6
|
+
throw new Error("entityId and filters.id must be the same");
|
|
7
|
+
if(!payload.filters.title)
|
|
8
|
+
throw new Error("filters.title is required");
|
|
9
|
+
if(!payload.asyncDataProvider)
|
|
10
|
+
throw new Error("asyncDataProvider is required");
|
|
11
|
+
if(!payload.componentFactory)
|
|
12
|
+
throw new Error("componentFactory is required");
|
|
13
|
+
if(!payload.errorMessage)
|
|
14
|
+
throw new Error("errorMessage is required for failure asyncDataProviders");
|
|
15
|
+
};
|
package/src/internal-plugins/activity-history/activity-history-item/add/add-history-items/reducer.ts
CHANGED
|
@@ -12,11 +12,14 @@ export function addActivityHistoryItemsReducer(
|
|
|
12
12
|
}>,
|
|
13
13
|
) {
|
|
14
14
|
const { id, items, componentFactory, searchPredicate } = action.payload;
|
|
15
|
-
|
|
16
|
-
state
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
return {
|
|
16
|
+
...state,
|
|
17
|
+
collections: addItemsToCollection(
|
|
18
|
+
state.collections,
|
|
19
|
+
id,
|
|
20
|
+
items,
|
|
21
|
+
componentFactory,
|
|
22
|
+
searchPredicate,
|
|
23
|
+
),
|
|
24
|
+
};
|
|
22
25
|
}
|
|
@@ -29,6 +29,10 @@ export interface IActivityHistoryItem {
|
|
|
29
29
|
type?: string;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
export interface IActivityHistoryItemWithComponent extends IActivityHistoryItem {
|
|
33
|
+
component: HTMLElement;
|
|
34
|
+
}
|
|
35
|
+
|
|
32
36
|
export interface IActivityHistoryItemCollection {
|
|
33
37
|
id: string;
|
|
34
38
|
items: IActivityHistoryItem[];
|
|
@@ -6,12 +6,11 @@ import { PrimariaApi } from "../../../../../../api/api";
|
|
|
6
6
|
import { TYPES } from "../../../../infrastructure/ioc/types";
|
|
7
7
|
import { template } from "./template";
|
|
8
8
|
import styles from "./styles.css?inline";
|
|
9
|
-
import {
|
|
10
|
-
activeGroupsWithEnabledFilters,
|
|
11
|
-
activityHistoryCustomFilterGroupsSelector,
|
|
12
|
-
} from "../../custom-filters/selectors";
|
|
9
|
+
import { activeGroupsWithEnabledFilters } from "../../custom-filters/selectors";
|
|
13
10
|
import { SetCustomFilterValue } from "../../custom-filters/set-custom-filter-value/request";
|
|
14
|
-
import { IActivityHistoryCustomFilterGroup } from "../../model";
|
|
11
|
+
import { IActivityHistoryCustomFilterGroup, IActivityHistoryFilterGroup } from "../../model";
|
|
12
|
+
import { activityHistoryEnabledCommonFiltersSelector } from "../../common-filters/selectors";
|
|
13
|
+
import { SetCommonFilter } from "../../common-filters/set-common-filter/request";
|
|
15
14
|
|
|
16
15
|
//@ts-ignore
|
|
17
16
|
@customElement("active-filters-badges")
|
|
@@ -27,6 +26,11 @@ export class ActiveFiltersBadges extends LitElement {
|
|
|
27
26
|
@lazyInject(TYPES.primaryApi)
|
|
28
27
|
api: PrimariaApi;
|
|
29
28
|
|
|
29
|
+
activityHistoryCommonFiltersSelector;
|
|
30
|
+
|
|
31
|
+
@connectedProperty(activityHistoryEnabledCommonFiltersSelector)
|
|
32
|
+
enabledCommonFilters: IActivityHistoryFilterGroup[];
|
|
33
|
+
|
|
30
34
|
@connectedProperty(activeGroupsWithEnabledFilters)
|
|
31
35
|
enabledFilters: IActivityHistoryCustomFilterGroup[];
|
|
32
36
|
|
|
@@ -40,4 +44,8 @@ export class ActiveFiltersBadges extends LitElement {
|
|
|
40
44
|
}),
|
|
41
45
|
);
|
|
42
46
|
}
|
|
47
|
+
|
|
48
|
+
_onDeleteCommonFilterValue(filterId: string) {
|
|
49
|
+
this.api.broker.send(new SetCommonFilter({ id: filterId, enabled: false }));
|
|
50
|
+
}
|
|
43
51
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { html, nothing } from "lit";
|
|
2
2
|
import { ActiveFiltersBadges } from "./active-filters-badges";
|
|
3
3
|
import { translate } from "../../../../localization";
|
|
4
|
+
import { formatShowFilterTitle, normalizeDeletedFilterBooleanValue } from "../../utils";
|
|
4
5
|
|
|
5
6
|
const MAX_VISIBLE_CHIPS = 5;
|
|
6
7
|
|
|
@@ -15,23 +16,46 @@ const renderChip = (id, filter, value, props) => html`
|
|
|
15
16
|
></dss-chip>
|
|
16
17
|
`;
|
|
17
18
|
|
|
19
|
+
const renderCommonChip = (filter, props) => html`
|
|
20
|
+
<dss-chip
|
|
21
|
+
label=${formatShowFilterTitle(filter.title)}
|
|
22
|
+
size="sm"
|
|
23
|
+
hasdelete
|
|
24
|
+
selected
|
|
25
|
+
@click=${() => props._onDeleteCommonFilterValue(filter.id)}
|
|
26
|
+
@onDelete=${() => props._onDeleteCommonFilterValue(filter.id)}
|
|
27
|
+
></dss-chip>
|
|
28
|
+
`;
|
|
29
|
+
|
|
18
30
|
const renderFilterTitle = (title) => html`<div class="filter-title">${`${title}:`}</div>`;
|
|
19
31
|
|
|
20
32
|
export const template = (props: ActiveFiltersBadges) => {
|
|
21
|
-
if (props.enabledFilters.length === 0) return nothing;
|
|
33
|
+
if (props.enabledFilters.length === 0 && props.enabledCommonFilters.length === 0) return nothing;
|
|
22
34
|
|
|
23
35
|
let visibleCount = 0;
|
|
24
36
|
const visibleChips: any[] = [];
|
|
25
37
|
const hiddenElements: any[] = [];
|
|
26
38
|
const hiddenChips: any[] = [];
|
|
27
39
|
|
|
40
|
+
for (const filter of props.enabledCommonFilters) {
|
|
41
|
+
const chip = renderCommonChip(filter, props);
|
|
42
|
+
|
|
43
|
+
if (visibleCount < MAX_VISIBLE_CHIPS) {
|
|
44
|
+
visibleChips.push(chip);
|
|
45
|
+
visibleCount++;
|
|
46
|
+
} else {
|
|
47
|
+
hiddenElements.push(chip);
|
|
48
|
+
hiddenChips.push(chip);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
28
52
|
for (const { title, id, filters } of props.enabledFilters) {
|
|
29
53
|
let titleAddedToVisible = false;
|
|
30
54
|
let titleAddedToHidden = false;
|
|
31
55
|
|
|
32
56
|
for (const filter of filters) {
|
|
33
57
|
for (const value of [...new Set(filter.enabledValues)]) {
|
|
34
|
-
const chip = renderChip(id, filter, value, props);
|
|
58
|
+
const chip = renderChip(id, filter, normalizeDeletedFilterBooleanValue(value), props);
|
|
35
59
|
|
|
36
60
|
if (visibleCount < MAX_VISIBLE_CHIPS) {
|
|
37
61
|
if (!titleAddedToVisible) {
|
|
@@ -4,11 +4,8 @@ import styles from "./styles.css?inline";
|
|
|
4
4
|
import { customElement } from "lit/decorators.js";
|
|
5
5
|
import { connectedProperty } from "../../../../infrastructure/state/connected-property";
|
|
6
6
|
import { activityHistorySearchStringSelector } from "../../../search/selectors";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
areCustomFiltersActive,
|
|
10
|
-
} from "../../custom-filters/selectors";
|
|
11
|
-
import { IActivityHistoryCustomFilterGroup } from "../../model";
|
|
7
|
+
import { areCustomFiltersActive } from "../../custom-filters/selectors";
|
|
8
|
+
import { areCommonFiltersActive } from "../../common-filters/selectors";
|
|
12
9
|
|
|
13
10
|
//@ts-ignore
|
|
14
11
|
@customElement("active-filters-header")
|
|
@@ -24,9 +21,9 @@ export class ActiveFiltersHeader extends LitElement {
|
|
|
24
21
|
@connectedProperty(activityHistorySearchStringSelector)
|
|
25
22
|
searchString: string;
|
|
26
23
|
|
|
27
|
-
@connectedProperty(activityHistoryCustomFilterGroupsSelector)
|
|
28
|
-
filters: IActivityHistoryCustomFilterGroup[];
|
|
29
|
-
|
|
30
24
|
@connectedProperty(areCustomFiltersActive)
|
|
31
25
|
areCustomFiltersActive: boolean;
|
|
26
|
+
|
|
27
|
+
@connectedProperty(areCommonFiltersActive)
|
|
28
|
+
areCommonFiltersActive: boolean;
|
|
32
29
|
}
|
|
@@ -3,7 +3,12 @@ import { ActiveFiltersHeader } from "./active-filters-header";
|
|
|
3
3
|
import { when } from "lit/directives/when.js";
|
|
4
4
|
|
|
5
5
|
export const template = (props: ActiveFiltersHeader) => {
|
|
6
|
-
if (
|
|
6
|
+
if (
|
|
7
|
+
!props.areCustomFiltersActive &&
|
|
8
|
+
!props.areCommonFiltersActive &&
|
|
9
|
+
props.searchString.length < 1
|
|
10
|
+
)
|
|
11
|
+
return nothing;
|
|
7
12
|
|
|
8
13
|
return html`
|
|
9
14
|
<div class="active-filters-header">
|
|
@@ -12,3 +12,13 @@ export const isEnabledCommonFilterSelector = (filterId: string) =>
|
|
|
12
12
|
activityHistoryCommonFiltersSelector,
|
|
13
13
|
(filterSpecs) => filterSpecs?.find((s) => s.id === filterId)?.enabled,
|
|
14
14
|
);
|
|
15
|
+
|
|
16
|
+
export const activityHistoryEnabledCommonFiltersSelector = createSelector(
|
|
17
|
+
activityHistoryCommonFiltersSelector,
|
|
18
|
+
(filters) => filters.filter((f) => f.enabled),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export const areCommonFiltersActive = createSelector(
|
|
22
|
+
activityHistoryEnabledCommonFiltersSelector,
|
|
23
|
+
(filters) => !!filters.length,
|
|
24
|
+
);
|