@tekkare/romulus 0.1.250 → 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.
Files changed (270) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/BarChart.vue +1 -0
  3. package/dist/runtime/components/ChoroplethMap.vue +4 -1
  4. package/dist/runtime/internal/geo/fr-regions.json +1 -1
  5. package/dist/runtime/utils/chart.d.ts +6 -3
  6. package/dist/runtime/utils/chart.js +15 -2
  7. package/layer/app/app.config.ts +6 -0
  8. package/layer/app/components/OipPubmedSummary.vue +223 -0
  9. package/layer/app/components/OipResourceDrawer.vue +331 -0
  10. package/layer/app/components/OipResourceDrawerLink.vue +40 -0
  11. package/layer/app/components/OipResourceField.vue +41 -0
  12. package/layer/app/components/OipResourceGenericCard.vue +54 -0
  13. package/layer/app/components/OipResourceSource.vue +50 -0
  14. package/layer/app/components/OipResourceTags.vue +65 -0
  15. package/layer/app/components/OipResourceView.vue +107 -0
  16. package/layer/app/components/oip-drugs/fiches/AssociationFiche.vue +775 -0
  17. package/layer/app/components/oip-drugs/fiches/BrandFiche.vue +1111 -0
  18. package/layer/app/components/oip-drugs/fiches/IndicationFiche.vue +182 -0
  19. package/layer/app/components/oip-drugs/fiches/JoFiche.vue +101 -0
  20. package/layer/app/components/oip-drugs/fiches/PndsFiche.vue +279 -0
  21. package/layer/app/components/oip-drugs/heart/JoCard.vue +314 -0
  22. package/layer/app/components/oip-drugs/heart/TextHighlight.vue +326 -0
  23. package/layer/app/components/oip-drugs/heart/ansm/BrandCard.vue +187 -0
  24. package/layer/app/components/oip-drugs/heart/ansm/CisDetail.vue +363 -0
  25. package/layer/app/components/oip-drugs/heart/ansm/CisTable.vue +167 -0
  26. package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyCard.vue +373 -0
  27. package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyFiche.vue +372 -0
  28. package/layer/app/components/oip-drugs/heart/associations/TranscriptionCard.vue +55 -0
  29. package/layer/app/components/oip-drugs/heart/elastic/SourceContainerElastic.vue +103 -0
  30. package/layer/app/components/oip-drugs/heart/france/BrandGroupCard.vue +450 -0
  31. package/layer/app/components/oip-drugs/heart/mitm/Card.vue +199 -0
  32. package/layer/app/components/oip-drugs/heart/mitm/Source.vue +23 -0
  33. package/layer/app/components/oip-drugs/heart/mitm/details/Overview.vue +381 -0
  34. package/layer/app/components/oip-drugs/heart/put/Card.vue +212 -0
  35. package/layer/app/components/oip-drugs/heart/results/AccesCard.vue +696 -0
  36. package/layer/app/components/oip-drugs/heart/results/AlertCard.vue +342 -0
  37. package/layer/app/components/oip-drugs/heart/results/AsmrCursor.vue +229 -0
  38. package/layer/app/components/oip-drugs/heart/results/EvalCard.vue +370 -0
  39. package/layer/app/components/oip-drugs/heart/results/IndicCard.vue +743 -0
  40. package/layer/app/components/oip-drugs/heart/results/MatchExplain.vue +243 -0
  41. package/layer/app/components/oip-drugs/heart/results/PrefRows.vue +105 -0
  42. package/layer/app/components/oip-drugs/heart/results/details/HasHistory.vue +487 -0
  43. package/layer/app/components/oip-drugs/heart/results/details/InternationalCountryCard.vue +461 -0
  44. package/layer/app/components/oip-drugs/heart/results/details/InternationalOverview.vue +200 -0
  45. package/layer/app/components/oip-drugs/heart/results/details/InternationalPriceDrawer.vue +647 -0
  46. package/layer/app/components/oip-drugs/heart/results/details/Monograph.vue +128 -0
  47. package/layer/app/components/oip-drugs/heart/results/details/OpinionDrawer.vue +535 -0
  48. package/layer/app/components/oip-drugs/heart/results/details/PresentationDrawer.vue +738 -0
  49. package/layer/app/components/oip-drugs/heart/results/details/Price.vue +227 -0
  50. package/layer/app/components/oip-drugs/heart/results/details/PriceOverview.vue +1261 -0
  51. package/layer/app/components/oip-drugs/heart/results/details/RecordDrawer.vue +494 -0
  52. package/layer/app/components/oip-drugs/heart/results/details/RecordList.vue +426 -0
  53. package/layer/app/components/oip-drugs/heart/results/les/Card.vue +340 -0
  54. package/layer/app/components/oip-drugs/heart/results/les/Drawer.vue +67 -0
  55. package/layer/app/components/oip-drugs/heart/results/les/IndicationDetail.vue +149 -0
  56. package/layer/app/components/oip-drugs/heart/results/les/Source.vue +20 -0
  57. package/layer/app/components/oip-drugs/heart/results/les/SourceTag.vue +39 -0
  58. package/layer/app/components/oip-drugs/heart/searchPage/DynamicSource.vue +29 -0
  59. package/layer/app/components/oip-drugs/medicam/market/Chart.vue +121 -0
  60. package/layer/app/components/oip-drugs/outpatient/ProductEvolution.vue +236 -0
  61. package/layer/app/components/oip-drugs/resources/Ansm.vue +75 -0
  62. package/layer/app/components/oip-drugs/resources/Association.vue +30 -0
  63. package/layer/app/components/oip-drugs/resources/Brand.vue +65 -0
  64. package/layer/app/components/oip-drugs/resources/Contribution.vue +216 -0
  65. package/layer/app/components/oip-drugs/resources/Hospital.vue +151 -0
  66. package/layer/app/components/oip-drugs/resources/Indication.vue +54 -0
  67. package/layer/app/components/oip-drugs/resources/Jo.vue +57 -0
  68. package/layer/app/components/oip-drugs/resources/Medicam.vue +140 -0
  69. package/layer/app/components/oip-drugs/resources/Mitm.vue +84 -0
  70. package/layer/app/components/oip-drugs/resources/NationalAssembly.vue +93 -0
  71. package/layer/app/components/oip-drugs/resources/Outpatient.vue +97 -0
  72. package/layer/app/components/oip-drugs/resources/Pnds.vue +24 -0
  73. package/layer/app/components/oip-drugs/resources/Price.vue +106 -0
  74. package/layer/app/components/oip-drugs/resources/Put.vue +143 -0
  75. package/layer/app/components/oip-science/appel-a-projet/Cards.vue +162 -0
  76. package/layer/app/components/oip-science/clinical-trials/Cards.vue +382 -0
  77. package/layer/app/components/oip-science/clinical-trials/CardsGlobal.vue +273 -0
  78. package/layer/app/components/oip-science/clinical-trials/LinkedList.vue +366 -0
  79. package/layer/app/components/oip-science/clinical-trials/PairBadge.vue +25 -0
  80. package/layer/app/components/oip-science/clinical-trials/PairBody.vue +182 -0
  81. package/layer/app/components/oip-science/clinical-trials/SitesDrawer.vue +95 -0
  82. package/layer/app/components/oip-science/common/Form.vue +73 -0
  83. package/layer/app/components/oip-science/common/MatchAvgTag.vue +283 -0
  84. package/layer/app/components/oip-science/common/NoData.vue +16 -0
  85. package/layer/app/components/oip-science/common/ScoreBadge.vue +35 -0
  86. package/layer/app/components/oip-science/common/Source.vue +31 -0
  87. package/layer/app/components/oip-science/common/TableTags.vue +279 -0
  88. package/layer/app/components/oip-science/common/Timeline.vue +104 -0
  89. package/layer/app/components/oip-science/common/ToolTips.vue +89 -0
  90. package/layer/app/components/oip-science/cross/publicationPubmed.vue +484 -0
  91. package/layer/app/components/oip-science/crossData/CrossDataLocations.vue +71 -0
  92. package/layer/app/components/oip-science/ctgov/Investigators.vue +148 -0
  93. package/layer/app/components/oip-science/ctgov/Locations.vue +102 -0
  94. package/layer/app/components/oip-science/ctgov/Publications.vue +52 -0
  95. package/layer/app/components/oip-science/ctgov/Sponsors.vue +66 -0
  96. package/layer/app/components/oip-science/ctgov/StudyDescription.vue +1136 -0
  97. package/layer/app/components/oip-science/ctgov-centers/ClinicalTrials.vue +40 -0
  98. package/layer/app/components/oip-science/ctgov-centers/Publications.vue +50 -0
  99. package/layer/app/components/oip-science/ctgov-investigators/ClinicalTrials.vue +43 -0
  100. package/layer/app/components/oip-science/ctgov-investigators/Informations.vue +171 -0
  101. package/layer/app/components/oip-science/ctgov-investigators/Publications.vue +44 -0
  102. package/layer/app/components/oip-science/elastic/DateRangeFilter.vue +209 -0
  103. package/layer/app/components/oip-science/elastic/Filter.vue +719 -0
  104. package/layer/app/components/oip-science/elastic/MatchExplain.vue +152 -0
  105. package/layer/app/components/oip-science/establishments/Cards.vue +183 -0
  106. package/layer/app/components/oip-science/establishments/ClinicalTrials.vue +62 -0
  107. package/layer/app/components/oip-science/establishments/Hcps.vue +227 -0
  108. package/layer/app/components/oip-science/establishments/Informations.vue +263 -0
  109. package/layer/app/components/oip-science/establishments/LinkedList.vue +153 -0
  110. package/layer/app/components/oip-science/establishments/MatchedLabels.vue +194 -0
  111. package/layer/app/components/oip-science/establishments/PublicFinance.vue +62 -0
  112. package/layer/app/components/oip-science/euct/Investigators.vue +125 -0
  113. package/layer/app/components/oip-science/euct/Locations.vue +102 -0
  114. package/layer/app/components/oip-science/euct/Products.vue +101 -0
  115. package/layer/app/components/oip-science/euct/Publications.vue +89 -0
  116. package/layer/app/components/oip-science/euct/Sponsors.vue +86 -0
  117. package/layer/app/components/oip-science/euct/StudyDescription.vue +1075 -0
  118. package/layer/app/components/oip-science/euct-centers/ClinicalTrials.vue +40 -0
  119. package/layer/app/components/oip-science/euct-centers/Publications.vue +55 -0
  120. package/layer/app/components/oip-science/euct-investigators/ClinicalTrials.vue +41 -0
  121. package/layer/app/components/oip-science/euct-investigators/Informations.vue +171 -0
  122. package/layer/app/components/oip-science/euct-investigators/Publications.vue +51 -0
  123. package/layer/app/components/oip-science/hcps/AppelsAProjet.vue +62 -0
  124. package/layer/app/components/oip-science/hcps/Cards.vue +237 -0
  125. package/layer/app/components/oip-science/hcps/ClinicalTrials.vue +78 -0
  126. package/layer/app/components/oip-science/hcps/Informations.vue +296 -0
  127. package/layer/app/components/oip-science/hcps/LinkedList.vue +153 -0
  128. package/layer/app/components/oip-science/hcps/MatchedLabels.vue +207 -0
  129. package/layer/app/components/oip-science/hcps/Pubmed.vue +128 -0
  130. package/layer/app/components/oip-science/local/SliderTabs.vue +99 -0
  131. package/layer/app/components/oip-science/local/SliderTabsFull.vue +135 -0
  132. package/layer/app/components/oip-science/pubmed/Authors.vue +128 -0
  133. package/layer/app/components/oip-science/pubmed/Cards.vue +417 -0
  134. package/layer/app/components/oip-science/pubmed/ClinicalTrials.vue +71 -0
  135. package/layer/app/components/oip-science/resources/Phrc.vue +39 -0
  136. package/layer/app/composables/useOipChapterScroll.ts +43 -0
  137. package/layer/app/composables/useOipResourceHooks.ts +27 -0
  138. package/layer/app/composables/useOipResourceNav.ts +66 -0
  139. package/layer/app/composables/useOipResourceViews.ts +117 -0
  140. package/layer/app/composables/useOipResources.ts +83 -0
  141. package/layer/app/oip-drugs/PORTING.md +120 -0
  142. package/layer/app/oip-drugs/composables/useAssociationDetail.ts +220 -0
  143. package/layer/app/oip-drugs/composables/useAssociationExport.ts +63 -0
  144. package/layer/app/oip-drugs/composables/useAssociationLookup.ts +105 -0
  145. package/layer/app/oip-drugs/composables/useCardPrefOptions.ts +201 -0
  146. package/layer/app/oip-drugs/composables/useCurrency.ts +121 -0
  147. package/layer/app/oip-drugs/composables/useDirectusSources.ts +224 -0
  148. package/layer/app/oip-drugs/composables/useHeartBrandMatches.ts +112 -0
  149. package/layer/app/oip-drugs/composables/useHeartDetailSearches.ts +479 -0
  150. package/layer/app/oip-drugs/composables/useHeartIntlApprovals.ts +661 -0
  151. package/layer/app/oip-drugs/composables/useHeartLesIndication.ts +168 -0
  152. package/layer/app/oip-drugs/composables/useHeartMatchExplain.ts +231 -0
  153. package/layer/app/oip-drugs/composables/useHeartReports.ts +239 -0
  154. package/layer/app/oip-drugs/composables/useHeartSearchFields.ts +233 -0
  155. package/layer/app/oip-drugs/composables/useMergeByKey.ts +23 -0
  156. package/layer/app/oip-drugs/composables/useOipDrugsElastic.ts +621 -0
  157. package/layer/app/oip-drugs/composables/usePageEmpty.ts +58 -0
  158. package/layer/app/oip-drugs/composables/v-useSalesSource.ts +64 -0
  159. package/layer/app/oip-drugs/drugs.css +500 -0
  160. package/layer/app/oip-drugs/esIndices.ts +322 -0
  161. package/layer/app/oip-drugs/kinds.ts +264 -0
  162. package/layer/app/oip-drugs/locales/en.json +1571 -0
  163. package/layer/app/oip-drugs/locales/fr.json +1571 -0
  164. package/layer/app/oip-drugs/monographs/blincyto-has.vue +289 -0
  165. package/layer/app/oip-drugs/monographs/darzalex-has.vue +276 -0
  166. package/layer/app/oip-drugs/monographs/dupixent-has.vue +216 -0
  167. package/layer/app/oip-drugs/monographs/enhertu-has.vue +280 -0
  168. package/layer/app/oip-drugs/monographs/has-sein-2023.vue +1649 -0
  169. package/layer/app/oip-drugs/monographs/ibrance-has.vue +277 -0
  170. package/layer/app/oip-drugs/monographs/imfinzi-has.vue +293 -0
  171. package/layer/app/oip-drugs/monographs/keytruda-has.vue +334 -0
  172. package/layer/app/oip-drugs/monographs/kisqali-has.vue +201 -0
  173. package/layer/app/oip-drugs/monographs/opdivo-has.vue +231 -0
  174. package/layer/app/oip-drugs/monographs/ozempic-has.vue +186 -0
  175. package/layer/app/oip-drugs/monographs/tecentriq-has.vue +312 -0
  176. package/layer/app/oip-drugs/monographs/yescarta-has.vue +272 -0
  177. package/layer/app/oip-drugs/types/elastic.ts +113 -0
  178. package/layer/app/oip-drugs/types/es/agencies.ts +152 -0
  179. package/layer/app/oip-drugs/types/es/directory.ts +54 -0
  180. package/layer/app/oip-drugs/types/es/docByIndex.ts +151 -0
  181. package/layer/app/oip-drugs/types/es/openmedic.ts +67 -0
  182. package/layer/app/oip-drugs/types/es/outpatient.ts +71 -0
  183. package/layer/app/oip-drugs/types/es/portal.ts +130 -0
  184. package/layer/app/oip-drugs/types/es/retail.ts +107 -0
  185. package/layer/app/oip-drugs/types/es/statistics.ts +29 -0
  186. package/layer/app/oip-drugs/types/heart/CurrencyRate.ts +16 -0
  187. package/layer/app/oip-drugs/types/heart/ansm.ts +106 -0
  188. package/layer/app/oip-drugs/types/heart/tag.ts +29 -0
  189. package/layer/app/oip-drugs/utils/associationFields.ts +118 -0
  190. package/layer/app/oip-drugs/utils/contributions.ts +36 -0
  191. package/layer/app/oip-drugs/utils/countryMapping.ts +387 -0
  192. package/layer/app/oip-drugs/utils/esHighlight.ts +21 -0
  193. package/layer/app/oip-drugs/utils/exportSheets.ts +57 -0
  194. package/layer/app/oip-drugs/utils/heartIntlPrice.ts +228 -0
  195. package/layer/app/oip-drugs/utils/heartStyleAndFormat.ts +262 -0
  196. package/layer/app/oip-drugs/utils/heartStyleAnsm.ts +71 -0
  197. package/layer/app/oip-drugs/utils/latestDate.ts +31 -0
  198. package/layer/app/oip-drugs/utils/p-heartCardTags.ts +53 -0
  199. package/layer/app/oip-drugs/utils/priceExport.ts +269 -0
  200. package/layer/app/oip-drugs/utils/q-hasRatings.ts +43 -0
  201. package/layer/app/oip-drugs/utils/s-openCard.ts +20 -0
  202. package/layer/app/oip-drugs/utils/sourceUuids.ts +155 -0
  203. package/layer/app/oip-drugs/utils/styleAndFormat.ts +40 -0
  204. package/layer/app/oip-drugs/utils/v-salesSeries.ts +156 -0
  205. package/layer/app/oip-drugs/utils/v-scopeTags.ts +23 -0
  206. package/layer/app/oip-science/PORTING.md +93 -0
  207. package/layer/app/oip-science/assets/smartSelection.json +428 -0
  208. package/layer/app/oip-science/composables/useAuthorMatching.ts +342 -0
  209. package/layer/app/oip-science/composables/useCTCenter.ts +107 -0
  210. package/layer/app/oip-science/composables/useCTInvestigators.ts +107 -0
  211. package/layer/app/oip-science/composables/useEstablishmentCrossData.ts +139 -0
  212. package/layer/app/oip-science/composables/useEstablishmentMatching.ts +176 -0
  213. package/layer/app/oip-science/composables/useEuct.ts +418 -0
  214. package/layer/app/oip-science/composables/useEuctCenter.ts +107 -0
  215. package/layer/app/oip-science/composables/useEuctInvestigators.ts +107 -0
  216. package/layer/app/oip-science/composables/useGlobalClinicalTrials.ts +624 -0
  217. package/layer/app/oip-science/composables/useLinkedTrials.ts +83 -0
  218. package/layer/app/oip-science/composables/useOipElastic.ts +658 -0
  219. package/layer/app/oip-science/composables/useOipSearchExplain.ts +166 -0
  220. package/layer/app/oip-science/composables/usePubmed.ts +487 -0
  221. package/layer/app/oip-science/composables/useSearchFields.ts +153 -0
  222. package/layer/app/oip-science/indexConfig.json +794 -0
  223. package/layer/app/oip-science/locales/en.json +483 -0
  224. package/layer/app/oip-science/locales/fr.json +483 -0
  225. package/layer/app/oip-science/science.css +1182 -0
  226. package/layer/app/oip-science/utils/a-openCard.ts +23 -0
  227. package/layer/app/oip-science/utils/b-openCard.ts +35 -0
  228. package/layer/app/oip-science/utils/c-filterContext.ts +81 -0
  229. package/layer/app/oip-science/utils/f-types.ts +241 -0
  230. package/layer/app/oip-science/utils/frDiseaseTerms.ts +269 -0
  231. package/layer/app/oip-science/utils/matchMethod.ts +53 -0
  232. package/layer/app/oip-science/utils/sliderTabs.ts +13 -0
  233. package/layer/app/oip-science/utils/sources.ts +15 -0
  234. package/layer/app/oip-science/utils/styleAndFormat.ts +608 -0
  235. package/layer/app/oip-science/utils/toPresets.ts +7 -0
  236. package/layer/app/oip-science/utils/valueTable.ts +126 -0
  237. package/layer/app/plugins/oip-science-i18n.ts +17 -0
  238. package/layer/app/utils/oipFicheRoutes.ts +118 -0
  239. package/layer/app/utils/oipResources.ts +440 -0
  240. package/layer/nuxt.config.ts +22 -2
  241. package/layer/public/reports/blincyto-has.html +292 -0
  242. package/layer/public/reports/blincyto-has.pdf +0 -0
  243. package/layer/public/reports/darzalex-has.html +279 -0
  244. package/layer/public/reports/darzalex-has.pdf +0 -0
  245. package/layer/public/reports/dupixent-has.html +217 -0
  246. package/layer/public/reports/dupixent-has.pdf +0 -0
  247. package/layer/public/reports/enhertu-has.html +283 -0
  248. package/layer/public/reports/enhertu-has.pdf +0 -0
  249. package/layer/public/reports/has-sein-2023.html +1639 -0
  250. package/layer/public/reports/ibrance-has.html +280 -0
  251. package/layer/public/reports/ibrance-has.pdf +0 -0
  252. package/layer/public/reports/imfinzi-has.html +297 -0
  253. package/layer/public/reports/imfinzi-has.pdf +0 -0
  254. package/layer/public/reports/keytruda-has.html +337 -0
  255. package/layer/public/reports/keytruda-has.pdf +0 -0
  256. package/layer/public/reports/kisqali-has.html +202 -0
  257. package/layer/public/reports/kisqali-has.pdf +0 -0
  258. package/layer/public/reports/opdivo-has.html +232 -0
  259. package/layer/public/reports/opdivo-has.pdf +0 -0
  260. package/layer/public/reports/ozempic-has.html +188 -0
  261. package/layer/public/reports/ozempic-has.pdf +0 -0
  262. package/layer/public/reports/tecentriq-has.html +315 -0
  263. package/layer/public/reports/tecentriq-has.pdf +0 -0
  264. package/layer/public/reports/yescarta-has.html +275 -0
  265. package/layer/public/reports/yescarta-has.pdf +0 -0
  266. package/layer/server/api/oip/es.post.ts +99 -0
  267. package/layer/server/api/oip/pdf.get.ts +70 -0
  268. package/layer/server/api/oip/reports.get.ts +98 -0
  269. package/layer/server/api/oip/resource-types.get.ts +22 -0
  270. package/package.json +1 -1
