@claudebernard/therapeutic-alternative-element 2.1.2 → 2.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.
|
@@ -5,9 +5,10 @@ import { TemplateResult } from 'lit-html';
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Composant pour afficher les alternatives thérapeutiques d'un médicament donné.
|
|
8
|
-
* Permet à l'utilisateur d'affiner sa recherche via
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Permet à l'utilisateur d'affiner sa recherche via un pool de base (scope), des critères
|
|
9
|
+
* complémentaires (include), des critères d'exclusion et des filtres rapides, et affiche les
|
|
10
|
+
* résultats correspondants — toute la recherche/agrégation reste côté serveur, ce composant ne
|
|
11
|
+
* fait qu'envoyer les critères et afficher ce qui revient.
|
|
11
12
|
* @element bcb-therapeutic-alternative
|
|
12
13
|
*/
|
|
13
14
|
export declare class TherapeuticAlternative extends LitElement {
|
|
@@ -21,17 +22,22 @@ export declare class TherapeuticAlternative extends LitElement {
|
|
|
21
22
|
private totalElements;
|
|
22
23
|
private lastRequestHadNoSelectedCriteria;
|
|
23
24
|
private currentPage;
|
|
25
|
+
private scope;
|
|
24
26
|
private include;
|
|
25
27
|
private exclude;
|
|
26
28
|
private quickFilters;
|
|
29
|
+
private quickFilterFacets;
|
|
30
|
+
private scopeFacets;
|
|
27
31
|
private includeFacets;
|
|
28
32
|
private excludeFacets;
|
|
29
33
|
private truncatedGroups;
|
|
30
34
|
private expandFacetGroupKey;
|
|
35
|
+
private scopeLabels;
|
|
31
36
|
private includeLabels;
|
|
32
37
|
private excludeLabels;
|
|
33
|
-
private
|
|
38
|
+
private openScopeGroup;
|
|
34
39
|
private openExclusionGroup;
|
|
40
|
+
private openIncludeGroup;
|
|
35
41
|
private checklistSearchTerm;
|
|
36
42
|
private sectionCollapsed;
|
|
37
43
|
private apiSearchError;
|
|
@@ -40,28 +46,36 @@ export declare class TherapeuticAlternative extends LitElement {
|
|
|
40
46
|
private apiReplaceError;
|
|
41
47
|
private apiReplaceLoading;
|
|
42
48
|
private missingPrescriptionError;
|
|
43
|
-
private
|
|
49
|
+
private hasAppliedInitialScope;
|
|
44
50
|
private refetchTimer;
|
|
45
51
|
private requestSeq;
|
|
46
52
|
private get currentMedication();
|
|
47
53
|
private get currentMedicationRequest();
|
|
48
54
|
private get sourceProductId();
|
|
49
55
|
updated(changedProperties: Map<string | number | symbol, unknown>): void;
|
|
56
|
+
private resetAllCriteria;
|
|
50
57
|
disconnectedCallback(): void;
|
|
51
58
|
private clearScheduledRefetch;
|
|
52
59
|
private scheduleRefetch;
|
|
53
60
|
private hasNoSelectedCriteria;
|
|
54
61
|
private fetchAlternatives;
|
|
55
62
|
private applyResponse;
|
|
63
|
+
private static buildScopeFacets;
|
|
56
64
|
private static buildIncludeFacets;
|
|
57
65
|
private static buildExcludeFacets;
|
|
58
|
-
private static
|
|
59
|
-
private
|
|
66
|
+
private static codesOf;
|
|
67
|
+
private static deriveInitialScope;
|
|
68
|
+
private includePossessedIds;
|
|
69
|
+
private excludePossessedIds;
|
|
70
|
+
private toggleScope;
|
|
60
71
|
private toggleExclude;
|
|
72
|
+
private toggleInclude;
|
|
61
73
|
private toggleQuickFilter;
|
|
62
74
|
private exclusionTruncatedGroupKey;
|
|
63
|
-
private
|
|
75
|
+
private includeTruncatedGroupKey;
|
|
76
|
+
private toggleScopePanel;
|
|
64
77
|
private toggleExclusionPanel;
|
|
78
|
+
private toggleIncludePanel;
|
|
65
79
|
private requestMoreFacetValues;
|
|
66
80
|
private toggleSection;
|
|
67
81
|
private cancelAlternative;
|
|
@@ -71,12 +85,15 @@ export declare class TherapeuticAlternative extends LitElement {
|
|
|
71
85
|
private renderChecklistSearch;
|
|
72
86
|
private renderSelectedChips;
|
|
73
87
|
private renderPill;
|
|
74
|
-
private
|
|
88
|
+
private renderScopeSection;
|
|
75
89
|
private renderExclusionSection;
|
|
90
|
+
private renderIncludeSection;
|
|
76
91
|
private renderQuickFiltersSection;
|
|
92
|
+
private renderQuickFilterCount;
|
|
77
93
|
private renderSectionHeader;
|
|
78
94
|
private matchReasonLabels;
|
|
79
95
|
private renderResults;
|
|
96
|
+
private renderTitle;
|
|
80
97
|
render(): TemplateResult<1>;
|
|
81
98
|
static styles: CSSResult;
|
|
82
99
|
}
|
|
@@ -2297,33 +2297,42 @@ ProductCard = __decorateClass$1([
|
|
|
2297
2297
|
], ProductCard);
|
|
2298
2298
|
const REFETCH_DEBOUNCE_MS = 300;
|
|
2299
2299
|
const CHECKLIST_SEARCH_THRESHOLD = 30;
|
|
2300
|
-
const
|
|
2300
|
+
const SCOPE_GROUPS = [
|
|
2301
2301
|
{ key: "indications", idsField: "indicationIds", label: "Indications" },
|
|
2302
|
-
{ key: "
|
|
2303
|
-
{ key: "atcClasses", idsField: "atcClassIds", label: "Classe thérapeutique" }
|
|
2302
|
+
{ key: "atcClasses", idsField: "atcClassIds", label: "Classe ATC" }
|
|
2304
2303
|
];
|
|
2305
2304
|
const EXCLUSION_GROUPS = [
|
|
2306
2305
|
{ key: "activeSubstances", idsField: "activeSubstanceIds", label: "Principes actifs" },
|
|
2307
2306
|
{ key: "notableExcipients", idsField: "notableExcipientIds", label: "Excipients à effet notoire" },
|
|
2308
2307
|
{ key: "excipients", idsField: "excipientIds", label: "Excipients" },
|
|
2309
2308
|
{ key: "contraindications", idsField: "contraindicationIds", label: "Utilisation contre-indiquée" },
|
|
2310
|
-
{ key: "cautions", idsField: "cautionIds", label: "
|
|
2311
|
-
{ key: "precautions", idsField: "precautionIds", label: "Précaution d'emploi" }
|
|
2309
|
+
{ key: "cautions", idsField: "cautionIds", label: "Utilisation déconseillée" },
|
|
2310
|
+
{ key: "precautions", idsField: "precautionIds", label: "Précaution d'emploi" },
|
|
2311
|
+
{ key: "sideEffects", idsField: "sideEffectIds", label: "Effets indésirables" }
|
|
2312
2312
|
];
|
|
2313
2313
|
const QUICK_FILTERS = [
|
|
2314
2314
|
{ key: "sameGalenicForm", label: "Même forme galénique" },
|
|
2315
2315
|
{ key: "sameDosage", label: "Même dosage" },
|
|
2316
2316
|
{ key: "sameRouteOfAdministration", label: "Même voie d'administration" }
|
|
2317
2317
|
];
|
|
2318
|
+
const INCLUDE_GROUPS = [
|
|
2319
|
+
{ key: "activeSubstances", idsField: "activeSubstanceIds", label: "Principes actifs" },
|
|
2320
|
+
{ key: "galenicForms", idsField: "galenicFormIds", label: "Forme galénique", quickFilterKey: "sameGalenicForm" },
|
|
2321
|
+
{ key: "dosages", idsField: "dosageIds", label: "Dosage", quickFilterKey: "sameDosage" },
|
|
2322
|
+
{ key: "routesOfAdministration", idsField: "routeOfAdministrationIds", label: "Voie d'administration", quickFilterKey: "sameRouteOfAdministration" }
|
|
2323
|
+
];
|
|
2318
2324
|
const INCLUDE_COUNT_TOOLTIP = "Nombre de résultats restants avec cette valeur";
|
|
2319
2325
|
const EXCLUDE_COUNT_TOOLTIP = "Nombre de résultats retirés avec cette valeur";
|
|
2320
2326
|
const MATCH_REASON_VERB_BY_DIMENSION = {
|
|
2321
2327
|
indication: "indiqué pour",
|
|
2322
2328
|
activeSubstance: "contient"
|
|
2323
2329
|
};
|
|
2324
|
-
const therapeuticAlternativeCSS = '.claudebernard-therapeutic-alternative {\n font-family: var(--custom-font-family, "Ubuntu"), sans-serif;\n padding: 1rem;\n}\n.claudebernard-therapeutic-alternative-without-title {\n padding-top: 0;\n max-width: 70vw;\n width: 60vw;\n max-height: 70vh;\n}\n.claudebernard-therapeutic-alternative-title {\n font-size: 0.75rem;\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--bcbd-gray-500-color, #64748B);\n margin-bottom: 0.375rem;\n display: block;\n}\n.claudebernard-therapeutic-alternative__source {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 1rem;\n flex-wrap: wrap;\n}\n.claudebernard-therapeutic-alternative__disclaimer {\n margin-bottom: 1rem;\n}\n.claudebernard-therapeutic-alternative__loading {\n width: 100%;\n height: 30%;\n padding-top: 2rem;\n display: flex;\n justify-content: center;\n}\n.claudebernard-therapeutic-alternative__section {\n border: 1px solid var(--bcbd-border-gray-color, #CBD5E1);\n border-radius: 0.625rem;\n padding: 0.75rem 0.875rem;\n margin-bottom: 1rem;\n}\n.claudebernard-therapeutic-alternative__section-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n cursor: pointer;\n background: none;\n border: none;\n padding: 0;\n margin-bottom: 0.625rem;\n font-family: inherit;\n line-height: 1;\n}\n.claudebernard-therapeutic-alternative__section-header span:first-child {\n font-size: 0.75rem;\n font-weight: 700;\n color: var(--bcbd-gray-500-color, #64748B);\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n.claudebernard-therapeutic-alternative__chevron {\n font-size: 0.75rem;\n color: var(--bcbd-gray-400-color, #94A3B8);\n transition: transform 0.15s ease-out;\n display: inline-flex;\n}\n.claudebernard-therapeutic-alternative__chevron--expanded {\n transform: rotate(180deg);\n}\n.claudebernard-therapeutic-alternative__pills {\n display: flex;\n gap: 0.5rem;\n flex-wrap: wrap;\n}\n.claudebernard-therapeutic-alternative__pill {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n border: 1px solid var(--bcbd-border-gray-color, #CBD5E1);\n background: #fff;\n color: var(--bcbd-gray-600-color, #475569);\n border-radius: 6.25rem;\n padding: 0.375rem 0.75rem;\n font-size: 0.8125rem;\n font-family: inherit;\n font-weight: 400;\n white-space: nowrap;\n cursor: pointer;\n line-height: 1;\n}\n.claudebernard-therapeutic-alternative__pill--active {\n border-color: var(--bcbd-primary-color, #0C75AD);\n background: color-mix(in srgb, var(--bcbd-primary-color, #0C75AD) 10%, transparent);\n color: var(--bcbd-primary-color, #0C75AD);\n font-weight: 700;\n}\n.claudebernard-therapeutic-alternative__panel {\n border-top: 1px solid var(--bcbd-border-color-200, #E2E8F0);\n margin-top: 0.75rem;\n padding-top: 0.75rem;\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n.claudebernard-therapeutic-alternative__panel-rows {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n max-height: 16.25rem;\n overflow-y: auto;\n padding: 0.25rem 0.125rem;\n}\n.claudebernard-therapeutic-alternative__panel-empty {\n display: block;\n margin-top: 0.75rem;\n font-size: 0.875rem;\n color: var(--bcbd-gray-500-color, #64748B);\n}\n.claudebernard-therapeutic-alternative__panel-expand {\n align-self: flex-start;\n background: none;\n border: none;\n padding: 0.25rem 0 0;\n font-family: inherit;\n font-size: 0.8125rem;\n font-weight: 700;\n color: var(--bcbd-primary-color, #0C75AD);\n cursor: pointer;\n}\n.claudebernard-therapeutic-alternative__panel-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n border-radius: 0.375rem;\n --bcbd-checkbox-font-size: 0.875rem;\n --bcbd-checkbox-size: 0.9375rem;\n}\n.claudebernard-therapeutic-alternative__panel-row--pending {\n background: var(--bcbd-gray-100-color, #F1F5F9);\n padding: 0.25rem 0.375rem;\n margin: -0.25rem -0.375rem;\n}\n.claudebernard-therapeutic-alternative__count-badge {\n font-size: 0.75rem;\n color: var(--bcbd-white-color, #fff);\n background: var(--bcbd-primary-color, #0C75AD);\n border-radius: 0.25rem;\n padding: 0.125rem 0.375rem;\n flex-shrink: 0;\n}\n.claudebernard-therapeutic-alternative__selected-chips {\n display: flex;\n gap: 0.375rem;\n flex-wrap: wrap;\n margin-top: 0.5rem;\n}\n.claudebernard-therapeutic-
|
|
2330
|
+
const therapeuticAlternativeCSS = '.claudebernard-therapeutic-alternative {\n font-family: var(--custom-font-family, "Ubuntu"), sans-serif;\n padding: 1rem;\n}\n.claudebernard-therapeutic-alternative-without-title {\n padding-top: 0;\n max-width: 70vw;\n width: 60vw;\n max-height: 70vh;\n}\n.claudebernard-therapeutic-alternative-title {\n font-size: 0.75rem;\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--bcbd-gray-500-color, #64748B);\n margin-bottom: 0.375rem;\n display: block;\n}\n.claudebernard-therapeutic-alternative__source {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 1rem;\n flex-wrap: wrap;\n}\n.claudebernard-therapeutic-alternative__source .claudebernard-therapeutic-alternative-title {\n margin-bottom: 0;\n}\n.claudebernard-therapeutic-alternative__source-actions {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex-wrap: wrap;\n}\n.claudebernard-therapeutic-alternative__disclaimer {\n margin-bottom: 1rem;\n}\n.claudebernard-therapeutic-alternative__loading {\n width: 100%;\n height: 30%;\n padding-top: 2rem;\n display: flex;\n justify-content: center;\n}\n.claudebernard-therapeutic-alternative__section {\n border: 1px solid var(--bcbd-border-gray-color, #CBD5E1);\n border-radius: 0.625rem;\n padding: 0.75rem 0.875rem;\n margin-bottom: 1rem;\n}\n.claudebernard-therapeutic-alternative__section-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n cursor: pointer;\n background: none;\n border: none;\n padding: 0;\n margin-bottom: 0.625rem;\n font-family: inherit;\n line-height: 1;\n}\n.claudebernard-therapeutic-alternative__section-header span:first-child {\n font-size: 0.75rem;\n font-weight: 700;\n color: var(--bcbd-gray-500-color, #64748B);\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n.claudebernard-therapeutic-alternative__chevron {\n font-size: 0.75rem;\n color: var(--bcbd-gray-400-color, #94A3B8);\n transition: transform 0.15s ease-out;\n display: inline-flex;\n}\n.claudebernard-therapeutic-alternative__chevron--expanded {\n transform: rotate(180deg);\n}\n.claudebernard-therapeutic-alternative__pills {\n display: flex;\n gap: 0.5rem;\n flex-wrap: wrap;\n}\n.claudebernard-therapeutic-alternative__pill {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n border: 1px solid var(--bcbd-border-gray-color, #CBD5E1);\n background: #fff;\n color: var(--bcbd-gray-600-color, #475569);\n border-radius: 6.25rem;\n padding: 0.375rem 0.75rem;\n font-size: 0.8125rem;\n font-family: inherit;\n font-weight: 400;\n white-space: nowrap;\n cursor: pointer;\n line-height: 1;\n}\n.claudebernard-therapeutic-alternative__pill--active {\n border-color: var(--bcbd-primary-color, #0C75AD);\n background: color-mix(in srgb, var(--bcbd-primary-color, #0C75AD) 10%, transparent);\n color: var(--bcbd-primary-color, #0C75AD);\n font-weight: 700;\n}\n.claudebernard-therapeutic-alternative__panel {\n border-top: 1px solid var(--bcbd-border-color-200, #E2E8F0);\n margin-top: 0.75rem;\n padding-top: 0.75rem;\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n.claudebernard-therapeutic-alternative__panel-rows {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n max-height: 16.25rem;\n overflow-y: auto;\n padding: 0.25rem 0.125rem;\n}\n.claudebernard-therapeutic-alternative__panel-empty {\n display: block;\n margin-top: 0.75rem;\n font-size: 0.875rem;\n color: var(--bcbd-gray-500-color, #64748B);\n}\n.claudebernard-therapeutic-alternative__panel-expand {\n align-self: flex-start;\n background: none;\n border: none;\n padding: 0.25rem 0 0;\n font-family: inherit;\n font-size: 0.8125rem;\n font-weight: 700;\n color: var(--bcbd-primary-color, #0C75AD);\n cursor: pointer;\n}\n.claudebernard-therapeutic-alternative__panel-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n border-radius: 0.375rem;\n --bcbd-checkbox-font-size: 0.875rem;\n --bcbd-checkbox-size: 0.9375rem;\n}\n.claudebernard-therapeutic-alternative__panel-row--pending {\n background: var(--bcbd-gray-100-color, #F1F5F9);\n padding: 0.25rem 0.375rem;\n margin: -0.25rem -0.375rem;\n}\n.claudebernard-therapeutic-alternative__panel-row--possessed {\n font-weight: 700;\n}\n.claudebernard-therapeutic-alternative__count-badge {\n font-size: 0.75rem;\n color: var(--bcbd-white-color, #fff);\n background: var(--bcbd-primary-color, #0C75AD);\n border-radius: 0.25rem;\n padding: 0.125rem 0.375rem;\n flex-shrink: 0;\n}\n.claudebernard-therapeutic-alternative__selected-chips {\n display: flex;\n gap: 0.375rem;\n flex-wrap: wrap;\n margin-top: 0.5rem;\n}\n.claudebernard-therapeutic-alternative__results {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n.claudebernard-therapeutic-alternative__results-header {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n margin-bottom: 0.625rem;\n}\n.claudebernard-therapeutic-alternative__results-badge {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n background: color-mix(in srgb, var(--bcbd-primary-color, #0C75AD) 10%, transparent);\n color: var(--bcbd-primary-color, #0C75AD);\n border-radius: 6.25rem;\n padding: 0.3125rem 0.75rem;\n font-size: 0.8125rem;\n font-weight: 700;\n line-height: 1;\n}\n.claudebernard-therapeutic-alternative__results-list {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n.claudebernard-therapeutic-alternative__no-results {\n text-align: center;\n padding: 1.5rem 1rem;\n color: var(--bcbd-gray-500-color, #64748B);\n font-size: 0.875rem;\n}\n.claudebernard-therapeutic-alternative__no-medication {\n padding: 1.25rem;\n}';
|
|
2331
|
+
function emptyScopeCriteria() {
|
|
2332
|
+
return { indicationIds: [], atcClassIds: [] };
|
|
2333
|
+
}
|
|
2325
2334
|
function emptyIncludeCriteria() {
|
|
2326
|
-
return {
|
|
2335
|
+
return { activeSubstanceIds: [], galenicFormIds: [], dosageIds: [], routeOfAdministrationIds: [] };
|
|
2327
2336
|
}
|
|
2328
2337
|
function emptyExcludeCriteria() {
|
|
2329
2338
|
return {
|
|
@@ -2332,14 +2341,18 @@ function emptyExcludeCriteria() {
|
|
|
2332
2341
|
excipientIds: [],
|
|
2333
2342
|
contraindicationIds: [],
|
|
2334
2343
|
cautionIds: [],
|
|
2335
|
-
precautionIds: []
|
|
2344
|
+
precautionIds: [],
|
|
2345
|
+
sideEffectIds: []
|
|
2336
2346
|
};
|
|
2337
2347
|
}
|
|
2338
2348
|
function emptyQuickFilters() {
|
|
2339
2349
|
return { sameGalenicForm: false, sameDosage: false, sameRouteOfAdministration: false };
|
|
2340
2350
|
}
|
|
2351
|
+
function emptyScopeFacets() {
|
|
2352
|
+
return { indications: [], atcClasses: [] };
|
|
2353
|
+
}
|
|
2341
2354
|
function emptyIncludeFacets() {
|
|
2342
|
-
return {
|
|
2355
|
+
return { activeSubstances: [], galenicForms: [], dosages: [], routesOfAdministration: [] };
|
|
2343
2356
|
}
|
|
2344
2357
|
function emptyExcludeFacets() {
|
|
2345
2358
|
return {
|
|
@@ -2348,11 +2361,15 @@ function emptyExcludeFacets() {
|
|
|
2348
2361
|
excipients: [],
|
|
2349
2362
|
contraindications: [],
|
|
2350
2363
|
cautions: [],
|
|
2351
|
-
precautions: []
|
|
2364
|
+
precautions: [],
|
|
2365
|
+
sideEffects: []
|
|
2352
2366
|
};
|
|
2353
2367
|
}
|
|
2368
|
+
function emptyScopeLabels() {
|
|
2369
|
+
return { indications: {}, atcClasses: {} };
|
|
2370
|
+
}
|
|
2354
2371
|
function emptyIncludeLabels() {
|
|
2355
|
-
return {
|
|
2372
|
+
return { activeSubstances: {}, galenicForms: {}, dosages: {}, routesOfAdministration: {} };
|
|
2356
2373
|
}
|
|
2357
2374
|
function labelsForIds(ids, values) {
|
|
2358
2375
|
const labelById = new Map(values.filter((v2) => v2.id).map((v2) => [v2.id, v2.label ?? v2.id]));
|
|
@@ -2372,7 +2389,8 @@ function emptyExcludeLabels() {
|
|
|
2372
2389
|
excipients: {},
|
|
2373
2390
|
contraindications: {},
|
|
2374
2391
|
cautions: {},
|
|
2375
|
-
precautions: {}
|
|
2392
|
+
precautions: {},
|
|
2393
|
+
sideEffects: {}
|
|
2376
2394
|
};
|
|
2377
2395
|
}
|
|
2378
2396
|
var __defProp = Object.defineProperty;
|
|
@@ -2397,26 +2415,31 @@ let TherapeuticAlternative = class extends i$1 {
|
|
|
2397
2415
|
this.totalElements = 0;
|
|
2398
2416
|
this.lastRequestHadNoSelectedCriteria = true;
|
|
2399
2417
|
this.currentPage = 1;
|
|
2418
|
+
this.scope = emptyScopeCriteria();
|
|
2400
2419
|
this.include = emptyIncludeCriteria();
|
|
2401
2420
|
this.exclude = emptyExcludeCriteria();
|
|
2402
2421
|
this.quickFilters = emptyQuickFilters();
|
|
2422
|
+
this.quickFilterFacets = {};
|
|
2423
|
+
this.scopeFacets = emptyScopeFacets();
|
|
2403
2424
|
this.includeFacets = emptyIncludeFacets();
|
|
2404
2425
|
this.excludeFacets = emptyExcludeFacets();
|
|
2405
2426
|
this.truncatedGroups = [];
|
|
2406
2427
|
this.expandFacetGroupKey = null;
|
|
2428
|
+
this.scopeLabels = emptyScopeLabels();
|
|
2407
2429
|
this.includeLabels = emptyIncludeLabels();
|
|
2408
2430
|
this.excludeLabels = emptyExcludeLabels();
|
|
2409
|
-
this.
|
|
2431
|
+
this.openScopeGroup = null;
|
|
2410
2432
|
this.openExclusionGroup = null;
|
|
2433
|
+
this.openIncludeGroup = null;
|
|
2411
2434
|
this.checklistSearchTerm = "";
|
|
2412
|
-
this.sectionCollapsed = {
|
|
2435
|
+
this.sectionCollapsed = { scope: false, include: false, exclusion: false, quickFilters: false };
|
|
2413
2436
|
this.apiSearchError = null;
|
|
2414
2437
|
this.apiSearchLoading = false;
|
|
2415
2438
|
this.pendingToggleIds = /* @__PURE__ */ new Set();
|
|
2416
2439
|
this.apiReplaceError = null;
|
|
2417
2440
|
this.apiReplaceLoading = false;
|
|
2418
2441
|
this.missingPrescriptionError = null;
|
|
2419
|
-
this.
|
|
2442
|
+
this.hasAppliedInitialScope = false;
|
|
2420
2443
|
this.requestSeq = 0;
|
|
2421
2444
|
}
|
|
2422
2445
|
get currentMedication() {
|
|
@@ -2439,20 +2462,30 @@ let TherapeuticAlternative = class extends i$1 {
|
|
|
2439
2462
|
});
|
|
2440
2463
|
}
|
|
2441
2464
|
if (changedProperties.has("medication") && this.currentMedication) {
|
|
2442
|
-
this.
|
|
2443
|
-
this.include = emptyIncludeCriteria();
|
|
2444
|
-
this.exclude = emptyExcludeCriteria();
|
|
2445
|
-
this.includeLabels = emptyIncludeLabels();
|
|
2446
|
-
this.excludeLabels = emptyExcludeLabels();
|
|
2447
|
-
this.quickFilters = emptyQuickFilters();
|
|
2448
|
-
this.openInclusionGroup = null;
|
|
2449
|
-
this.openExclusionGroup = null;
|
|
2450
|
-
this.expandFacetGroupKey = null;
|
|
2451
|
-
this.currentPage = 1;
|
|
2452
|
-
this.hasAppliedInitialInclude = false;
|
|
2453
|
-
this.fetchAlternatives();
|
|
2465
|
+
this.resetAllCriteria();
|
|
2454
2466
|
}
|
|
2455
2467
|
}
|
|
2468
|
+
// Shared by a medication change (updated() above) and the user-facing "Réinitialiser" button:
|
|
2469
|
+
// both mean "start over from the initial state for this medication" -- clearing
|
|
2470
|
+
// hasAppliedInitialScope so the next response re-derives and re-applies scope's own pre-check
|
|
2471
|
+
// (see applyResponse) rather than leaving scope empty, which would NOT be the initial state.
|
|
2472
|
+
resetAllCriteria() {
|
|
2473
|
+
this.clearScheduledRefetch();
|
|
2474
|
+
this.scope = emptyScopeCriteria();
|
|
2475
|
+
this.include = emptyIncludeCriteria();
|
|
2476
|
+
this.exclude = emptyExcludeCriteria();
|
|
2477
|
+
this.scopeLabels = emptyScopeLabels();
|
|
2478
|
+
this.includeLabels = emptyIncludeLabels();
|
|
2479
|
+
this.excludeLabels = emptyExcludeLabels();
|
|
2480
|
+
this.quickFilters = emptyQuickFilters();
|
|
2481
|
+
this.openScopeGroup = null;
|
|
2482
|
+
this.openExclusionGroup = null;
|
|
2483
|
+
this.openIncludeGroup = null;
|
|
2484
|
+
this.expandFacetGroupKey = null;
|
|
2485
|
+
this.currentPage = 1;
|
|
2486
|
+
this.hasAppliedInitialScope = false;
|
|
2487
|
+
this.fetchAlternatives();
|
|
2488
|
+
}
|
|
2456
2489
|
disconnectedCallback() {
|
|
2457
2490
|
super.disconnectedCallback();
|
|
2458
2491
|
this.clearScheduledRefetch();
|
|
@@ -2471,10 +2504,11 @@ let TherapeuticAlternative = class extends i$1 {
|
|
|
2471
2504
|
// Mirrors the backend's own hasNoSelectedCriteria(request) -- must be evaluated against
|
|
2472
2505
|
// whatever's about to be SENT, not read back later, see lastRequestHadNoSelectedCriteria.
|
|
2473
2506
|
hasNoSelectedCriteria() {
|
|
2474
|
-
const
|
|
2507
|
+
const anyScope = this.scope.indicationIds.length > 0 || this.scope.atcClassIds.length > 0;
|
|
2508
|
+
const anyInclude = Object.values(this.include).some((ids) => ids.length > 0);
|
|
2475
2509
|
const anyExclude = Object.values(this.exclude).some((ids) => ids.length > 0);
|
|
2476
2510
|
const anyQuickFilter = this.quickFilters.sameGalenicForm || this.quickFilters.sameDosage || this.quickFilters.sameRouteOfAdministration;
|
|
2477
|
-
return !anyInclude && !anyExclude && !anyQuickFilter;
|
|
2511
|
+
return !anyScope && !anyInclude && !anyExclude && !anyQuickFilter;
|
|
2478
2512
|
}
|
|
2479
2513
|
fetchAlternatives() {
|
|
2480
2514
|
const productId = this.sourceProductId;
|
|
@@ -2486,6 +2520,7 @@ let TherapeuticAlternative = class extends i$1 {
|
|
|
2486
2520
|
this.apiSearchError = null;
|
|
2487
2521
|
this.lastRequestHadNoSelectedCriteria = this.hasNoSelectedCriteria();
|
|
2488
2522
|
const body = {
|
|
2523
|
+
scope: this.scope,
|
|
2489
2524
|
include: this.include,
|
|
2490
2525
|
exclude: this.exclude,
|
|
2491
2526
|
quickFilters: this.quickFilters,
|
|
@@ -2515,68 +2550,122 @@ let TherapeuticAlternative = class extends i$1 {
|
|
|
2515
2550
|
this.sourceProduct = data.sourceProduct ?? null;
|
|
2516
2551
|
this.results = data.results?.content ?? [];
|
|
2517
2552
|
this.totalElements = data.results?.totalElements ?? 0;
|
|
2553
|
+
this.scopeFacets = TherapeuticAlternative.buildScopeFacets(data);
|
|
2518
2554
|
this.includeFacets = TherapeuticAlternative.buildIncludeFacets(data);
|
|
2519
2555
|
this.excludeFacets = TherapeuticAlternative.buildExcludeFacets(data);
|
|
2556
|
+
this.quickFilterFacets = data.facets?.quickFilters ?? {};
|
|
2520
2557
|
this.truncatedGroups = data.facets?.truncatedGroups ?? [];
|
|
2521
|
-
if (!this.
|
|
2522
|
-
this.
|
|
2523
|
-
this.
|
|
2524
|
-
this.
|
|
2525
|
-
indications: labelsForIds(this.
|
|
2526
|
-
|
|
2527
|
-
atcClasses: labelsForIds(this.include.atcClassIds, this.includeFacets.atcClasses)
|
|
2558
|
+
if (!this.hasAppliedInitialScope) {
|
|
2559
|
+
this.hasAppliedInitialScope = true;
|
|
2560
|
+
this.scope = TherapeuticAlternative.deriveInitialScope(this.sourceProduct);
|
|
2561
|
+
this.scopeLabels = {
|
|
2562
|
+
indications: labelsForIds(this.scope.indicationIds, this.scopeFacets.indications),
|
|
2563
|
+
atcClasses: labelsForIds(this.scope.atcClassIds, this.scopeFacets.atcClasses)
|
|
2528
2564
|
};
|
|
2529
2565
|
}
|
|
2530
2566
|
}
|
|
2567
|
+
static buildScopeFacets(data) {
|
|
2568
|
+
return {
|
|
2569
|
+
indications: data.facets?.scope?.indications ?? [],
|
|
2570
|
+
atcClasses: data.facets?.scope?.atcClasses ?? []
|
|
2571
|
+
};
|
|
2572
|
+
}
|
|
2531
2573
|
static buildIncludeFacets(data) {
|
|
2532
2574
|
return {
|
|
2533
|
-
indications: data.facets?.include?.indications ?? [],
|
|
2534
2575
|
activeSubstances: data.facets?.include?.activeSubstances ?? [],
|
|
2535
|
-
|
|
2576
|
+
galenicForms: data.facets?.include?.galenicForms ?? [],
|
|
2577
|
+
dosages: data.facets?.include?.dosages ?? [],
|
|
2578
|
+
routesOfAdministration: data.facets?.include?.routesOfAdministration ?? []
|
|
2536
2579
|
};
|
|
2537
2580
|
}
|
|
2538
2581
|
static buildExcludeFacets(data) {
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
cautions: data.facets?.exclude?.cautions ?? [],
|
|
2545
|
-
precautions: data.facets?.exclude?.precautions ?? []
|
|
2546
|
-
};
|
|
2582
|
+
const facets = emptyExcludeFacets();
|
|
2583
|
+
for (const group of EXCLUSION_GROUPS) {
|
|
2584
|
+
facets[group.key] = data.facets?.exclude?.[group.key] ?? [];
|
|
2585
|
+
}
|
|
2586
|
+
return facets;
|
|
2547
2587
|
}
|
|
2548
|
-
static
|
|
2549
|
-
|
|
2588
|
+
static codesOf(list) {
|
|
2589
|
+
return (list ?? []).map((item) => item.code).filter((code) => !!code);
|
|
2590
|
+
}
|
|
2591
|
+
static deriveInitialScope(sourceProduct) {
|
|
2550
2592
|
return {
|
|
2551
|
-
indicationIds: codesOf(sourceProduct?.extendedProperties?.keywords?.indicationsKeywords),
|
|
2552
|
-
|
|
2553
|
-
//
|
|
2554
|
-
//
|
|
2555
|
-
//
|
|
2556
|
-
//
|
|
2557
|
-
//
|
|
2558
|
-
//
|
|
2559
|
-
//
|
|
2560
|
-
// son propre défaut sur atcClassIds vide (voir le commentaire plus haut sur fetchAlternatives).
|
|
2593
|
+
indicationIds: TherapeuticAlternative.codesOf(sourceProduct?.extendedProperties?.keywords?.indicationsKeywords),
|
|
2594
|
+
// Contrairement à indications (une liste plate de valeurs propres au produit),
|
|
2595
|
+
// sourceProduct.properties.atcClasses est un fil d'Ariane hiérarchique (classe > sous-classe
|
|
2596
|
+
// > ... > code le plus spécifique, ex: M, M01, M01A, M01AX, M01AX02) — les mapper tous comme
|
|
2597
|
+
// ids cochés n'a pas de sens (5 "sélections" pour UNE seule classification), et envoie en
|
|
2598
|
+
// plus des codes ancêtres bien trop larges (ex: "M" = tout le système musculo-squelettique)
|
|
2599
|
+
// dans la requête, invisibles dans l'UI puisque sans label correspondant dans
|
|
2600
|
+
// scopeFacets.atcClasses. Rien n'est pré-coché pour ce groupe : le backend applique déjà son
|
|
2601
|
+
// propre défaut sur atcClassIds vide (voir le commentaire plus haut sur fetchAlternatives).
|
|
2561
2602
|
atcClassIds: []
|
|
2562
2603
|
};
|
|
2563
2604
|
}
|
|
2564
|
-
//
|
|
2565
|
-
//
|
|
2566
|
-
//
|
|
2567
|
-
//
|
|
2568
|
-
|
|
2569
|
-
|
|
2605
|
+
// The ids to bold in an include checklist panel (see renderChecklistPanel's possessedIds param)
|
|
2606
|
+
// -- what the SOURCE product itself already has for that dimension, not what's currently
|
|
2607
|
+
// selected. galenicForms/dosages are single-valued on the source (a product has exactly one), so
|
|
2608
|
+
// each contributes at most one id; activeSubstances/routesOfAdministration can be multi-valued.
|
|
2609
|
+
// dosages is a best-effort string match (source's raw productDosage against the facet's own
|
|
2610
|
+
// dosage-string ids) -- it can occasionally miss on formatting, unlike the others' exact codes.
|
|
2611
|
+
includePossessedIds(key) {
|
|
2612
|
+
const properties = this.sourceProduct?.properties;
|
|
2613
|
+
const composition = this.sourceProduct?.extendedProperties?.composition;
|
|
2614
|
+
switch (key) {
|
|
2615
|
+
case "activeSubstances":
|
|
2616
|
+
return new Set(TherapeuticAlternative.codesOf(composition?.activeSubstances));
|
|
2617
|
+
case "galenicForms":
|
|
2618
|
+
return properties?.galenicFormCode ? /* @__PURE__ */ new Set([properties.galenicFormCode]) : /* @__PURE__ */ new Set();
|
|
2619
|
+
case "dosages":
|
|
2620
|
+
return properties?.productDosage ? /* @__PURE__ */ new Set([properties.productDosage]) : /* @__PURE__ */ new Set();
|
|
2621
|
+
case "routesOfAdministration":
|
|
2622
|
+
return new Set(TherapeuticAlternative.codesOf(this.sourceProduct?.extendedProperties?.routes));
|
|
2623
|
+
default:
|
|
2624
|
+
return /* @__PURE__ */ new Set();
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
// Same idea as includePossessedIds, for the exclude checklist panels: the exclude groups' own
|
|
2628
|
+
// values come from the whole candidate pool (see SearchCustomService.addExcludeMustNotQueries),
|
|
2629
|
+
// not just the source product, so "does the source itself already have this excipient/
|
|
2630
|
+
// contraindication/..." is a real, non-trivial question worth answering here too.
|
|
2631
|
+
excludePossessedIds(key) {
|
|
2632
|
+
const composition = this.sourceProduct?.extendedProperties?.composition;
|
|
2633
|
+
const keywords = this.sourceProduct?.extendedProperties?.keywords;
|
|
2634
|
+
switch (key) {
|
|
2635
|
+
case "activeSubstances":
|
|
2636
|
+
return new Set(TherapeuticAlternative.codesOf(composition?.activeSubstances));
|
|
2637
|
+
case "notableExcipients":
|
|
2638
|
+
return new Set(TherapeuticAlternative.codesOf(composition?.excipientsWithKnownEffects));
|
|
2639
|
+
case "excipients":
|
|
2640
|
+
return new Set(TherapeuticAlternative.codesOf(composition?.excipients));
|
|
2641
|
+
case "contraindications":
|
|
2642
|
+
return new Set(TherapeuticAlternative.codesOf(keywords?.contraindicationsKeywords));
|
|
2643
|
+
case "cautions":
|
|
2644
|
+
return new Set(TherapeuticAlternative.codesOf(keywords?.contraindicationsNotRecommendedKeywords));
|
|
2645
|
+
case "precautions":
|
|
2646
|
+
return new Set(TherapeuticAlternative.codesOf(keywords?.precautionsForUseKeywords));
|
|
2647
|
+
case "sideEffects":
|
|
2648
|
+
return new Set(TherapeuticAlternative.codesOf(keywords?.sideEffectsKeywords));
|
|
2649
|
+
default:
|
|
2650
|
+
return /* @__PURE__ */ new Set();
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
// label n'est utilisé que sur la branche "coche" (pour alimenter scopeLabels/includeLabels/
|
|
2654
|
+
// excludeLabels — voir emptyScopeLabels) ; sur la branche "décoche" l'id est simplement retiré
|
|
2655
|
+
// des deux côtés, que l'appelant soit une checkbox du panel (qui a le label sous la main) ou un
|
|
2656
|
+
// chip de rappel (qui décoche un id dont le panel courant n'a peut-être même plus trace).
|
|
2657
|
+
toggleScope(group, id, label) {
|
|
2658
|
+
const current = this.scope[group.idsField] ?? [];
|
|
2570
2659
|
const checking = !current.includes(id);
|
|
2571
2660
|
const next = checking ? [...current, id] : current.filter((existing) => existing !== id);
|
|
2572
|
-
this.
|
|
2573
|
-
const nextLabels = { ...this.
|
|
2661
|
+
this.scope = { ...this.scope, [group.idsField]: next };
|
|
2662
|
+
const nextLabels = { ...this.scopeLabels[group.key] };
|
|
2574
2663
|
if (checking) {
|
|
2575
2664
|
nextLabels[id] = label;
|
|
2576
2665
|
} else {
|
|
2577
2666
|
delete nextLabels[id];
|
|
2578
2667
|
}
|
|
2579
|
-
this.
|
|
2668
|
+
this.scopeLabels = { ...this.scopeLabels, [group.key]: nextLabels };
|
|
2580
2669
|
this.pendingToggleIds = new Set(this.pendingToggleIds).add(id);
|
|
2581
2670
|
this.scheduleRefetch();
|
|
2582
2671
|
}
|
|
@@ -2595,25 +2684,67 @@ let TherapeuticAlternative = class extends i$1 {
|
|
|
2595
2684
|
this.pendingToggleIds = new Set(this.pendingToggleIds).add(id);
|
|
2596
2685
|
this.scheduleRefetch();
|
|
2597
2686
|
}
|
|
2687
|
+
// Checking a specific value in an include group takes over from its corresponding quick filter,
|
|
2688
|
+
// when it has one (see IncludeGroupConfig.quickFilterKey -- activeSubstances has none) --
|
|
2689
|
+
// leaving both on could silently AND into an impossible/contradictory combination (e.g. quick
|
|
2690
|
+
// filter pinned to the source's own "Comprimé" while the checklist asks for "Solution
|
|
2691
|
+
// injectable") rather than the refinement the user asked for. Same rationale as
|
|
2692
|
+
// toggleQuickFilter below, the other direction.
|
|
2693
|
+
toggleInclude(group, id, label) {
|
|
2694
|
+
const current = this.include[group.idsField] ?? [];
|
|
2695
|
+
const checking = !current.includes(id);
|
|
2696
|
+
const next = checking ? [...current, id] : current.filter((existing) => existing !== id);
|
|
2697
|
+
this.include = { ...this.include, [group.idsField]: next };
|
|
2698
|
+
const nextLabels = { ...this.includeLabels[group.key] };
|
|
2699
|
+
if (checking) {
|
|
2700
|
+
nextLabels[id] = label;
|
|
2701
|
+
} else {
|
|
2702
|
+
delete nextLabels[id];
|
|
2703
|
+
}
|
|
2704
|
+
this.includeLabels = { ...this.includeLabels, [group.key]: nextLabels };
|
|
2705
|
+
if (checking && group.quickFilterKey && this.quickFilters[group.quickFilterKey]) {
|
|
2706
|
+
this.quickFilters = { ...this.quickFilters, [group.quickFilterKey]: false };
|
|
2707
|
+
}
|
|
2708
|
+
this.pendingToggleIds = new Set(this.pendingToggleIds).add(id);
|
|
2709
|
+
this.scheduleRefetch();
|
|
2710
|
+
}
|
|
2598
2711
|
toggleQuickFilter(key) {
|
|
2599
|
-
|
|
2712
|
+
const turningOn = !this.quickFilters[key];
|
|
2713
|
+
this.quickFilters = { ...this.quickFilters, [key]: turningOn };
|
|
2714
|
+
if (turningOn) {
|
|
2715
|
+
const group = INCLUDE_GROUPS.find((g2) => g2.quickFilterKey === key);
|
|
2716
|
+
if (group && this.include[group.idsField].length > 0) {
|
|
2717
|
+
this.include = { ...this.include, [group.idsField]: [] };
|
|
2718
|
+
this.includeLabels = { ...this.includeLabels, [group.key]: {} };
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
this.pendingToggleIds = new Set(this.pendingToggleIds).add(key);
|
|
2600
2722
|
this.scheduleRefetch();
|
|
2601
2723
|
}
|
|
2602
2724
|
// The exclude "activeSubstances" group's own backend/truncatedGroups key is
|
|
2603
2725
|
// "excludeActiveSubstances" (see SearchCustomService.assignFacetValues) -- distinct from the
|
|
2604
|
-
//
|
|
2726
|
+
// include dimension of the same frontend name, so it can't reuse ExcludeGroupKey as-is.
|
|
2605
2727
|
// Every other exclude group's backend key matches its frontend key 1:1.
|
|
2606
2728
|
exclusionTruncatedGroupKey(key) {
|
|
2607
2729
|
return key === "activeSubstances" ? "excludeActiveSubstances" : key;
|
|
2608
2730
|
}
|
|
2609
|
-
|
|
2610
|
-
|
|
2731
|
+
// Same rationale as exclusionTruncatedGroupKey, the include side: "activeSubstances"' own
|
|
2732
|
+
// backend key is "includeActiveSubstances".
|
|
2733
|
+
includeTruncatedGroupKey(key) {
|
|
2734
|
+
return key === "activeSubstances" ? "includeActiveSubstances" : key;
|
|
2735
|
+
}
|
|
2736
|
+
toggleScopePanel(key) {
|
|
2737
|
+
this.openScopeGroup = this.openScopeGroup === key ? null : key;
|
|
2611
2738
|
this.checklistSearchTerm = "";
|
|
2612
2739
|
}
|
|
2613
2740
|
toggleExclusionPanel(key) {
|
|
2614
2741
|
this.openExclusionGroup = this.openExclusionGroup === key ? null : key;
|
|
2615
2742
|
this.checklistSearchTerm = "";
|
|
2616
2743
|
}
|
|
2744
|
+
toggleIncludePanel(key) {
|
|
2745
|
+
this.openIncludeGroup = this.openIncludeGroup === key ? null : key;
|
|
2746
|
+
this.checklistSearchTerm = "";
|
|
2747
|
+
}
|
|
2617
2748
|
// "Afficher plus" for one truncated group's checklist -- see expandFacetGroupKey. Triggers an
|
|
2618
2749
|
// immediate refetch (unlike opening/closing a panel) since this is the one action that actually
|
|
2619
2750
|
// needs a new response to have an effect.
|
|
@@ -2691,7 +2822,7 @@ let TherapeuticAlternative = class extends i$1 {
|
|
|
2691
2822
|
this.apiReplaceLoading = false;
|
|
2692
2823
|
}
|
|
2693
2824
|
}
|
|
2694
|
-
renderChecklistPanel(items, selectedIds, showCount, countTooltip, onToggle, testIdPrefix, showCode = false, truncated = false, onExpand) {
|
|
2825
|
+
renderChecklistPanel(items, selectedIds, showCount, countTooltip, onToggle, testIdPrefix, showCode = false, truncated = false, onExpand, possessedIds = /* @__PURE__ */ new Set()) {
|
|
2695
2826
|
const visibleItems = items.filter((item) => {
|
|
2696
2827
|
if (item.id !== void 0 && this.pendingToggleIds.has(item.id)) {
|
|
2697
2828
|
return true;
|
|
@@ -2718,7 +2849,8 @@ let TherapeuticAlternative = class extends i$1 {
|
|
|
2718
2849
|
const pending = this.pendingToggleIds.has(item.id);
|
|
2719
2850
|
const rowClasses = [
|
|
2720
2851
|
`${PREFIX}-therapeutic-alternative__panel-row`,
|
|
2721
|
-
pending ? `${PREFIX}-therapeutic-alternative__panel-row--pending` : ""
|
|
2852
|
+
pending ? `${PREFIX}-therapeutic-alternative__panel-row--pending` : "",
|
|
2853
|
+
possessedIds.has(item.id) ? `${PREFIX}-therapeutic-alternative__panel-row--possessed` : ""
|
|
2722
2854
|
].join(" ");
|
|
2723
2855
|
return b`
|
|
2724
2856
|
<div class=${rowClasses}>
|
|
@@ -2764,8 +2896,8 @@ let TherapeuticAlternative = class extends i$1 {
|
|
|
2764
2896
|
}
|
|
2765
2897
|
// Only rendered past CHECKLIST_SEARCH_THRESHOLD (see renderChecklistPanel) -- below that, typing
|
|
2766
2898
|
// is slower than just scanning the list by eye. checklistSearchTerm is reset whenever a different
|
|
2767
|
-
// group/section opens (
|
|
2768
|
-
// previous panel can't silently hide everything in the next one.
|
|
2899
|
+
// group/section opens (toggleScopePanel/toggleExclusionPanel/toggleIncludePanel) so a leftover
|
|
2900
|
+
// term from a previous panel can't silently hide everything in the next one.
|
|
2769
2901
|
renderChecklistSearch(testIdPrefix) {
|
|
2770
2902
|
return b`
|
|
2771
2903
|
<bcbd-input
|
|
@@ -2781,9 +2913,9 @@ let TherapeuticAlternative = class extends i$1 {
|
|
|
2781
2913
|
}
|
|
2782
2914
|
// Rappel des valeurs actuellement cochées pour TOUTE la section (tous ses groupes confondus),
|
|
2783
2915
|
// toujours affiché dès qu'il y en a — indépendamment de quel panel est ouvert, voire qu'aucun ne
|
|
2784
|
-
// le soit. C'est la vue "source de vérité" (includeLabels/excludeLabels) plutôt que
|
|
2785
|
-
// ouvert : une valeur cochée reste visible et décochable ici même si un refetch a fait
|
|
2786
|
-
// sa ligne du panel courant (voir
|
|
2916
|
+
// le soit. C'est la vue "source de vérité" (scopeLabels/includeLabels/excludeLabels) plutôt que
|
|
2917
|
+
// le panel ouvert : une valeur cochée reste visible et décochable ici même si un refetch a fait
|
|
2918
|
+
// disparaître sa ligne du panel courant (voir emptyScopeLabels).
|
|
2787
2919
|
renderSelectedChips(labelsByGroup, onRemove, testIdPrefix) {
|
|
2788
2920
|
const entries = [];
|
|
2789
2921
|
for (const [groupKey, labels] of Object.entries(labelsByGroup)) {
|
|
@@ -2830,36 +2962,36 @@ let TherapeuticAlternative = class extends i$1 {
|
|
|
2830
2962
|
</button>
|
|
2831
2963
|
`;
|
|
2832
2964
|
}
|
|
2833
|
-
|
|
2834
|
-
const chips = this.renderSelectedChips(this.
|
|
2835
|
-
const group =
|
|
2965
|
+
renderScopeSection() {
|
|
2966
|
+
const chips = this.renderSelectedChips(this.scopeLabels, (groupKey, id) => {
|
|
2967
|
+
const group = SCOPE_GROUPS.find((g2) => g2.key === groupKey);
|
|
2836
2968
|
if (group) {
|
|
2837
|
-
this.
|
|
2969
|
+
this.toggleScope(group, id, "");
|
|
2838
2970
|
}
|
|
2839
|
-
}, "
|
|
2840
|
-
if (this.sectionCollapsed.
|
|
2971
|
+
}, "scope");
|
|
2972
|
+
if (this.sectionCollapsed.scope) {
|
|
2841
2973
|
return chips;
|
|
2842
2974
|
}
|
|
2843
|
-
const openGroup =
|
|
2844
|
-
const pills =
|
|
2975
|
+
const openGroup = SCOPE_GROUPS.find((group) => group.key === this.openScopeGroup);
|
|
2976
|
+
const pills = SCOPE_GROUPS.map((group) => this.renderPill(
|
|
2845
2977
|
group.label,
|
|
2846
|
-
this.
|
|
2847
|
-
group.key === this.
|
|
2848
|
-
() => this.
|
|
2849
|
-
`
|
|
2978
|
+
this.scope[group.idsField].length > 0,
|
|
2979
|
+
group.key === this.openScopeGroup,
|
|
2980
|
+
() => this.toggleScopePanel(group.key),
|
|
2981
|
+
`scope-pill-${group.key}`
|
|
2850
2982
|
));
|
|
2851
2983
|
return b`
|
|
2852
|
-
<div class="${PREFIX}-therapeutic-alternative__pills" data-test-id="
|
|
2984
|
+
<div class="${PREFIX}-therapeutic-alternative__pills" data-test-id="scope-pills">
|
|
2853
2985
|
${pills}
|
|
2854
2986
|
</div>
|
|
2855
2987
|
${chips}
|
|
2856
2988
|
${openGroup ? this.renderChecklistPanel(
|
|
2857
|
-
this.
|
|
2858
|
-
this.
|
|
2989
|
+
this.scopeFacets[openGroup.key],
|
|
2990
|
+
this.scope[openGroup.idsField],
|
|
2859
2991
|
true,
|
|
2860
2992
|
INCLUDE_COUNT_TOOLTIP,
|
|
2861
|
-
(id, label) => this.
|
|
2862
|
-
`
|
|
2993
|
+
(id, label) => this.toggleScope(openGroup, id, label),
|
|
2994
|
+
`scope-${openGroup.key}`,
|
|
2863
2995
|
openGroup.key === "atcClasses",
|
|
2864
2996
|
// Already expanded == clicking again would send the exact same expandFacetGroup and get
|
|
2865
2997
|
// the exact same (still truncated, if it's a group with more than FACET_VALUES_EXPANDED_SIZE
|
|
@@ -2904,7 +3036,47 @@ let TherapeuticAlternative = class extends i$1 {
|
|
|
2904
3036
|
`exclusion-${openGroup.key}`,
|
|
2905
3037
|
false,
|
|
2906
3038
|
canExpand,
|
|
2907
|
-
() => this.requestMoreFacetValues(truncatedGroupKey)
|
|
3039
|
+
() => this.requestMoreFacetValues(truncatedGroupKey),
|
|
3040
|
+
this.excludePossessedIds(openGroup.key)
|
|
3041
|
+
) : A}
|
|
3042
|
+
`;
|
|
3043
|
+
}
|
|
3044
|
+
renderIncludeSection() {
|
|
3045
|
+
const chips = this.renderSelectedChips(this.includeLabels, (groupKey, id) => {
|
|
3046
|
+
const group = INCLUDE_GROUPS.find((g2) => g2.key === groupKey);
|
|
3047
|
+
if (group) {
|
|
3048
|
+
this.toggleInclude(group, id, "");
|
|
3049
|
+
}
|
|
3050
|
+
}, "include");
|
|
3051
|
+
if (this.sectionCollapsed.include) {
|
|
3052
|
+
return chips;
|
|
3053
|
+
}
|
|
3054
|
+
const openGroup = INCLUDE_GROUPS.find((group) => group.key === this.openIncludeGroup);
|
|
3055
|
+
const pills = INCLUDE_GROUPS.map((group) => this.renderPill(
|
|
3056
|
+
group.label,
|
|
3057
|
+
this.include[group.idsField].length > 0,
|
|
3058
|
+
group.key === this.openIncludeGroup,
|
|
3059
|
+
() => this.toggleIncludePanel(group.key),
|
|
3060
|
+
`include-pill-${group.key}`
|
|
3061
|
+
));
|
|
3062
|
+
const truncatedGroupKey = openGroup ? this.includeTruncatedGroupKey(openGroup.key) : null;
|
|
3063
|
+
const canExpand = truncatedGroupKey !== null && this.truncatedGroups.includes(truncatedGroupKey) && this.expandFacetGroupKey !== truncatedGroupKey;
|
|
3064
|
+
return b`
|
|
3065
|
+
<div class="${PREFIX}-therapeutic-alternative__pills" data-test-id="include-pills">
|
|
3066
|
+
${pills}
|
|
3067
|
+
</div>
|
|
3068
|
+
${chips}
|
|
3069
|
+
${openGroup ? this.renderChecklistPanel(
|
|
3070
|
+
this.includeFacets[openGroup.key],
|
|
3071
|
+
this.include[openGroup.idsField],
|
|
3072
|
+
true,
|
|
3073
|
+
INCLUDE_COUNT_TOOLTIP,
|
|
3074
|
+
(id, label) => this.toggleInclude(openGroup, id, label),
|
|
3075
|
+
`include-${openGroup.key}`,
|
|
3076
|
+
false,
|
|
3077
|
+
canExpand,
|
|
3078
|
+
() => this.requestMoreFacetValues(truncatedGroupKey),
|
|
3079
|
+
this.includePossessedIds(openGroup.key)
|
|
2908
3080
|
) : A}
|
|
2909
3081
|
`;
|
|
2910
3082
|
}
|
|
@@ -2912,25 +3084,56 @@ let TherapeuticAlternative = class extends i$1 {
|
|
|
2912
3084
|
if (this.sectionCollapsed.quickFilters) {
|
|
2913
3085
|
return A;
|
|
2914
3086
|
}
|
|
2915
|
-
const
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
3087
|
+
const visibleFilters = QUICK_FILTERS.filter((filter) => {
|
|
3088
|
+
if (this.quickFilters[filter.key] || this.pendingToggleIds.has(filter.key)) {
|
|
3089
|
+
return true;
|
|
3090
|
+
}
|
|
3091
|
+
return this.quickFilterFacets[filter.key] !== 0;
|
|
3092
|
+
});
|
|
3093
|
+
if (visibleFilters.length === 0) {
|
|
3094
|
+
return b`<span class="${PREFIX}-therapeutic-alternative__panel-empty">Aucune valeur disponible.</span>`;
|
|
3095
|
+
}
|
|
3096
|
+
const rows = visibleFilters.map((filter) => {
|
|
3097
|
+
const count = this.quickFilterFacets[filter.key];
|
|
3098
|
+
const pending = this.pendingToggleIds.has(filter.key);
|
|
3099
|
+
const rowClasses = [
|
|
3100
|
+
`${PREFIX}-therapeutic-alternative__panel-row`,
|
|
3101
|
+
pending ? `${PREFIX}-therapeutic-alternative__panel-row--pending` : ""
|
|
2920
3102
|
].join(" ");
|
|
2921
3103
|
return b`
|
|
2922
|
-
<
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
3104
|
+
<div class=${rowClasses}>
|
|
3105
|
+
<bcbd-checkbox
|
|
3106
|
+
id=${`quick-filter-${filter.key}`}
|
|
3107
|
+
label=${filter.label}
|
|
3108
|
+
?checked=${this.quickFilters[filter.key]}
|
|
3109
|
+
color="primary"
|
|
3110
|
+
@change=${() => this.toggleQuickFilter(filter.key)}
|
|
3111
|
+
data-test-id=${`quick-filter-${filter.key}`}>
|
|
3112
|
+
</bcbd-checkbox>
|
|
3113
|
+
${pending ? b`
|
|
3114
|
+
<bcbd-loader theme="standard" size="18" data-test-id=${`quick-filter-loader-${filter.key}`}></bcbd-loader>
|
|
3115
|
+
` : this.renderQuickFilterCount(filter.key, count)}
|
|
3116
|
+
</div>
|
|
2926
3117
|
`;
|
|
2927
3118
|
});
|
|
2928
3119
|
return b`
|
|
2929
|
-
<div class="${PREFIX}-therapeutic-
|
|
2930
|
-
${
|
|
3120
|
+
<div class="${PREFIX}-therapeutic-alternative__panel" data-test-id="quick-filters-panel">
|
|
3121
|
+
<div class="${PREFIX}-therapeutic-alternative__panel-rows">
|
|
3122
|
+
${rows}
|
|
3123
|
+
</div>
|
|
2931
3124
|
</div>
|
|
2932
3125
|
`;
|
|
2933
3126
|
}
|
|
3127
|
+
renderQuickFilterCount(key, count) {
|
|
3128
|
+
if (count === void 0) {
|
|
3129
|
+
return A;
|
|
3130
|
+
}
|
|
3131
|
+
return b`
|
|
3132
|
+
<bcbd-tooltip placement="left" content=${INCLUDE_COUNT_TOOLTIP} data-test-id=${`quick-filter-count-tooltip-${key}`}>
|
|
3133
|
+
<span class="${PREFIX}-therapeutic-alternative__count-badge" data-test-id=${`quick-filter-count-${key}`}>${count}</span>
|
|
3134
|
+
</bcbd-tooltip>
|
|
3135
|
+
`;
|
|
3136
|
+
}
|
|
2934
3137
|
renderSectionHeader(title, section) {
|
|
2935
3138
|
const collapsed = this.sectionCollapsed[section];
|
|
2936
3139
|
const chevronClasses = [
|
|
@@ -2952,8 +3155,8 @@ let TherapeuticAlternative = class extends i$1 {
|
|
|
2952
3155
|
// Turns a result's raw matchReasons into short bullet lines, e.g. "- indiqué pour Arthrose" or
|
|
2953
3156
|
// "- contient Bisoprolol (synonyme de Métoprolol)" -- label/synonymLabel come straight from the
|
|
2954
3157
|
// backend (resolved from the source/matching product's own documents), not looked up in
|
|
2955
|
-
// includeFacets:
|
|
2956
|
-
// is a code found on this specific result product, not a criterion at all.
|
|
3158
|
+
// scopeFacets/includeFacets: those only ever cover the request's criterion codes, never
|
|
3159
|
+
// synonymCode, which is a code found on this specific result product, not a criterion at all.
|
|
2957
3160
|
matchReasonLabels(result) {
|
|
2958
3161
|
const labels = (result.matchReasons ?? []).map((reason) => {
|
|
2959
3162
|
const dimension = reason.dimension ?? "";
|
|
@@ -3022,6 +3225,11 @@ let TherapeuticAlternative = class extends i$1 {
|
|
|
3022
3225
|
</bcbd-pagination>`}
|
|
3023
3226
|
`;
|
|
3024
3227
|
}
|
|
3228
|
+
// The product name in the title replaces the "Produit sélectionné" line when has-title is on; without
|
|
3229
|
+
// a title, that line (see render) is the only place the product name shows.
|
|
3230
|
+
renderTitle(medicationTitle) {
|
|
3231
|
+
return b`<span class="${PREFIX}-therapeutic-alternative-title" data-test-id="title">Alternative Thérapeutique pour ${medicationTitle !== "" ? decodeHtml(medicationTitle) : "ce produit"}</span>`;
|
|
3232
|
+
}
|
|
3025
3233
|
render() {
|
|
3026
3234
|
const containerClasses = [
|
|
3027
3235
|
`${PREFIX}-therapeutic-alternative`,
|
|
@@ -3030,16 +3238,23 @@ let TherapeuticAlternative = class extends i$1 {
|
|
|
3030
3238
|
const medicationTitle = getMedicationTitle(this.currentMedication);
|
|
3031
3239
|
return b`
|
|
3032
3240
|
<div class=${containerClasses} data-test-id="therapeutic-alternative-container">
|
|
3033
|
-
${this.hasTitle ? b`<span class="${PREFIX}-therapeutic-alternative-title">Alternative thérapeutique</span>` : ""}
|
|
3034
3241
|
${this.currentMedication ? b`
|
|
3035
3242
|
<div class="${PREFIX}-therapeutic-alternative__source mb-3" data-test-id="source-product">
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3243
|
+
${this.hasTitle ? this.renderTitle(medicationTitle) : b`
|
|
3244
|
+
<span data-test-id="source-product-label">
|
|
3245
|
+
Produit sélectionné : <span class="text-primary">${medicationTitle !== "" ? decodeHtml(medicationTitle) : "Inconnu"}</span>
|
|
3246
|
+
</span>`}
|
|
3247
|
+
<div class="${PREFIX}-therapeutic-alternative__source-actions">
|
|
3248
|
+
<bcbd-button size="small" background-color="secondary" outlined
|
|
3249
|
+
?disabled=${this.hasNoSelectedCriteria()}
|
|
3250
|
+
@click=${this.resetAllCriteria} data-test-id="reset-criteria-button">
|
|
3251
|
+
Réinitialiser
|
|
3252
|
+
</bcbd-button>
|
|
3253
|
+
<bcbd-button size="small" background-color="primary" outlined
|
|
3254
|
+
@click=${this.cancelAlternative} data-test-id="cancel-alternative-button">
|
|
3255
|
+
Annuler
|
|
3256
|
+
</bcbd-button>
|
|
3257
|
+
</div>
|
|
3043
3258
|
</div>
|
|
3044
3259
|
|
|
3045
3260
|
<div class="${PREFIX}-therapeutic-alternative__disclaimer">
|
|
@@ -3059,13 +3274,18 @@ let TherapeuticAlternative = class extends i$1 {
|
|
|
3059
3274
|
</bcbd-alert>
|
|
3060
3275
|
</div>` : ""}
|
|
3061
3276
|
|
|
3062
|
-
<div class="${PREFIX}-therapeutic-alternative__section" data-test-id="
|
|
3063
|
-
${this.renderSectionHeader("Rechercher une alternative", "
|
|
3064
|
-
${this.
|
|
3277
|
+
<div class="${PREFIX}-therapeutic-alternative__section" data-test-id="scope-section">
|
|
3278
|
+
${this.renderSectionHeader("Rechercher une alternative", "scope")}
|
|
3279
|
+
${this.renderScopeSection()}
|
|
3280
|
+
</div>
|
|
3281
|
+
|
|
3282
|
+
<div class="${PREFIX}-therapeutic-alternative__section" data-test-id="include-section">
|
|
3283
|
+
${this.renderSectionHeader("Critères d'inclusion", "include")}
|
|
3284
|
+
${this.renderIncludeSection()}
|
|
3065
3285
|
</div>
|
|
3066
3286
|
|
|
3067
3287
|
<div class="${PREFIX}-therapeutic-alternative__section" data-test-id="exclusion-section">
|
|
3068
|
-
${this.renderSectionHeader("
|
|
3288
|
+
${this.renderSectionHeader("Critères d'exclusion", "exclusion")}
|
|
3069
3289
|
${this.renderExclusionSection()}
|
|
3070
3290
|
</div>
|
|
3071
3291
|
|
|
@@ -3077,6 +3297,7 @@ let TherapeuticAlternative = class extends i$1 {
|
|
|
3077
3297
|
<div class="${PREFIX}-therapeutic-alternative__results" data-test-id="results-container">
|
|
3078
3298
|
${this.renderResults()}
|
|
3079
3299
|
</div>` : b`
|
|
3300
|
+
${this.hasTitle ? this.renderTitle(medicationTitle) : ""}
|
|
3080
3301
|
<div class="${PREFIX}-therapeutic-alternative__no-medication" data-test-id="no-medication-container">
|
|
3081
3302
|
<bcbd-alert title-message="Pas de médicament sélectionné"
|
|
3082
3303
|
description-message="Un problème est survenu au moment de sélectionner le produit"
|
|
@@ -3118,6 +3339,9 @@ __decorateClass([
|
|
|
3118
3339
|
__decorateClass([
|
|
3119
3340
|
r()
|
|
3120
3341
|
], TherapeuticAlternative.prototype, "currentPage", 2);
|
|
3342
|
+
__decorateClass([
|
|
3343
|
+
r()
|
|
3344
|
+
], TherapeuticAlternative.prototype, "scope", 2);
|
|
3121
3345
|
__decorateClass([
|
|
3122
3346
|
r()
|
|
3123
3347
|
], TherapeuticAlternative.prototype, "include", 2);
|
|
@@ -3127,6 +3351,12 @@ __decorateClass([
|
|
|
3127
3351
|
__decorateClass([
|
|
3128
3352
|
r()
|
|
3129
3353
|
], TherapeuticAlternative.prototype, "quickFilters", 2);
|
|
3354
|
+
__decorateClass([
|
|
3355
|
+
r()
|
|
3356
|
+
], TherapeuticAlternative.prototype, "quickFilterFacets", 2);
|
|
3357
|
+
__decorateClass([
|
|
3358
|
+
r()
|
|
3359
|
+
], TherapeuticAlternative.prototype, "scopeFacets", 2);
|
|
3130
3360
|
__decorateClass([
|
|
3131
3361
|
r()
|
|
3132
3362
|
], TherapeuticAlternative.prototype, "includeFacets", 2);
|
|
@@ -3139,6 +3369,9 @@ __decorateClass([
|
|
|
3139
3369
|
__decorateClass([
|
|
3140
3370
|
r()
|
|
3141
3371
|
], TherapeuticAlternative.prototype, "expandFacetGroupKey", 2);
|
|
3372
|
+
__decorateClass([
|
|
3373
|
+
r()
|
|
3374
|
+
], TherapeuticAlternative.prototype, "scopeLabels", 2);
|
|
3142
3375
|
__decorateClass([
|
|
3143
3376
|
r()
|
|
3144
3377
|
], TherapeuticAlternative.prototype, "includeLabels", 2);
|
|
@@ -3147,10 +3380,13 @@ __decorateClass([
|
|
|
3147
3380
|
], TherapeuticAlternative.prototype, "excludeLabels", 2);
|
|
3148
3381
|
__decorateClass([
|
|
3149
3382
|
r()
|
|
3150
|
-
], TherapeuticAlternative.prototype, "
|
|
3383
|
+
], TherapeuticAlternative.prototype, "openScopeGroup", 2);
|
|
3151
3384
|
__decorateClass([
|
|
3152
3385
|
r()
|
|
3153
3386
|
], TherapeuticAlternative.prototype, "openExclusionGroup", 2);
|
|
3387
|
+
__decorateClass([
|
|
3388
|
+
r()
|
|
3389
|
+
], TherapeuticAlternative.prototype, "openIncludeGroup", 2);
|
|
3154
3390
|
__decorateClass([
|
|
3155
3391
|
r()
|
|
3156
3392
|
], TherapeuticAlternative.prototype, "checklistSearchTerm", 2);
|
package/package.json
CHANGED