@tekkare/romulus 0.1.251 → 0.1.252
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/module.json +1 -1
- package/layer/app/app.config.ts +6 -0
- package/layer/app/components/OipPubmedSummary.vue +223 -0
- package/layer/app/components/OipResourceDrawer.vue +331 -0
- package/layer/app/components/OipResourceDrawerLink.vue +40 -0
- package/layer/app/components/OipResourceField.vue +41 -0
- package/layer/app/components/OipResourceGenericCard.vue +54 -0
- package/layer/app/components/OipResourceSource.vue +50 -0
- package/layer/app/components/OipResourceTags.vue +65 -0
- package/layer/app/components/OipResourceView.vue +107 -0
- package/layer/app/components/oip-drugs/fiches/AssociationFiche.vue +775 -0
- package/layer/app/components/oip-drugs/fiches/BrandFiche.vue +1111 -0
- package/layer/app/components/oip-drugs/fiches/IndicationFiche.vue +182 -0
- package/layer/app/components/oip-drugs/fiches/JoFiche.vue +101 -0
- package/layer/app/components/oip-drugs/fiches/PndsFiche.vue +279 -0
- package/layer/app/components/oip-drugs/heart/JoCard.vue +314 -0
- package/layer/app/components/oip-drugs/heart/TextHighlight.vue +326 -0
- package/layer/app/components/oip-drugs/heart/ansm/BrandCard.vue +187 -0
- package/layer/app/components/oip-drugs/heart/ansm/CisDetail.vue +363 -0
- package/layer/app/components/oip-drugs/heart/ansm/CisTable.vue +167 -0
- package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyCard.vue +373 -0
- package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyFiche.vue +372 -0
- package/layer/app/components/oip-drugs/heart/associations/TranscriptionCard.vue +55 -0
- package/layer/app/components/oip-drugs/heart/elastic/SourceContainerElastic.vue +103 -0
- package/layer/app/components/oip-drugs/heart/france/BrandGroupCard.vue +450 -0
- package/layer/app/components/oip-drugs/heart/mitm/Card.vue +199 -0
- package/layer/app/components/oip-drugs/heart/mitm/Source.vue +23 -0
- package/layer/app/components/oip-drugs/heart/mitm/details/Overview.vue +381 -0
- package/layer/app/components/oip-drugs/heart/put/Card.vue +212 -0
- package/layer/app/components/oip-drugs/heart/results/AccesCard.vue +696 -0
- package/layer/app/components/oip-drugs/heart/results/AlertCard.vue +342 -0
- package/layer/app/components/oip-drugs/heart/results/AsmrCursor.vue +229 -0
- package/layer/app/components/oip-drugs/heart/results/EvalCard.vue +370 -0
- package/layer/app/components/oip-drugs/heart/results/IndicCard.vue +743 -0
- package/layer/app/components/oip-drugs/heart/results/MatchExplain.vue +243 -0
- package/layer/app/components/oip-drugs/heart/results/PrefRows.vue +105 -0
- package/layer/app/components/oip-drugs/heart/results/details/HasHistory.vue +487 -0
- package/layer/app/components/oip-drugs/heart/results/details/InternationalCountryCard.vue +461 -0
- package/layer/app/components/oip-drugs/heart/results/details/InternationalOverview.vue +200 -0
- package/layer/app/components/oip-drugs/heart/results/details/InternationalPriceDrawer.vue +647 -0
- package/layer/app/components/oip-drugs/heart/results/details/Monograph.vue +128 -0
- package/layer/app/components/oip-drugs/heart/results/details/OpinionDrawer.vue +535 -0
- package/layer/app/components/oip-drugs/heart/results/details/PresentationDrawer.vue +738 -0
- package/layer/app/components/oip-drugs/heart/results/details/Price.vue +227 -0
- package/layer/app/components/oip-drugs/heart/results/details/PriceOverview.vue +1261 -0
- package/layer/app/components/oip-drugs/heart/results/details/RecordDrawer.vue +494 -0
- package/layer/app/components/oip-drugs/heart/results/details/RecordList.vue +426 -0
- package/layer/app/components/oip-drugs/heart/results/les/Card.vue +340 -0
- package/layer/app/components/oip-drugs/heart/results/les/Drawer.vue +67 -0
- package/layer/app/components/oip-drugs/heart/results/les/IndicationDetail.vue +149 -0
- package/layer/app/components/oip-drugs/heart/results/les/Source.vue +20 -0
- package/layer/app/components/oip-drugs/heart/results/les/SourceTag.vue +39 -0
- package/layer/app/components/oip-drugs/heart/searchPage/DynamicSource.vue +29 -0
- package/layer/app/components/oip-drugs/medicam/market/Chart.vue +121 -0
- package/layer/app/components/oip-drugs/outpatient/ProductEvolution.vue +236 -0
- package/layer/app/components/oip-drugs/resources/Ansm.vue +75 -0
- package/layer/app/components/oip-drugs/resources/Association.vue +30 -0
- package/layer/app/components/oip-drugs/resources/Brand.vue +65 -0
- package/layer/app/components/oip-drugs/resources/Contribution.vue +216 -0
- package/layer/app/components/oip-drugs/resources/Hospital.vue +151 -0
- package/layer/app/components/oip-drugs/resources/Indication.vue +54 -0
- package/layer/app/components/oip-drugs/resources/Jo.vue +57 -0
- package/layer/app/components/oip-drugs/resources/Medicam.vue +140 -0
- package/layer/app/components/oip-drugs/resources/Mitm.vue +84 -0
- package/layer/app/components/oip-drugs/resources/NationalAssembly.vue +93 -0
- package/layer/app/components/oip-drugs/resources/Outpatient.vue +97 -0
- package/layer/app/components/oip-drugs/resources/Pnds.vue +24 -0
- package/layer/app/components/oip-drugs/resources/Price.vue +106 -0
- package/layer/app/components/oip-drugs/resources/Put.vue +143 -0
- package/layer/app/components/oip-science/appel-a-projet/Cards.vue +162 -0
- package/layer/app/components/oip-science/clinical-trials/Cards.vue +382 -0
- package/layer/app/components/oip-science/clinical-trials/CardsGlobal.vue +273 -0
- package/layer/app/components/oip-science/clinical-trials/LinkedList.vue +366 -0
- package/layer/app/components/oip-science/clinical-trials/PairBadge.vue +25 -0
- package/layer/app/components/oip-science/clinical-trials/PairBody.vue +182 -0
- package/layer/app/components/oip-science/clinical-trials/SitesDrawer.vue +95 -0
- package/layer/app/components/oip-science/common/Form.vue +73 -0
- package/layer/app/components/oip-science/common/MatchAvgTag.vue +283 -0
- package/layer/app/components/oip-science/common/NoData.vue +16 -0
- package/layer/app/components/oip-science/common/ScoreBadge.vue +35 -0
- package/layer/app/components/oip-science/common/Source.vue +31 -0
- package/layer/app/components/oip-science/common/TableTags.vue +279 -0
- package/layer/app/components/oip-science/common/Timeline.vue +104 -0
- package/layer/app/components/oip-science/common/ToolTips.vue +89 -0
- package/layer/app/components/oip-science/cross/publicationPubmed.vue +484 -0
- package/layer/app/components/oip-science/crossData/CrossDataLocations.vue +71 -0
- package/layer/app/components/oip-science/ctgov/Investigators.vue +148 -0
- package/layer/app/components/oip-science/ctgov/Locations.vue +102 -0
- package/layer/app/components/oip-science/ctgov/Publications.vue +52 -0
- package/layer/app/components/oip-science/ctgov/Sponsors.vue +66 -0
- package/layer/app/components/oip-science/ctgov/StudyDescription.vue +1136 -0
- package/layer/app/components/oip-science/ctgov-centers/ClinicalTrials.vue +40 -0
- package/layer/app/components/oip-science/ctgov-centers/Publications.vue +50 -0
- package/layer/app/components/oip-science/ctgov-investigators/ClinicalTrials.vue +43 -0
- package/layer/app/components/oip-science/ctgov-investigators/Informations.vue +171 -0
- package/layer/app/components/oip-science/ctgov-investigators/Publications.vue +44 -0
- package/layer/app/components/oip-science/elastic/DateRangeFilter.vue +209 -0
- package/layer/app/components/oip-science/elastic/Filter.vue +719 -0
- package/layer/app/components/oip-science/elastic/MatchExplain.vue +152 -0
- package/layer/app/components/oip-science/establishments/Cards.vue +183 -0
- package/layer/app/components/oip-science/establishments/ClinicalTrials.vue +62 -0
- package/layer/app/components/oip-science/establishments/Hcps.vue +227 -0
- package/layer/app/components/oip-science/establishments/Informations.vue +263 -0
- package/layer/app/components/oip-science/establishments/LinkedList.vue +153 -0
- package/layer/app/components/oip-science/establishments/MatchedLabels.vue +194 -0
- package/layer/app/components/oip-science/establishments/PublicFinance.vue +62 -0
- package/layer/app/components/oip-science/euct/Investigators.vue +125 -0
- package/layer/app/components/oip-science/euct/Locations.vue +102 -0
- package/layer/app/components/oip-science/euct/Products.vue +101 -0
- package/layer/app/components/oip-science/euct/Publications.vue +89 -0
- package/layer/app/components/oip-science/euct/Sponsors.vue +86 -0
- package/layer/app/components/oip-science/euct/StudyDescription.vue +1075 -0
- package/layer/app/components/oip-science/euct-centers/ClinicalTrials.vue +40 -0
- package/layer/app/components/oip-science/euct-centers/Publications.vue +55 -0
- package/layer/app/components/oip-science/euct-investigators/ClinicalTrials.vue +41 -0
- package/layer/app/components/oip-science/euct-investigators/Informations.vue +171 -0
- package/layer/app/components/oip-science/euct-investigators/Publications.vue +51 -0
- package/layer/app/components/oip-science/hcps/AppelsAProjet.vue +62 -0
- package/layer/app/components/oip-science/hcps/Cards.vue +237 -0
- package/layer/app/components/oip-science/hcps/ClinicalTrials.vue +78 -0
- package/layer/app/components/oip-science/hcps/Informations.vue +296 -0
- package/layer/app/components/oip-science/hcps/LinkedList.vue +153 -0
- package/layer/app/components/oip-science/hcps/MatchedLabels.vue +207 -0
- package/layer/app/components/oip-science/hcps/Pubmed.vue +128 -0
- package/layer/app/components/oip-science/local/SliderTabs.vue +99 -0
- package/layer/app/components/oip-science/local/SliderTabsFull.vue +135 -0
- package/layer/app/components/oip-science/pubmed/Authors.vue +128 -0
- package/layer/app/components/oip-science/pubmed/Cards.vue +417 -0
- package/layer/app/components/oip-science/pubmed/ClinicalTrials.vue +71 -0
- package/layer/app/components/oip-science/resources/Phrc.vue +39 -0
- package/layer/app/composables/useOipChapterScroll.ts +43 -0
- package/layer/app/composables/useOipResourceHooks.ts +27 -0
- package/layer/app/composables/useOipResourceNav.ts +66 -0
- package/layer/app/composables/useOipResourceViews.ts +117 -0
- package/layer/app/composables/useOipResources.ts +83 -0
- package/layer/app/oip-drugs/PORTING.md +120 -0
- package/layer/app/oip-drugs/composables/useAssociationDetail.ts +220 -0
- package/layer/app/oip-drugs/composables/useAssociationExport.ts +63 -0
- package/layer/app/oip-drugs/composables/useAssociationLookup.ts +105 -0
- package/layer/app/oip-drugs/composables/useCardPrefOptions.ts +201 -0
- package/layer/app/oip-drugs/composables/useCurrency.ts +121 -0
- package/layer/app/oip-drugs/composables/useDirectusSources.ts +224 -0
- package/layer/app/oip-drugs/composables/useHeartBrandMatches.ts +112 -0
- package/layer/app/oip-drugs/composables/useHeartDetailSearches.ts +479 -0
- package/layer/app/oip-drugs/composables/useHeartIntlApprovals.ts +661 -0
- package/layer/app/oip-drugs/composables/useHeartLesIndication.ts +168 -0
- package/layer/app/oip-drugs/composables/useHeartMatchExplain.ts +231 -0
- package/layer/app/oip-drugs/composables/useHeartReports.ts +239 -0
- package/layer/app/oip-drugs/composables/useHeartSearchFields.ts +233 -0
- package/layer/app/oip-drugs/composables/useMergeByKey.ts +23 -0
- package/layer/app/oip-drugs/composables/useOipDrugsElastic.ts +621 -0
- package/layer/app/oip-drugs/composables/usePageEmpty.ts +58 -0
- package/layer/app/oip-drugs/composables/v-useSalesSource.ts +64 -0
- package/layer/app/oip-drugs/drugs.css +500 -0
- package/layer/app/oip-drugs/esIndices.ts +322 -0
- package/layer/app/oip-drugs/kinds.ts +264 -0
- package/layer/app/oip-drugs/locales/en.json +1571 -0
- package/layer/app/oip-drugs/locales/fr.json +1571 -0
- package/layer/app/oip-drugs/monographs/blincyto-has.vue +289 -0
- package/layer/app/oip-drugs/monographs/darzalex-has.vue +276 -0
- package/layer/app/oip-drugs/monographs/dupixent-has.vue +216 -0
- package/layer/app/oip-drugs/monographs/enhertu-has.vue +280 -0
- package/layer/app/oip-drugs/monographs/has-sein-2023.vue +1649 -0
- package/layer/app/oip-drugs/monographs/ibrance-has.vue +277 -0
- package/layer/app/oip-drugs/monographs/imfinzi-has.vue +293 -0
- package/layer/app/oip-drugs/monographs/keytruda-has.vue +334 -0
- package/layer/app/oip-drugs/monographs/kisqali-has.vue +201 -0
- package/layer/app/oip-drugs/monographs/opdivo-has.vue +231 -0
- package/layer/app/oip-drugs/monographs/ozempic-has.vue +186 -0
- package/layer/app/oip-drugs/monographs/tecentriq-has.vue +312 -0
- package/layer/app/oip-drugs/monographs/yescarta-has.vue +272 -0
- package/layer/app/oip-drugs/types/elastic.ts +113 -0
- package/layer/app/oip-drugs/types/es/agencies.ts +152 -0
- package/layer/app/oip-drugs/types/es/directory.ts +54 -0
- package/layer/app/oip-drugs/types/es/docByIndex.ts +151 -0
- package/layer/app/oip-drugs/types/es/openmedic.ts +67 -0
- package/layer/app/oip-drugs/types/es/outpatient.ts +71 -0
- package/layer/app/oip-drugs/types/es/portal.ts +130 -0
- package/layer/app/oip-drugs/types/es/retail.ts +107 -0
- package/layer/app/oip-drugs/types/es/statistics.ts +29 -0
- package/layer/app/oip-drugs/types/heart/CurrencyRate.ts +16 -0
- package/layer/app/oip-drugs/types/heart/ansm.ts +106 -0
- package/layer/app/oip-drugs/types/heart/tag.ts +29 -0
- package/layer/app/oip-drugs/utils/associationFields.ts +118 -0
- package/layer/app/oip-drugs/utils/contributions.ts +36 -0
- package/layer/app/oip-drugs/utils/countryMapping.ts +387 -0
- package/layer/app/oip-drugs/utils/esHighlight.ts +21 -0
- package/layer/app/oip-drugs/utils/exportSheets.ts +57 -0
- package/layer/app/oip-drugs/utils/heartIntlPrice.ts +228 -0
- package/layer/app/oip-drugs/utils/heartStyleAndFormat.ts +262 -0
- package/layer/app/oip-drugs/utils/heartStyleAnsm.ts +71 -0
- package/layer/app/oip-drugs/utils/latestDate.ts +31 -0
- package/layer/app/oip-drugs/utils/p-heartCardTags.ts +53 -0
- package/layer/app/oip-drugs/utils/priceExport.ts +269 -0
- package/layer/app/oip-drugs/utils/q-hasRatings.ts +43 -0
- package/layer/app/oip-drugs/utils/s-openCard.ts +20 -0
- package/layer/app/oip-drugs/utils/sourceUuids.ts +155 -0
- package/layer/app/oip-drugs/utils/styleAndFormat.ts +40 -0
- package/layer/app/oip-drugs/utils/v-salesSeries.ts +156 -0
- package/layer/app/oip-drugs/utils/v-scopeTags.ts +23 -0
- package/layer/app/oip-science/PORTING.md +93 -0
- package/layer/app/oip-science/assets/smartSelection.json +428 -0
- package/layer/app/oip-science/composables/useAuthorMatching.ts +342 -0
- package/layer/app/oip-science/composables/useCTCenter.ts +107 -0
- package/layer/app/oip-science/composables/useCTInvestigators.ts +107 -0
- package/layer/app/oip-science/composables/useEstablishmentCrossData.ts +139 -0
- package/layer/app/oip-science/composables/useEstablishmentMatching.ts +176 -0
- package/layer/app/oip-science/composables/useEuct.ts +418 -0
- package/layer/app/oip-science/composables/useEuctCenter.ts +107 -0
- package/layer/app/oip-science/composables/useEuctInvestigators.ts +107 -0
- package/layer/app/oip-science/composables/useGlobalClinicalTrials.ts +624 -0
- package/layer/app/oip-science/composables/useLinkedTrials.ts +83 -0
- package/layer/app/oip-science/composables/useOipElastic.ts +658 -0
- package/layer/app/oip-science/composables/useOipSearchExplain.ts +166 -0
- package/layer/app/oip-science/composables/usePubmed.ts +487 -0
- package/layer/app/oip-science/composables/useSearchFields.ts +153 -0
- package/layer/app/oip-science/indexConfig.json +794 -0
- package/layer/app/oip-science/locales/en.json +483 -0
- package/layer/app/oip-science/locales/fr.json +483 -0
- package/layer/app/oip-science/science.css +1182 -0
- package/layer/app/oip-science/utils/a-openCard.ts +23 -0
- package/layer/app/oip-science/utils/b-openCard.ts +35 -0
- package/layer/app/oip-science/utils/c-filterContext.ts +81 -0
- package/layer/app/oip-science/utils/f-types.ts +241 -0
- package/layer/app/oip-science/utils/frDiseaseTerms.ts +269 -0
- package/layer/app/oip-science/utils/matchMethod.ts +53 -0
- package/layer/app/oip-science/utils/sliderTabs.ts +13 -0
- package/layer/app/oip-science/utils/sources.ts +15 -0
- package/layer/app/oip-science/utils/styleAndFormat.ts +608 -0
- package/layer/app/oip-science/utils/toPresets.ts +7 -0
- package/layer/app/oip-science/utils/valueTable.ts +126 -0
- package/layer/app/plugins/oip-science-i18n.ts +17 -0
- package/layer/app/utils/oipFicheRoutes.ts +118 -0
- package/layer/app/utils/oipResources.ts +440 -0
- package/layer/nuxt.config.ts +22 -2
- package/layer/public/reports/blincyto-has.html +292 -0
- package/layer/public/reports/blincyto-has.pdf +0 -0
- package/layer/public/reports/darzalex-has.html +279 -0
- package/layer/public/reports/darzalex-has.pdf +0 -0
- package/layer/public/reports/dupixent-has.html +217 -0
- package/layer/public/reports/dupixent-has.pdf +0 -0
- package/layer/public/reports/enhertu-has.html +283 -0
- package/layer/public/reports/enhertu-has.pdf +0 -0
- package/layer/public/reports/has-sein-2023.html +1639 -0
- package/layer/public/reports/ibrance-has.html +280 -0
- package/layer/public/reports/ibrance-has.pdf +0 -0
- package/layer/public/reports/imfinzi-has.html +297 -0
- package/layer/public/reports/imfinzi-has.pdf +0 -0
- package/layer/public/reports/keytruda-has.html +337 -0
- package/layer/public/reports/keytruda-has.pdf +0 -0
- package/layer/public/reports/kisqali-has.html +202 -0
- package/layer/public/reports/kisqali-has.pdf +0 -0
- package/layer/public/reports/opdivo-has.html +232 -0
- package/layer/public/reports/opdivo-has.pdf +0 -0
- package/layer/public/reports/ozempic-has.html +188 -0
- package/layer/public/reports/ozempic-has.pdf +0 -0
- package/layer/public/reports/tecentriq-has.html +315 -0
- package/layer/public/reports/tecentriq-has.pdf +0 -0
- package/layer/public/reports/yescarta-has.html +275 -0
- package/layer/public/reports/yescarta-has.pdf +0 -0
- package/layer/server/api/oip/es.post.ts +99 -0
- package/layer/server/api/oip/pdf.get.ts +70 -0
- package/layer/server/api/oip/reports.get.ts +98 -0
- package/layer/server/api/oip/resource-types.get.ts +22 -0
- package/package.json +1 -1
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Une marque du suivi des prix, ses presentations dans un tableau.
|
|
3
|
+
|
|
4
|
+
L'en-tete donne la marque, son laboratoire, le nombre de presentations et la
|
|
5
|
+
date du dernier mouvement de prix ; le tableau qui suit detaille chaque
|
|
6
|
+
presentation (son code CIP, son PFHT, l'evolution depuis le tarif precedent,
|
|
7
|
+
la date de prise d'effet et le taux de remboursement). Le prix public s'en
|
|
8
|
+
deduit (marges de distribution et TVA) : il vit dans la fiche produit.
|
|
9
|
+
-->
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
import type { EsIndexName } from "../../../../oip-drugs/esIndices";
|
|
12
|
+
import { ES_INDEX } from "../../../../oip-drugs/esIndices";
|
|
13
|
+
import { HEART_SOURCE_UUIDS } from "../../../../oip-drugs/utils/sourceUuids";
|
|
14
|
+
import { drugsBrandPath } from "../../../../oip-drugs/kinds";
|
|
15
|
+
import { valueTable } from "../../../../utils/valueTable";
|
|
16
|
+
|
|
17
|
+
const props = withDefaults(
|
|
18
|
+
defineProps<{
|
|
19
|
+
/** Un groupe renvoye par `useHeartGroupedPrice`. */
|
|
20
|
+
group: any;
|
|
21
|
+
/** Termes de la recherche, surlignes dans le nom de marque. */
|
|
22
|
+
searchQueries?: any[];
|
|
23
|
+
/** Taux de change du jour, passes a l'export de la marque. */
|
|
24
|
+
rates?: any[] | null;
|
|
25
|
+
indexQuery?: EsIndexName;
|
|
26
|
+
/** Rendu serre de la fiche projet : l'en-tete seul, sans le tableau. */
|
|
27
|
+
dense?: boolean;
|
|
28
|
+
}>(),
|
|
29
|
+
{ searchQueries: () => [], rates: null, indexQuery: ES_INDEX.DRUG_CARDS, dense: false },
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const { locale } = useI18n();
|
|
33
|
+
const nav = useOipResourceNav();
|
|
34
|
+
const { projectButton } = useOipResourceHooks();
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Tarif courant d'une presentation, et celui qui le precede.
|
|
38
|
+
*
|
|
39
|
+
* `reimbursement_history` n'est pas trie : prendre la premiere entree ferait
|
|
40
|
+
* passer un vieux tarif pour le tarif en vigueur.
|
|
41
|
+
*
|
|
42
|
+
* Il empile aussi un tarif par circuit de distribution (retrocession, T2A,
|
|
43
|
+
* ville) a la meme date : comparer la premiere entree a la deuxieme comparait
|
|
44
|
+
* deux circuits entre eux et annoncait 0 % la ou le prix avait bouge. Le tarif
|
|
45
|
+
* precedent est donc celui du meme circuit, a une date anterieure, sauf quand
|
|
46
|
+
* le tarif courant arrive sans circuit, voir `isKnownChannel`.
|
|
47
|
+
*/
|
|
48
|
+
function tariffs(product: any) {
|
|
49
|
+
const history = Array.isArray(product?.reimbursement_history)
|
|
50
|
+
? [...product.reimbursement_history].sort(
|
|
51
|
+
(a: any, b: any) =>
|
|
52
|
+
new Date(b.application_date).getTime() - new Date(a.application_date).getTime(),
|
|
53
|
+
)
|
|
54
|
+
: [];
|
|
55
|
+
const current = history[0] ?? {};
|
|
56
|
+
const sameChannel = isKnownChannel(current.distribution_type)
|
|
57
|
+
? history.filter((entry: any) => entry.distribution_type === current.distribution_type)
|
|
58
|
+
: history;
|
|
59
|
+
const previous =
|
|
60
|
+
sameChannel.find(
|
|
61
|
+
(entry: any) =>
|
|
62
|
+
entry.application_date !== current.application_date && entry.factory_price != null,
|
|
63
|
+
) ?? null;
|
|
64
|
+
return { current, previous };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Un circuit de distribution nomme. La source livre ses derniers tarifs sans
|
|
69
|
+
* circuit (« Non renseigne ») alors que l'historique en porte un (TAA,
|
|
70
|
+
* Retrocession) : exiger le meme circuit ne trouvait alors aucun tarif
|
|
71
|
+
* precedent et toute la colonne Evolution tombait a NA. Faute de circuit
|
|
72
|
+
* connu, on compare au tarif precedent quel qu'il soit.
|
|
73
|
+
*/
|
|
74
|
+
const UNKNOWN_CHANNELS = new Set(["", "nan", "na", "non renseigne", "unknown"]);
|
|
75
|
+
|
|
76
|
+
function isKnownChannel(value: any) {
|
|
77
|
+
if (!value) return false;
|
|
78
|
+
const normalized = String(value)
|
|
79
|
+
.trim()
|
|
80
|
+
.toLowerCase()
|
|
81
|
+
.normalize("NFD")
|
|
82
|
+
.replace(/[\u0300-\u036f]/g, "");
|
|
83
|
+
return !UNKNOWN_CHANNELS.has(normalized);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Variation en pourcentage d'un prix par rapport au precedent. */
|
|
87
|
+
function variation(now: number | null | undefined, before: number | null | undefined) {
|
|
88
|
+
if (now === null || now === undefined || !before) return null;
|
|
89
|
+
return ((Number(now) - Number(before)) / Number(before)) * 100;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function money(value: number | null | undefined) {
|
|
93
|
+
if (value === null || value === undefined) return "NA";
|
|
94
|
+
return new Intl.NumberFormat(locale.value === "en" ? "en-US" : "fr-FR", {
|
|
95
|
+
style: "currency",
|
|
96
|
+
currency: "EUR",
|
|
97
|
+
maximumFractionDigits: 2,
|
|
98
|
+
}).format(Number(value));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function shortDate(raw: string | null | undefined) {
|
|
102
|
+
if (!raw) return "NA";
|
|
103
|
+
const date = new Date(raw);
|
|
104
|
+
if (Number.isNaN(date.getTime())) return "NA";
|
|
105
|
+
return date.toLocaleDateString(locale.value === "en" ? "en-GB" : "fr-FR", {
|
|
106
|
+
day: "numeric",
|
|
107
|
+
month: "short",
|
|
108
|
+
year: "numeric",
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const heads = computed(() =>
|
|
113
|
+
locale.value === "en"
|
|
114
|
+
? ["Presentation", "Ex-factory", "Change", "Effective date", "Reimb."]
|
|
115
|
+
: ["Présentation", "PFHT", "Évolution", "Prise d'effet", "Rembours."],
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const rows = computed(() =>
|
|
119
|
+
(props.group?.products ?? []).map((product: any) => {
|
|
120
|
+
const { current, previous } = tariffs(product);
|
|
121
|
+
// Un prix inchange depuis toujours n'a pas d'evolution a montrer, mais il
|
|
122
|
+
// garde sa date de prise d'effet.
|
|
123
|
+
const change = variation(current?.factory_price, previous?.factory_price);
|
|
124
|
+
return {
|
|
125
|
+
values: [
|
|
126
|
+
{
|
|
127
|
+
cip: product.local_drug_id ?? null,
|
|
128
|
+
label: product.presentation_label || product.label || "NA",
|
|
129
|
+
},
|
|
130
|
+
money(current?.factory_price ?? null),
|
|
131
|
+
{ change },
|
|
132
|
+
shortDate(current?.application_date),
|
|
133
|
+
current.reimbursement_rate != null
|
|
134
|
+
? `${Math.round(current.reimbursement_rate * 100)} %`
|
|
135
|
+
: "NA",
|
|
136
|
+
],
|
|
137
|
+
};
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Une variation se lit avec son sens : la fleche le porte, la couleur ne fait
|
|
143
|
+
* que la redoubler. Ni rouge ni vert — une hausse de prix n'est bonne ni
|
|
144
|
+
* mauvaise en soi, ca depend de qui lit.
|
|
145
|
+
*/
|
|
146
|
+
function formatChange(change: number) {
|
|
147
|
+
const rounded = Math.abs(change) >= 10 ? change.toFixed(0) : change.toFixed(1);
|
|
148
|
+
const arrow = change > 0 ? "↑" : change < 0 ? "↓" : "=";
|
|
149
|
+
return `${arrow} ${change > 0 ? "+" : ""}${rounded} %`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Fiche ouverte au clic sur la carte : la fiche produit du hub, sur son onglet
|
|
154
|
+
* Prix. La carte porte une marque, la fiche aussi (l'ancienne page de detail
|
|
155
|
+
* tarifaire, elle, s'adressait a une seule presentation).
|
|
156
|
+
*/
|
|
157
|
+
const detailsPath = computed(() => {
|
|
158
|
+
const path = drugsBrandPath(props.group?.brand);
|
|
159
|
+
return path ? `${path}?tab=price` : null;
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Toute la carte mene a la fiche, sauf ce qui a sa propre action : la
|
|
164
|
+
* pagination du tableau, un lien, un bouton.
|
|
165
|
+
*/
|
|
166
|
+
function openDetails(event: MouseEvent) {
|
|
167
|
+
const target = event.target as HTMLElement | null;
|
|
168
|
+
if (target?.closest("a, button, .rcd-pagination, .rcd-pagination-navigate")) return;
|
|
169
|
+
if (!detailsPath.value) return;
|
|
170
|
+
if (event.metaKey || event.ctrlKey || event.shiftKey) {
|
|
171
|
+
window.open(nav.appHref("fr_drugs_app", detailsPath.value), "_blank", "noopener");
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
nav.openApp("fr_drugs_app", detailsPath.value);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const countLabel = computed(() => {
|
|
178
|
+
const count = props.group?.count ?? 0;
|
|
179
|
+
if (locale.value === "en") return `${count} presentation${count > 1 ? "s" : ""}`;
|
|
180
|
+
return `${count} présentation${count > 1 ? "s" : ""}`;
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Le tableau des presentations etait ecrit pour `arg-table` : des libelles de
|
|
184
|
+
// tete et des lignes `{ values: [...] }`. La conversion en colonnes de RmTable
|
|
185
|
+
// est mecanique, cf. `valueTable`. Il ne se trie pas : la carte montre les
|
|
186
|
+
// presentations les plus recemment mises a jour, dans cet ordre.
|
|
187
|
+
const presTable = computed(() => {
|
|
188
|
+
const converted = valueTable(heads.value, rows.value, [
|
|
189
|
+
"presentation",
|
|
190
|
+
"",
|
|
191
|
+
"change",
|
|
192
|
+
"date",
|
|
193
|
+
"",
|
|
194
|
+
]);
|
|
195
|
+
return {
|
|
196
|
+
...converted,
|
|
197
|
+
columns: converted.columns.map((column) => ({ ...column, sortable: false })),
|
|
198
|
+
};
|
|
199
|
+
});
|
|
200
|
+
</script>
|
|
201
|
+
|
|
202
|
+
<template>
|
|
203
|
+
<!-- `rm-card` n'est pas un style : c'est ce par quoi le menu d'actions
|
|
204
|
+
(la roue crantee) reconnait la carte qui le porte. -->
|
|
205
|
+
<article class="oip-drugs bg_card rm-card" @click="openDetails">
|
|
206
|
+
<header class="bg_head">
|
|
207
|
+
<span class="bg_identity">
|
|
208
|
+
<span class="bg_brand">
|
|
209
|
+
<OipDrugsHeartTextHighlight
|
|
210
|
+
:text="group.brand"
|
|
211
|
+
:queries="searchQueries ?? []"
|
|
212
|
+
/>
|
|
213
|
+
</span>
|
|
214
|
+
<span class="bg_meta">
|
|
215
|
+
<template v-if="group.molecules?.length">{{ group.molecules.join(" · ") }}</template>
|
|
216
|
+
<template v-if="group.manufacturers?.length">
|
|
217
|
+
· {{ group.manufacturers.join(" · ") }}
|
|
218
|
+
</template>
|
|
219
|
+
</span>
|
|
220
|
+
</span>
|
|
221
|
+
|
|
222
|
+
<span class="bg_facts">
|
|
223
|
+
<span class="bg_count">{{ countLabel }}</span>
|
|
224
|
+
<span class="bg_date">
|
|
225
|
+
<template v-if="group.last_price_update">
|
|
226
|
+
{{ locale === "en" ? "Latest change" : "Dernier changement" }} :
|
|
227
|
+
{{ shortDate(group.last_price_update) }}
|
|
228
|
+
</template>
|
|
229
|
+
<template v-else>
|
|
230
|
+
{{ locale === "en" ? "No price change" : "Aucun changement de prix" }}
|
|
231
|
+
</template>
|
|
232
|
+
</span>
|
|
233
|
+
</span>
|
|
234
|
+
|
|
235
|
+
<!-- Le tableau ci-dessous ne montre que les 25 presentations les plus
|
|
236
|
+
recentes, sans leur historique : l'export, lui, porte la marque
|
|
237
|
+
entiere. Il se demande par la roue crantee, au meme endroit que sur
|
|
238
|
+
les cartes d'avis HAS. -->
|
|
239
|
+
<!-- Le projet range la marque entiere : toutes ses presentations, pas
|
|
240
|
+
seulement celles du tableau (cf. `ids`, useHeartGroupedPrice). -->
|
|
241
|
+
<!-- Emplacement libre pour un appelant (la fiche projet y pose son
|
|
242
|
+
bouton « Commenter ») avant le bouton projet : vide partout ailleurs. -->
|
|
243
|
+
<slot name="hubr-actions" />
|
|
244
|
+
<component
|
|
245
|
+
:is="projectButton"
|
|
246
|
+
v-if="projectButton"
|
|
247
|
+
:resource-id="group.ids ?? (group.products ?? []).map((product: any) => product.id)"
|
|
248
|
+
type="FrDrugPrice"
|
|
249
|
+
:label="group.brand"
|
|
250
|
+
compact
|
|
251
|
+
/>
|
|
252
|
+
</header>
|
|
253
|
+
|
|
254
|
+
<div v-if="!dense" class="bg_body">
|
|
255
|
+
<RmTable
|
|
256
|
+
flat
|
|
257
|
+
:columns="presTable.columns"
|
|
258
|
+
:rows="presTable.rows"
|
|
259
|
+
:page-size="5"
|
|
260
|
+
>
|
|
261
|
+
<!-- Le CIP identifie la presentation : il la coiffe plutot que de
|
|
262
|
+
prendre une colonne pour lui seul. -->
|
|
263
|
+
<template v-for="key in presTable.typeKeys.presentation" :key="key" #[key]="{ value }">
|
|
264
|
+
<span class="pres">
|
|
265
|
+
<RmTag v-if="value.cip" variant="gray">{{ value.cip }}</RmTag>
|
|
266
|
+
<span class="pres_label">{{ value.label }}</span>
|
|
267
|
+
</span>
|
|
268
|
+
</template>
|
|
269
|
+
|
|
270
|
+
<template v-for="key in presTable.typeKeys.change" :key="key" #[key]="{ value }">
|
|
271
|
+
<span
|
|
272
|
+
v-if="value.change !== null"
|
|
273
|
+
class="pc_change"
|
|
274
|
+
:class="value.change > 0 ? 'is_up' : value.change < 0 ? 'is_down' : 'is_flat'"
|
|
275
|
+
>
|
|
276
|
+
{{ formatChange(value.change) }}
|
|
277
|
+
</span>
|
|
278
|
+
<!-- Pas d'evolution : un seul tarif connu, rien a comparer. -->
|
|
279
|
+
<span v-else class="pc_none">NA</span>
|
|
280
|
+
</template>
|
|
281
|
+
|
|
282
|
+
<!-- « 15 mars 2024 » tient sur une ligne : coupe en deux, la date se
|
|
283
|
+
lit comme deux valeurs. -->
|
|
284
|
+
<template v-for="key in presTable.typeKeys.date" :key="key" #[key]="{ value }">
|
|
285
|
+
<span class="pc_date">{{ value }}</span>
|
|
286
|
+
</template>
|
|
287
|
+
</RmTable>
|
|
288
|
+
|
|
289
|
+
<p v-if="group.count > group.products.length" class="bg_note">
|
|
290
|
+
{{
|
|
291
|
+
locale === "en"
|
|
292
|
+
? `Showing the ${group.products.length} most recently updated presentations out of ${group.count}.`
|
|
293
|
+
: `Les ${group.products.length} présentations les plus récemment mises à jour, sur ${group.count}.`
|
|
294
|
+
}}
|
|
295
|
+
</p>
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
<!-- Meme pied que les cartes d'avis HAS : la source, et la date du dernier
|
|
299
|
+
tarif publie pour la marque. -->
|
|
300
|
+
<div v-if="!dense" class="bg_footer">
|
|
301
|
+
<OipDrugsHeartElasticSourceContainerElastic
|
|
302
|
+
:uuid="HEART_SOURCE_UUIDS.PRICE_TRACKER"
|
|
303
|
+
:last-label="$t('oipDrugs.heart.sources.lastPrice')"
|
|
304
|
+
:last-date="group?.last_price_update"
|
|
305
|
+
/>
|
|
306
|
+
</div>
|
|
307
|
+
</article>
|
|
308
|
+
</template>
|
|
309
|
+
|
|
310
|
+
<style scoped>
|
|
311
|
+
.bg_card {
|
|
312
|
+
box-sizing: border-box;
|
|
313
|
+
width: 100%;
|
|
314
|
+
border: 1px solid var(--border-default);
|
|
315
|
+
border-radius: 12px;
|
|
316
|
+
background: var(--bg-surface);
|
|
317
|
+
overflow: hidden;
|
|
318
|
+
cursor: pointer;
|
|
319
|
+
transition: border-color 0.15s linear, box-shadow 0.15s linear;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.bg_card:hover {
|
|
323
|
+
border-color: color-mix(in srgb, var(--color-primary) 35%, var(--border-default));
|
|
324
|
+
box-shadow: var(--shadow-md);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/* `border-box` obligatoire : le reset de l'app remet `box-sizing` a
|
|
328
|
+
`content-box`, et `width: 100%` + 32px de gouttiere poussait la colonne de
|
|
329
|
+
droite (nombre de presentations, date) hors de la carte, ou le
|
|
330
|
+
`overflow: hidden` la coupait. */
|
|
331
|
+
.bg_head {
|
|
332
|
+
display: flex;
|
|
333
|
+
align-items: center;
|
|
334
|
+
gap: 12px;
|
|
335
|
+
box-sizing: border-box;
|
|
336
|
+
width: 100%;
|
|
337
|
+
padding: 14px 16px;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.bg_identity {
|
|
341
|
+
display: flex;
|
|
342
|
+
flex-direction: column;
|
|
343
|
+
gap: 2px;
|
|
344
|
+
min-width: 0;
|
|
345
|
+
flex: 1 1 auto;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.bg_brand {
|
|
349
|
+
color: var(--text-primary);
|
|
350
|
+
font-size: 16px;
|
|
351
|
+
font-weight: 800;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.bg_meta {
|
|
355
|
+
color: var(--text-muted);
|
|
356
|
+
font-size: 13px;
|
|
357
|
+
overflow: hidden;
|
|
358
|
+
text-overflow: ellipsis;
|
|
359
|
+
white-space: nowrap;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.bg_facts {
|
|
363
|
+
display: flex;
|
|
364
|
+
flex: 0 0 auto;
|
|
365
|
+
flex-direction: column;
|
|
366
|
+
align-items: flex-end;
|
|
367
|
+
gap: 2px;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.bg_count {
|
|
371
|
+
padding: 2px 10px;
|
|
372
|
+
border-radius: 999px;
|
|
373
|
+
background: color-mix(in srgb, var(--color-primary) 8%, transparent);
|
|
374
|
+
color: var(--color-primary);
|
|
375
|
+
font-size: 12px;
|
|
376
|
+
font-weight: 700;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.bg_date {
|
|
380
|
+
color: var(--text-muted);
|
|
381
|
+
font-size: 12px;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/* Le tableau va d'un bord a l'autre : ses lignes zebrees s'arretaient a
|
|
385
|
+
16px des cotes, ce qui dessinait une seconde carte a l'interieur de la
|
|
386
|
+
carte. Le retrait revient sur ce qui n'est pas le tableau, et `flat` ote
|
|
387
|
+
le cadre que RmTable pose de lui-meme. */
|
|
388
|
+
.bg_body {
|
|
389
|
+
display: flex;
|
|
390
|
+
flex-direction: column;
|
|
391
|
+
gap: 8px;
|
|
392
|
+
box-sizing: border-box;
|
|
393
|
+
width: 100%;
|
|
394
|
+
padding: 0 0 16px;
|
|
395
|
+
border-top: 1px solid var(--border-default);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.pres {
|
|
399
|
+
display: flex;
|
|
400
|
+
flex-direction: column;
|
|
401
|
+
align-items: flex-start;
|
|
402
|
+
gap: 4px;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.pres_label {
|
|
406
|
+
font-weight: 600;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.pc_change {
|
|
410
|
+
font-size: 12px;
|
|
411
|
+
font-weight: 700;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/* Bleu pour la hausse, violet pour la baisse : deux teintes froides, aucune
|
|
415
|
+
ne dit si le mouvement est une bonne nouvelle. */
|
|
416
|
+
.pc_change.is_up {
|
|
417
|
+
color: var(--color-info);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.pc_change.is_down {
|
|
421
|
+
color: var(--color-secondary);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.pc_change.is_flat {
|
|
425
|
+
color: var(--text-muted);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.pc_none {
|
|
429
|
+
color: var(--text-muted);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.pc_date {
|
|
433
|
+
white-space: nowrap;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.bg_note {
|
|
437
|
+
padding: 0 16px;
|
|
438
|
+
color: var(--text-muted);
|
|
439
|
+
font-size: 12px;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.bg_footer {
|
|
443
|
+
box-sizing: border-box;
|
|
444
|
+
width: 100%;
|
|
445
|
+
padding: 12px 16px;
|
|
446
|
+
border-top: 1px solid var(--border-default);
|
|
447
|
+
background: var(--bg-surface-2);
|
|
448
|
+
font-size: 13px;
|
|
449
|
+
}
|
|
450
|
+
</style>
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Carte d'un medicament d'interet therapeutique majeur (MITM).
|
|
3
|
+
|
|
4
|
+
Meme modele que les cartes de marque et LES (RmResultCard) : un produit
|
|
5
|
+
(CIS), ses presentations, son dernier SMR/ASMR. Le detail vit dans la fiche,
|
|
6
|
+
ouverte au clic sur la carte entiere.
|
|
7
|
+
-->
|
|
8
|
+
<template>
|
|
9
|
+
<RmResultCard
|
|
10
|
+
class="oip-drugs"
|
|
11
|
+
:clickable="!inDetails"
|
|
12
|
+
:count="presentationCount"
|
|
13
|
+
:count-label="countLabel"
|
|
14
|
+
:more-label="inDetails ? '' : locale === 'fr' ? 'Voir le détail' : 'See details'"
|
|
15
|
+
:lang="dateLang"
|
|
16
|
+
:link="detailsHref"
|
|
17
|
+
@click="!inDetails && openDrugsCard(nav, $event, detailsPath)"
|
|
18
|
+
>
|
|
19
|
+
<template #title>
|
|
20
|
+
<OipDrugsHeartTextHighlight :text="result.brand || result.cis_label" :queries="highlight" />
|
|
21
|
+
</template>
|
|
22
|
+
<template v-if="subtitle" #subtitle>
|
|
23
|
+
<OipDrugsHeartTextHighlight :text="subtitle" :queries="highlight" />
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<template #actions>
|
|
27
|
+
<!-- Emplacement libre pour un appelant (la fiche projet y pose son
|
|
28
|
+
bouton « Commenter ») avant le bouton projet : vide partout ailleurs. -->
|
|
29
|
+
<slot name="hubr-actions" />
|
|
30
|
+
<component
|
|
31
|
+
:is="projectButton"
|
|
32
|
+
v-if="projectButton"
|
|
33
|
+
:resource-id="result._id"
|
|
34
|
+
type="FrMitm"
|
|
35
|
+
:label="result.brand || result.cis_label"
|
|
36
|
+
compact
|
|
37
|
+
/>
|
|
38
|
+
</template>
|
|
39
|
+
|
|
40
|
+
<template #meta>
|
|
41
|
+
<span v-if="lastSmr" class="rm-rcard__label">
|
|
42
|
+
{{ locale === "fr" ? "Dernier SMR" : "Last SMR" }} ·
|
|
43
|
+
<RmDate :date="formatHasDate(lastSmr.has_date)" :lang="dateLang" />
|
|
44
|
+
</span>
|
|
45
|
+
<span class="rm-rcard__ref">CIS {{ result.cis_code }}</span>
|
|
46
|
+
<RmTag variant="violet">
|
|
47
|
+
{{ locale === "fr" ? "Sur la liste ANSM" : "On the ANSM list" }}
|
|
48
|
+
</RmTag>
|
|
49
|
+
</template>
|
|
50
|
+
|
|
51
|
+
<p class="rm-rcard__lead" :class="{ 'rm-rcard__clamp': !inDetails }">
|
|
52
|
+
<OipDrugsHeartTextHighlight :text="result.cis_label" :queries="highlight" />
|
|
53
|
+
</p>
|
|
54
|
+
|
|
55
|
+
<p v-if="lastSmr || lastAsmr" class="rm-rcard__text">
|
|
56
|
+
<template v-if="lastSmr">SMR {{ lastSmr.value }}</template>
|
|
57
|
+
<template v-if="lastSmr && lastAsmr"> · </template>
|
|
58
|
+
<template v-if="lastAsmr">ASMR {{ lastAsmr.value }}</template>
|
|
59
|
+
</p>
|
|
60
|
+
|
|
61
|
+
<!-- Rangees choisies dans « Personnaliser ». -->
|
|
62
|
+
<OipDrugsHeartResultsPrefRows scope="mitm" :values="prefValues" />
|
|
63
|
+
|
|
64
|
+
<!-- Ce que le moteur a reellement matche sur cette ligne. -->
|
|
65
|
+
<OipDrugsHeartResultsMatchExplain :hit="{ highlight: result.highlight }" />
|
|
66
|
+
|
|
67
|
+
<template #footer>
|
|
68
|
+
<OipDrugsHeartElasticSourceContainerElastic
|
|
69
|
+
:uuid="HEART_SOURCE_UUIDS.MITM"
|
|
70
|
+
:last-label="$t('oipDrugs.heart.sources.lastOpinion')"
|
|
71
|
+
:last-date="lastOpinionDate"
|
|
72
|
+
/>
|
|
73
|
+
</template>
|
|
74
|
+
</RmResultCard>
|
|
75
|
+
</template>
|
|
76
|
+
|
|
77
|
+
<script setup lang="ts">
|
|
78
|
+
import type { PropType } from "vue";
|
|
79
|
+
import { HEART_SOURCE_UUIDS } from "../../../../oip-drugs/utils/sourceUuids";
|
|
80
|
+
import { drugsBrandPath } from "../../../../oip-drugs/kinds";
|
|
81
|
+
import { openDrugsCard } from "../../../../oip-drugs/utils/s-openCard";
|
|
82
|
+
|
|
83
|
+
const props = defineProps({
|
|
84
|
+
result: {
|
|
85
|
+
required: true,
|
|
86
|
+
type: Object,
|
|
87
|
+
},
|
|
88
|
+
inDetails: {
|
|
89
|
+
type: Boolean,
|
|
90
|
+
default: false,
|
|
91
|
+
},
|
|
92
|
+
/** Termes de la recherche, surlignes dans la carte (ex-filterStore). */
|
|
93
|
+
highlight: {
|
|
94
|
+
type: Array as PropType<string[]>,
|
|
95
|
+
default: () => [],
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const { locale } = useI18n();
|
|
100
|
+
const nav = useOipResourceNav();
|
|
101
|
+
const { projectButton } = useOipResourceHooks();
|
|
102
|
+
|
|
103
|
+
const dateLang = computed(() => (locale.value === "en" ? "en" : "fr"));
|
|
104
|
+
|
|
105
|
+
const detailsPath = computed(() => {
|
|
106
|
+
const path = drugsBrandPath(props.result.brand);
|
|
107
|
+
return path ? `${path}?tab=mitm&cis=${encodeURIComponent(props.result.cis_code ?? "")}` : "";
|
|
108
|
+
});
|
|
109
|
+
const detailsHref = computed(() => (detailsPath.value ? nav.appHref("fr_drugs_app", detailsPath.value) : undefined));
|
|
110
|
+
|
|
111
|
+
/** « MOLECULE (ATC5) · FABRICANT », comme le sous-titre de la carte de marque. */
|
|
112
|
+
const subtitle = computed(() =>
|
|
113
|
+
[
|
|
114
|
+
locale.value === "fr"
|
|
115
|
+
? props.result.atc5_french_code_label
|
|
116
|
+
: props.result.atc5_english_code_label,
|
|
117
|
+
props.result.owner_regroup,
|
|
118
|
+
]
|
|
119
|
+
.filter(Boolean)
|
|
120
|
+
.join(" · ")
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
/** Les presentations (CIP) rattachees au CIS. */
|
|
124
|
+
const presentationCount = computed(() =>
|
|
125
|
+
Array.isArray(props.result.price) ? props.result.price.length : 0
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const countLabel = computed(() => {
|
|
129
|
+
const n = presentationCount.value;
|
|
130
|
+
if (locale.value === "en") return n > 1 ? "presentations" : "presentation";
|
|
131
|
+
return n > 1 ? "présentations" : "présentation";
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
/** Premier SMR / ASMR renseigne : les tableaux portent souvent une ligne vide. */
|
|
135
|
+
const lastSmr = computed(
|
|
136
|
+
() => (props.result.last_smr ?? []).find((s: any) => s?.value) ?? null
|
|
137
|
+
);
|
|
138
|
+
const lastAsmr = computed(
|
|
139
|
+
() => (props.result.last_asmr ?? []).find((s: any) => s?.value) ?? null
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Ce que la carte peut montrer en plus, si « Personnaliser » le demande.
|
|
144
|
+
*
|
|
145
|
+
* `molecule` ne porte pas les molecules du produit mais les autres CIS de la
|
|
146
|
+
* meme molecule : la rangee annonce donc des marques, pas des principes actifs.
|
|
147
|
+
*/
|
|
148
|
+
const prefValues = computed(() => {
|
|
149
|
+
const en = locale.value === "en";
|
|
150
|
+
const smrEntries = (props.result.smr ?? []).filter((s: any) => s?.value);
|
|
151
|
+
const asmrEntries = (props.result.asmr ?? []).filter((s: any) => s?.value);
|
|
152
|
+
const prices = (props.result.price ?? []).filter((p: any) => p?.public_price);
|
|
153
|
+
const rates = [...new Set(prices.map((p: any) => p.reimbursement_rate).filter((r: any) => r != null))] as number[];
|
|
154
|
+
const amounts = prices.map((p: any) => p.public_price as number).sort((a: number, b: number) => a - b);
|
|
155
|
+
const money = (value: number) =>
|
|
156
|
+
value.toLocaleString(en ? "en-US" : "fr-FR", { style: "currency", currency: "EUR" });
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
hasReason: [
|
|
160
|
+
...new Set([props.result.last_smr?.[0]?.has_reason, props.result.last_asmr?.[0]?.has_reason].filter(Boolean)),
|
|
161
|
+
] as string[],
|
|
162
|
+
hasCode: [
|
|
163
|
+
...new Set([props.result.last_smr?.[0]?.has_code, props.result.last_asmr?.[0]?.has_code].filter(Boolean)),
|
|
164
|
+
] as string[],
|
|
165
|
+
opinionCount: [
|
|
166
|
+
smrEntries.length ? `${smrEntries.length} SMR` : null,
|
|
167
|
+
asmrEntries.length ? `${asmrEntries.length} ASMR` : null,
|
|
168
|
+
].filter(Boolean) as string[],
|
|
169
|
+
atc4: [en ? props.result.atc4_english_code_label : props.result.atc4_french_code_label].filter(Boolean) as string[],
|
|
170
|
+
sameMolecule: [
|
|
171
|
+
...new Set(
|
|
172
|
+
(props.result.molecule ?? [])
|
|
173
|
+
.map((m: any) => m?.brand)
|
|
174
|
+
.filter((brand: string) => brand && brand !== props.result.brand)
|
|
175
|
+
),
|
|
176
|
+
] as string[],
|
|
177
|
+
prices: [
|
|
178
|
+
amounts.length
|
|
179
|
+
? `${en ? "Public price" : "Prix public"} ${money(amounts[0])}${amounts.length > 1 && amounts[amounts.length - 1] !== amounts[0] ? ` ${en ? "to" : "à"} ${money(amounts[amounts.length - 1])}` : ""}`
|
|
180
|
+
: null,
|
|
181
|
+
...rates
|
|
182
|
+
.sort((a, b) => b - a)
|
|
183
|
+
.map((rate) => `${en ? "Reimbursed" : "Remboursement"} ${Math.round(rate * 100)} %`),
|
|
184
|
+
].filter(Boolean) as string[],
|
|
185
|
+
};
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
/** Le dernier avis HAS porte par la carte (SMR ou ASMR), en date ISO. */
|
|
189
|
+
const lastOpinionDate = computed(() =>
|
|
190
|
+
[lastSmr.value?.has_date, lastAsmr.value?.has_date]
|
|
191
|
+
.filter(Boolean)
|
|
192
|
+
.map((value: string) => formatHasDate(value)),
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
function formatHasDate(dateStr: string) {
|
|
196
|
+
if (!dateStr || dateStr.length !== 8) return dateStr;
|
|
197
|
+
return `${dateStr.substring(0, 4)}-${dateStr.substring(4, 6)}-${dateStr.substring(6, 8)}`;
|
|
198
|
+
}
|
|
199
|
+
</script>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Pied de source des blocs MITM (portage de heart/mitm/source de fr_drugs_app) :
|
|
3
|
+
la fiche Directus de la liste MITM, avec la date du dernier mouvement de ce
|
|
4
|
+
que l'appelant affiche quand il la connait.
|
|
5
|
+
-->
|
|
6
|
+
<template>
|
|
7
|
+
<OipDrugsHeartElasticSourceContainerElastic
|
|
8
|
+
class="oip-drugs"
|
|
9
|
+
:uuid="HEART_SOURCE_UUIDS.MITM"
|
|
10
|
+
tone="primary"
|
|
11
|
+
:last-label="lastLabel"
|
|
12
|
+
:last-date="lastDate"
|
|
13
|
+
/>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup lang="ts">
|
|
17
|
+
import { HEART_SOURCE_UUIDS } from "../../../../oip-drugs/utils/sourceUuids";
|
|
18
|
+
|
|
19
|
+
defineProps<{
|
|
20
|
+
lastLabel?: string;
|
|
21
|
+
lastDate?: string | number | null | Array<string | number | null | undefined>;
|
|
22
|
+
}>();
|
|
23
|
+
</script>
|