@@ -0,0 +1,152 @@
1
+ <!--
2
+ « Voir pourquoi »
3
+
4
+ Sous chaque carte, les familles de champs ou les termes de la recherche ont
5
+ ete trouves (avec le nombre d'objets concernes quand le champ est nested :
6
+ conditions, auteurs...) et, au depliage, les extraits exacts surlignes par
7
+ Elasticsearch. Rien n'est recalcule ici (cf. useOipSearchExplain).
8
+ Portage de components/elastic/MatchExplain.vue de France Augmented Science.
9
+ -->
10
+ <script setup lang="ts">
11
+ import { explainSearchMatch, type SearchMatch } from "../../../oip-science/composables/useOipSearchExplain";
12
+
13
+ const props = defineProps<{
14
+ /** `_match` du resultat, pose par `queryElastic({ explainFilter })`. */
15
+ match?: SearchMatch;
16
+ }>();
17
+
18
+ const { locale } = useI18n();
19
+
20
+ const fields = computed(() => explainSearchMatch(props.match, locale.value) ?? []);
21
+
22
+ // `_match` n'existe que quand la barre de recherche porte des termes : les
23
+ // extraits sont alors ouverts d'emblee, on les replie a la main.
24
+ const expanded = ref(true);
25
+
26
+ const excerpts = computed(() =>
27
+ fields.value.flatMap((field) =>
28
+ field.fragments.slice(0, 2).map((html) => ({ key: `${field.key}-${html}`, label: field.label, html })),
29
+ ),
30
+ );
31
+ </script>
32
+
33
+ <template>
34
+ <div v-if="fields.length" class="oip-science mx">
35
+ <!-- La carte entiere est cliquable : sans `stop`/`prevent`, deplier
36
+ ouvrirait la fiche. -->
37
+ <button type="button" class="mx_head" @click.stop.prevent="expanded = !expanded">
38
+ <RmIcon name="Target" :size="14" />
39
+ <span class="mx_intro">{{ locale === "en" ? "Match in" : "Correspondance dans" }}</span>
40
+ <span v-for="field in fields" :key="field.key" class="mx_chip">
41
+ {{ field.label }}
42
+ <b v-if="field.count">{{ field.count }}</b>
43
+ </span>
44
+ <span v-if="excerpts.length" class="mx_toggle">
45
+ {{ expanded ? (locale === "en" ? "Hide" : "Masquer") : (locale === "en" ? "See why" : "Voir pourquoi") }}
46
+ </span>
47
+ </button>
48
+
49
+ <div v-if="expanded && excerpts.length" class="mx_body" @click.stop>
50
+ <p v-for="excerpt in excerpts" :key="excerpt.key" class="mx_excerpt">
51
+ <span class="mx_excerpt_label">{{ excerpt.label }}</span>
52
+ <span class="mx_excerpt_text" v-html="excerpt.html" />
53
+ </p>
54
+ </div>
55
+ </div>
56
+ </template>
57
+
58
+ <style scoped>
59
+ .mx {
60
+ display: flex;
61
+ flex-direction: column;
62
+ gap: 8px;
63
+ box-sizing: border-box;
64
+ width: 100%;
65
+ padding-top: 10px;
66
+ border-top: 1px solid var(--border-soft);
67
+ }
68
+
69
+ .mx_head {
70
+ display: flex;
71
+ flex-wrap: wrap;
72
+ align-items: center;
73
+ gap: 6px;
74
+ padding: 0;
75
+ border: none;
76
+ background: none;
77
+ color: var(--text-muted);
78
+ font-family: inherit;
79
+ font-size: 13px;
80
+ text-align: left;
81
+ cursor: pointer;
82
+ }
83
+
84
+ .mx_intro {
85
+ font-weight: 600;
86
+ }
87
+
88
+ /* Meme rose que le surlignage des extraits : la pastille et le mot surligne
89
+ parlent de la meme chose. */
90
+ .mx_chip {
91
+ display: inline-flex;
92
+ align-items: center;
93
+ gap: 5px;
94
+ padding: 2px 9px;
95
+ border-radius: 999px;
96
+ background: #ffaeff;
97
+ color: #1a1a1a;
98
+ font-size: 12px;
99
+ font-weight: 700;
100
+ }
101
+
102
+ .mx_chip b {
103
+ font-weight: 800;
104
+ }
105
+
106
+ .mx_toggle {
107
+ margin-left: auto;
108
+ color: var(--color-primary);
109
+ font-size: 13px;
110
+ font-weight: 600;
111
+ text-decoration: underline;
112
+ }
113
+
114
+ .mx_body {
115
+ display: flex;
116
+ flex-direction: column;
117
+ gap: 8px;
118
+ cursor: default;
119
+ }
120
+
121
+ .mx_excerpt {
122
+ display: flex;
123
+ flex-direction: column;
124
+ gap: 2px;
125
+ margin: 0;
126
+ padding: 8px 10px;
127
+ border-radius: 8px;
128
+ background: var(--bg-hover);
129
+ font-size: 13px;
130
+ line-height: 1.5;
131
+ }
132
+
133
+ .mx_excerpt_label {
134
+ color: var(--text-muted);
135
+ font-size: 11px;
136
+ font-weight: 700;
137
+ letter-spacing: 0.04em;
138
+ text-transform: uppercase;
139
+ }
140
+
141
+ .mx_excerpt_text {
142
+ color: var(--text-primary);
143
+ overflow-wrap: anywhere;
144
+ }
145
+
146
+ .mx_excerpt_text :deep(mark) {
147
+ padding: 0 2px;
148
+ border-radius: 3px;
149
+ background: #ffaeff;
150
+ color: #1a1a1a;
151
+ }
152
+ </style>
@@ -0,0 +1,183 @@
1
+ <!--
2
+ Carte d'un etablissement (portage de components/establishments/Cards.vue de
3
+ France Augmented Science). Le menu d'export de la carte n'est pas repris.
4
+ -->
5
+ <template>
6
+ <RmResultCard
7
+ class="oip-science"
8
+ clickable
9
+ :lang="lang"
10
+ :link="detailsUrl"
11
+ @click="openCard($event, detailsUrl, openDetails)"
12
+ >
13
+ <template #code>
14
+ <RmCopyCode v-if="data?.hco_et_code" label="FINESS ET" :value="String(data.hco_et_code)" :lang="lang" />
15
+ </template>
16
+
17
+ <template #title>
18
+ <RmTextHighlight
19
+ :queries="highlight"
20
+ :text="ifNoRes(data?.hco_et_label)"
21
+ />
22
+ </template>
23
+
24
+ <template #subtitle>
25
+ {{ $t('oipScience.common.city') }}: {{ ifNoRes(data?.hco_et_city) }} · GHT: {{ ifNoRes(data?.hco_ej_label) }}
26
+ </template>
27
+
28
+ <template #actions>
29
+ <component
30
+ :is="projectButton"
31
+ v-if="projectButton"
32
+ :resource-id="data?.hco_et_code"
33
+ type="Establishment"
34
+ :label="data?.hco_et_label"
35
+ compact
36
+ />
37
+ </template>
38
+
39
+ <template #meta>
40
+ <RmTag v-if="data?.hco_et_sector" variant="blue">
41
+ {{ data.hco_et_sector }}
42
+ </RmTag>
43
+ <RmTag v-if="data?.hco_et_tekkare_category" variant="purple">
44
+ {{ data.hco_et_tekkare_category }}
45
+ </RmTag>
46
+ <OipScienceCommonMatchAvgTag
47
+ v-if="(data?.nct_name?.length || 0) + (data?.euct_name?.length || 0) > 0"
48
+ :nct-name="data?.nct_name"
49
+ :euct-name="data?.euct_name"
50
+ />
51
+ <slot name="extra" />
52
+ </template>
53
+
54
+ <div class="oip_row gap-16 wrap">
55
+ <div class="kpi-block" v-if="(data?.nct_trial_count || 0) > 0">
56
+ <p class="kpi-label">🇺🇸 NCT</p>
57
+ <div class="oip_row gap-16">
58
+ <div class="kpi-item">
59
+ <p class="kpi-value">{{ formatNumber(data.nct_trial_count) }}</p>
60
+ <p class="kpi-sub">{{ $t('oipScience.kpi.total') }}</p>
61
+ </div>
62
+ <div class="kpi-item">
63
+ <p class="kpi-value kpi-ongoing">{{ formatNumber(data?.nct_ongoing_count || 0) }}</p>
64
+ <p class="kpi-sub">{{ $t('oipScience.kpi.ongoing') }}</p>
65
+ </div>
66
+ </div>
67
+ </div>
68
+
69
+ <div class="kpi-block" v-if="(data?.euct_trial_count || 0) > 0">
70
+ <p class="kpi-label">🇪🇺 EUCT (CTIS)</p>
71
+ <div class="oip_row gap-16">
72
+ <div class="kpi-item">
73
+ <p class="kpi-value">{{ formatNumber(data.euct_trial_count) }}</p>
74
+ <p class="kpi-sub">{{ $t('oipScience.kpi.total') }}</p>
75
+ </div>
76
+ <div class="kpi-item">
77
+ <p class="kpi-value kpi-ongoing">{{ formatNumber(data?.euct_ongoing_count || 0) }}</p>
78
+ <p class="kpi-sub">{{ $t('oipScience.kpi.ongoing') }}</p>
79
+ </div>
80
+ </div>
81
+ </div>
82
+
83
+ <div class="kpi-block" v-if="(data?.nct_euct_hcp_count || 0) > 0">
84
+ <p class="kpi-label">🩺 {{ $t('oipScience.common.investigators') }}</p>
85
+ <div class="oip_row gap-16">
86
+ <div class="kpi-item">
87
+ <p class="kpi-value">{{ formatNumber(data.nct_euct_hcp_count) }}</p>
88
+ <p class="kpi-sub">{{ $t('oipScience.kpi.matched') }}</p>
89
+ </div>
90
+ </div>
91
+ </div>
92
+
93
+ <div class="kpi-block" v-if="(data?.pubmed_hcp_count || 0) > 0">
94
+ <p class="kpi-label">📚 {{ $t('oipScience.ui.authors') }}</p>
95
+ <div class="oip_row gap-16">
96
+ <div class="kpi-item">
97
+ <p class="kpi-value">{{ formatNumber(data.pubmed_hcp_count) }}</p>
98
+ <p class="kpi-sub">PubMed</p>
99
+ </div>
100
+ </div>
101
+ </div>
102
+
103
+ </div>
104
+
105
+ <template #footer>
106
+ <OipScienceCommonSource :sources="['FINESS']" :lang="lang" />
107
+ </template>
108
+ </RmResultCard>
109
+ </template>
110
+
111
+ <script setup lang="ts">
112
+ import type { PropType } from 'vue';
113
+ import { formatNumber, ifNoRes } from '../../../oip-science/utils/styleAndFormat';
114
+ import { openCard } from '../../../oip-science/utils/a-openCard';
115
+
116
+ const props = defineProps({
117
+ data: {
118
+ type: Object as () => any,
119
+ default: () => ({}),
120
+ },
121
+ /** Termes de la recherche en cours, surlignes dans le nom. */
122
+ highlight: {
123
+ type: Array as PropType<string[]>,
124
+ default: () => [],
125
+ },
126
+ });
127
+
128
+ const nav = useOipResourceNav();
129
+ const { projectButton } = useOipResourceHooks();
130
+
131
+ const { locale } = useI18n();
132
+ const lang = computed(() => (locale.value === 'en' ? 'en' : 'fr'));
133
+ const detailsUrl = computed(() =>
134
+ props.data?.hco_et_code ? nav.href('Establishment', String(props.data.hco_et_code), 'informations') : '',
135
+ );
136
+ const openDetails = () => nav.open('Establishment', String(props.data?.hco_et_code), 'informations');
137
+ </script>
138
+
139
+ <style scoped>
140
+ .kpi-block {
141
+ display: flex;
142
+ flex-direction: column;
143
+ align-items: center;
144
+ text-align: center;
145
+ gap: 4px;
146
+ padding: 0 16px;
147
+ min-width: 120px;
148
+ border-right: 1px solid var(--border-default);
149
+ }
150
+ .kpi-block:first-child {
151
+ padding-left: 0;
152
+ }
153
+ .kpi-block:last-child {
154
+ border-right: none;
155
+ }
156
+ .kpi-label {
157
+ font-size: 12px;
158
+ font-weight: 600;
159
+ color: var(--text-muted);
160
+ white-space: nowrap;
161
+ }
162
+ .kpi-item {
163
+ display: flex;
164
+ flex-direction: column;
165
+ align-items: center;
166
+ gap: 2px;
167
+ }
168
+ .kpi-value {
169
+ font-size: 15px;
170
+ font-weight: 700;
171
+ color: var(--text-primary);
172
+ }
173
+ .kpi-ongoing {
174
+ color: var(--color-success);
175
+ }
176
+ .kpi-sub {
177
+ font-size: 11px;
178
+ color: var(--text-muted);
179
+ text-transform: uppercase;
180
+ letter-spacing: 0.5px;
181
+ }
182
+
183
+ </style>
@@ -0,0 +1,62 @@
1
+ <!--
2
+ Onglet Essais cliniques de la fiche etablissement (porte de la page
3
+ establishments/[id]/clinical-trials de France Augmented Science). Les
4
+ donnees croisees, chargees par l'en-tete dans FAS, sont demandees ici.
5
+ -->
6
+ <template>
7
+ <div class="oip-science">
8
+ <OipScienceClinicalTrialsLinkedList
9
+ :nct-ids="nctIds"
10
+ :euct-ids="euctIds"
11
+ :extras="extras"
12
+ :title="$t('oipScience.common.clinicalTrials')"
13
+ :sources="['CT.gov', 'EUCT', 'FINESS']"
14
+ index-last-update="CT.gov"
15
+ :loading="isLoading"
16
+ />
17
+ </div>
18
+ </template>
19
+
20
+ <script setup lang="ts">
21
+ import useEstablishmentCrossData from '../../../oip-science/composables/useEstablishmentCrossData';
22
+
23
+ // Meme forme que LinkedTrialExtra de clinical-trials/LinkedList.vue.
24
+ type LinkedTrialExtra = { score?: number | null; role?: string | null };
25
+
26
+ const props = withDefaults(defineProps<{ id: string; highlight?: string[]; lang?: 'fr' | 'en' }>(), {
27
+ highlight: () => [],
28
+ lang: undefined,
29
+ });
30
+
31
+ const { fetchCrossData } = useEstablishmentCrossData();
32
+
33
+ const isLoading = ref(true);
34
+ const nctIds = ref<string[]>([]);
35
+ const euctIds = ref<string[]>([]);
36
+ const extras = ref<Record<string, LinkedTrialExtra>>({});
37
+
38
+ async function load(urlId: string) {
39
+ isLoading.value = true;
40
+ nctIds.value = [];
41
+ euctIds.value = [];
42
+ extras.value = {};
43
+ try {
44
+ const data = await fetchCrossData(urlId);
45
+ if (!data) return;
46
+ nctIds.value = data.nctIds;
47
+ euctIds.value = data.euctIds;
48
+ extras.value = Object.fromEntries(
49
+ data.crossResults.map((r: any) => [
50
+ r.clinical_trial_id,
51
+ { score: typeof r.score === 'number' ? r.score : null },
52
+ ]),
53
+ );
54
+ } catch (e) {
55
+ console.error(e);
56
+ } finally {
57
+ isLoading.value = false;
58
+ }
59
+ }
60
+
61
+ watch(() => props.id, (id) => { if (id) load(id); }, { immediate: true });
62
+ </script>
@@ -0,0 +1,227 @@
1
+ <!--
2
+ Onglet Investigateurs et auteurs de la fiche etablissement (porte de la page
3
+ establishments/[id]/hcps de France Augmented Science).
4
+ -->
5
+ <template>
6
+ <div class="oip-science">
7
+ <div v-if="!isLoading" class="oip_grid gap-16">
8
+ <RmSearchBar
9
+ v-if="results.length"
10
+ v-model="searchText"
11
+ :placeholder="$t('oipScience.common.searchEstablishmentHcps')"
12
+ :have-advanced="false"
13
+ />
14
+
15
+ <RmResultsLine icon="UserCircle" :items="[{ count: filteredResults.length, label: $t('oipScience.resultKpis.hcps', filteredResults.length) }]" />
16
+ <div class="oip_grid gap-8">
17
+ <OipScienceHcpsCards
18
+ v-for="hcp in visibleResults"
19
+ :key="hcp.hcp_code"
20
+ :data="hcp"
21
+ :aapCount="aapCounts[hcp.hcp_code] || 0"
22
+ :center-score="centerScores[hcp.hcp_code] ?? null"
23
+ :highlight="highlight"
24
+ />
25
+ </div>
26
+
27
+ <RmEmptyState
28
+ v-if="results.length && !filteredResults.length"
29
+ :title="$t('oipScience.common.noMatchingResult')"
30
+ />
31
+ <RmEmptyState
32
+ v-if="!results.length"
33
+ :title="$t('oipScience.common.noDataTitle')"
34
+ :text="$t('oipScience.common.noDataText')"
35
+ />
36
+ <RmButton
37
+ v-if="visibleResults.length < filteredResults.length"
38
+ variant="secondary"
39
+ class="load_more"
40
+ @click="displayCount += PAGE_SIZE"
41
+ >{{ $t('oipScience.common.loadMore') }}</RmButton>
42
+ </div>
43
+ <RmLoadingState v-if="isLoading" :lines="3" :chart-height="240" />
44
+ </div>
45
+ </template>
46
+
47
+ <script setup lang="ts">
48
+ import indexConfig from '../../../oip-science/indexConfig.json';
49
+ import useOipElastic from '../../../oip-science/composables/useOipElastic';
50
+ import useEstablishmentCrossData from '../../../oip-science/composables/useEstablishmentCrossData';
51
+ import useAuthorMatching from '../../../oip-science/composables/useAuthorMatching';
52
+
53
+ const props = withDefaults(defineProps<{ id: string; highlight?: string[]; lang?: 'fr' | 'en' }>(), {
54
+ highlight: () => [],
55
+ lang: undefined,
56
+ });
57
+
58
+ const { queryCustomElastic } = useOipElastic();
59
+ const { fetchCrossData } = useEstablishmentCrossData();
60
+
61
+ const isLoading = ref(true);
62
+ const results = ref<any[]>([]);
63
+ const aapCounts = ref<Record<string, number>>({});
64
+ const centerScores = ref<Record<string, number>>({});
65
+
66
+ async function load(urlId: string) {
67
+ isLoading.value = true;
68
+ results.value = [];
69
+ aapCounts.value = {};
70
+ centerScores.value = {};
71
+ searchText.value = '';
72
+ try {
73
+ // Query the new HCP directory: HCPs whose address contains this hco_et_code
74
+ const res = await queryCustomElastic({
75
+ index: indexConfig.hcps.index,
76
+ cluster: indexConfig.hcps.cluster,
77
+ customQuery: {
78
+ from: 0,
79
+ _source: true,
80
+ query: {
81
+ nested: {
82
+ path: 'address',
83
+ query: { term: { 'address.hco_et_code.keyword': urlId } },
84
+ },
85
+ },
86
+ size: 10000,
87
+ track_total_hits: true,
88
+ },
89
+ });
90
+
91
+ results.value = (res?.results || []).filter((r: any) => !!r?.hcp_code);
92
+
93
+ const rppsIds = results.value.map((r: any) => r.hcp_code);
94
+ if (rppsIds.length > 0) {
95
+ await Promise.all([fetchAapCounts(rppsIds), fetchCenterScores(urlId)]);
96
+ }
97
+ } catch (e) {
98
+ console.error(e);
99
+ } finally {
100
+ isLoading.value = false;
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Score de match de chaque professionnel pour ce centre : la moyenne de ses
106
+ * scores sur les seuls essais NCT / EUCT tenus ici. Les index de match ne
107
+ * relient un nom qu'a un essai, l'essai fait le lien avec le centre ; PubMed
108
+ * n'en a pas. Une requete par source, limitee aux essais communs et aux
109
+ * champs utiles.
110
+ */
111
+ async function fetchCenterScores(urlId: string) {
112
+ try {
113
+ const cross = await fetchCrossData(urlId);
114
+ if (!cross) return;
115
+ const sources = [
116
+ { cfg: indexConfig.hcpNct, centerIds: new Set(cross.nctIds), field: 'nct_trial' },
117
+ { cfg: indexConfig.hcpEuct, centerIds: new Set(cross.euctIds), field: 'euct_trial' },
118
+ ];
119
+ const scores: Record<string, number[]> = {};
120
+ await Promise.all(
121
+ sources.map(async ({ cfg, centerIds, field }) => {
122
+ // Essai du centre -> professionnels de la page qui y figurent.
123
+ const wanted = new Map<string, Set<string>>();
124
+ for (const hcp of results.value) {
125
+ for (const t of hcp?.[field] || []) {
126
+ const id = t?.clinical_trial_id;
127
+ if (!id || !centerIds.has(id)) continue;
128
+ if (!wanted.has(id)) wanted.set(id, new Set());
129
+ wanted.get(id)!.add(hcp.hcp_code);
130
+ }
131
+ }
132
+ if (!wanted.size) return;
133
+ const res = await queryCustomElastic({
134
+ index: cfg.index,
135
+ cluster: cfg.cluster,
136
+ customQuery: {
137
+ size: wanted.size,
138
+ _source: ['clinical_trial_id', 'hcp.hcp_code', 'hcp.score'],
139
+ query: { terms: { clinical_trial_id: [...wanted.keys()] } },
140
+ },
141
+ });
142
+ for (const doc of res?.results || []) {
143
+ const codes = wanted.get(String(doc?.clinical_trial_id));
144
+ if (!codes) continue;
145
+ for (const h of doc?.hcp || []) {
146
+ if (!codes.has(h?.hcp_code) || typeof h?.score !== 'number') continue;
147
+ (scores[h.hcp_code] ||= []).push(h.score);
148
+ }
149
+ }
150
+ }),
151
+ );
152
+ const avg: Record<string, number> = {};
153
+ for (const [code, list] of Object.entries(scores)) {
154
+ avg[code] = list.reduce((a, b) => a + b, 0) / list.length;
155
+ }
156
+ centerScores.value = avg;
157
+ } catch (e) {
158
+ console.error('Error fetching center match scores:', e);
159
+ }
160
+ }
161
+
162
+ async function fetchAapCounts(rppsIds: string[]) {
163
+ try {
164
+ const res = await queryCustomElastic({
165
+ index: indexConfig.phrc.index,
166
+ cluster: indexConfig.phrc.cluster,
167
+ customQuery: {
168
+ size: 0,
169
+ query: { terms: { 'hcp_code.keyword': rppsIds } },
170
+ aggs: {
171
+ by_hcp: {
172
+ terms: { field: 'hcp_code.keyword', size: rppsIds.length + 1 },
173
+ },
174
+ },
175
+ },
176
+ allAnswer: true,
177
+ });
178
+ const counts: Record<string, number> = {};
179
+ for (const bucket of res?.aggregations?.by_hcp?.buckets || []) {
180
+ counts[bucket.key] = bucket.doc_count;
181
+ }
182
+ aapCounts.value = counts;
183
+ } catch (e) {
184
+ console.error('Error fetching AAP counts:', e);
185
+ }
186
+ }
187
+
188
+ const principalSpecOf = (item: any): string => {
189
+ const specs = Array.isArray(item?.specialty) ? item.specialty : [];
190
+ const principal = specs.find((s: any) => s?.hcp_specialty_type === 'PRINCIPAL');
191
+ return principal?.hcp_specialty_label_regroup || '';
192
+ };
193
+
194
+ // Filtre local et affichage par paquets : un CHU compte des milliers de
195
+ // professionnels.
196
+ const { normalize } = useAuthorMatching();
197
+ const searchText = ref('');
198
+ const PAGE_SIZE = 20;
199
+ const displayCount = ref(PAGE_SIZE);
200
+
201
+ const filteredResults = computed(() => {
202
+ const q = normalize(searchText.value);
203
+ if (!q) return results.value;
204
+ return results.value.filter((item: any) =>
205
+ normalize(
206
+ [item?.hcp_first_name, item?.hcp_family_name, principalSpecOf(item), item?.hcp_code]
207
+ .filter(Boolean)
208
+ .join(' '),
209
+ ).includes(q),
210
+ );
211
+ });
212
+
213
+ const visibleResults = computed(() => filteredResults.value.slice(0, displayCount.value));
214
+
215
+ watch(searchText, () => {
216
+ displayCount.value = PAGE_SIZE;
217
+ });
218
+
219
+ watch(() => props.id, (id) => { if (id) load(id); }, { immediate: true });
220
+ </script>
221
+
222
+ <style scoped>
223
+ .load_more {
224
+ align-self: center;
225
+ justify-self: center;
226
+ }
227
+ </style>