@tekkare/romulus 0.1.251 → 0.1.253

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 (267) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/utils/projects.d.ts +8 -0
  3. package/dist/runtime/utils/projects.js +8 -5
  4. package/layer/app/app.config.ts +6 -0
  5. package/layer/app/components/OipPubmedSummary.vue +223 -0
  6. package/layer/app/components/OipResourceDrawer.vue +331 -0
  7. package/layer/app/components/OipResourceDrawerLink.vue +40 -0
  8. package/layer/app/components/OipResourceField.vue +41 -0
  9. package/layer/app/components/OipResourceGenericCard.vue +54 -0
  10. package/layer/app/components/OipResourceSource.vue +50 -0
  11. package/layer/app/components/OipResourceTags.vue +65 -0
  12. package/layer/app/components/OipResourceView.vue +107 -0
  13. package/layer/app/components/oip-drugs/fiches/AssociationFiche.vue +775 -0
  14. package/layer/app/components/oip-drugs/fiches/BrandFiche.vue +1111 -0
  15. package/layer/app/components/oip-drugs/fiches/IndicationFiche.vue +182 -0
  16. package/layer/app/components/oip-drugs/fiches/JoFiche.vue +101 -0
  17. package/layer/app/components/oip-drugs/fiches/PndsFiche.vue +279 -0
  18. package/layer/app/components/oip-drugs/heart/JoCard.vue +314 -0
  19. package/layer/app/components/oip-drugs/heart/TextHighlight.vue +326 -0
  20. package/layer/app/components/oip-drugs/heart/ansm/BrandCard.vue +187 -0
  21. package/layer/app/components/oip-drugs/heart/ansm/CisDetail.vue +363 -0
  22. package/layer/app/components/oip-drugs/heart/ansm/CisTable.vue +167 -0
  23. package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyCard.vue +373 -0
  24. package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyFiche.vue +372 -0
  25. package/layer/app/components/oip-drugs/heart/associations/TranscriptionCard.vue +55 -0
  26. package/layer/app/components/oip-drugs/heart/elastic/SourceContainerElastic.vue +103 -0
  27. package/layer/app/components/oip-drugs/heart/france/BrandGroupCard.vue +450 -0
  28. package/layer/app/components/oip-drugs/heart/mitm/Card.vue +199 -0
  29. package/layer/app/components/oip-drugs/heart/mitm/Source.vue +23 -0
  30. package/layer/app/components/oip-drugs/heart/mitm/details/Overview.vue +381 -0
  31. package/layer/app/components/oip-drugs/heart/put/Card.vue +212 -0
  32. package/layer/app/components/oip-drugs/heart/results/AccesCard.vue +696 -0
  33. package/layer/app/components/oip-drugs/heart/results/AlertCard.vue +342 -0
  34. package/layer/app/components/oip-drugs/heart/results/AsmrCursor.vue +229 -0
  35. package/layer/app/components/oip-drugs/heart/results/EvalCard.vue +370 -0
  36. package/layer/app/components/oip-drugs/heart/results/IndicCard.vue +743 -0
  37. package/layer/app/components/oip-drugs/heart/results/MatchExplain.vue +243 -0
  38. package/layer/app/components/oip-drugs/heart/results/PrefRows.vue +105 -0
  39. package/layer/app/components/oip-drugs/heart/results/details/HasHistory.vue +487 -0
  40. package/layer/app/components/oip-drugs/heart/results/details/InternationalCountryCard.vue +461 -0
  41. package/layer/app/components/oip-drugs/heart/results/details/InternationalOverview.vue +200 -0
  42. package/layer/app/components/oip-drugs/heart/results/details/InternationalPriceDrawer.vue +647 -0
  43. package/layer/app/components/oip-drugs/heart/results/details/Monograph.vue +128 -0
  44. package/layer/app/components/oip-drugs/heart/results/details/OpinionDrawer.vue +535 -0
  45. package/layer/app/components/oip-drugs/heart/results/details/PresentationDrawer.vue +738 -0
  46. package/layer/app/components/oip-drugs/heart/results/details/Price.vue +227 -0
  47. package/layer/app/components/oip-drugs/heart/results/details/PriceOverview.vue +1261 -0
  48. package/layer/app/components/oip-drugs/heart/results/details/RecordDrawer.vue +494 -0
  49. package/layer/app/components/oip-drugs/heart/results/details/RecordList.vue +426 -0
  50. package/layer/app/components/oip-drugs/heart/results/les/Card.vue +340 -0
  51. package/layer/app/components/oip-drugs/heart/results/les/Drawer.vue +67 -0
  52. package/layer/app/components/oip-drugs/heart/results/les/IndicationDetail.vue +149 -0
  53. package/layer/app/components/oip-drugs/heart/results/les/Source.vue +20 -0
  54. package/layer/app/components/oip-drugs/heart/results/les/SourceTag.vue +39 -0
  55. package/layer/app/components/oip-drugs/heart/searchPage/DynamicSource.vue +29 -0
  56. package/layer/app/components/oip-drugs/medicam/market/Chart.vue +121 -0
  57. package/layer/app/components/oip-drugs/outpatient/ProductEvolution.vue +236 -0
  58. package/layer/app/components/oip-drugs/resources/Ansm.vue +75 -0
  59. package/layer/app/components/oip-drugs/resources/Association.vue +30 -0
  60. package/layer/app/components/oip-drugs/resources/Brand.vue +65 -0
  61. package/layer/app/components/oip-drugs/resources/Contribution.vue +216 -0
  62. package/layer/app/components/oip-drugs/resources/Hospital.vue +151 -0
  63. package/layer/app/components/oip-drugs/resources/Indication.vue +54 -0
  64. package/layer/app/components/oip-drugs/resources/Jo.vue +57 -0
  65. package/layer/app/components/oip-drugs/resources/Medicam.vue +140 -0
  66. package/layer/app/components/oip-drugs/resources/Mitm.vue +84 -0
  67. package/layer/app/components/oip-drugs/resources/NationalAssembly.vue +93 -0
  68. package/layer/app/components/oip-drugs/resources/Outpatient.vue +97 -0
  69. package/layer/app/components/oip-drugs/resources/Pnds.vue +24 -0
  70. package/layer/app/components/oip-drugs/resources/Price.vue +106 -0
  71. package/layer/app/components/oip-drugs/resources/Put.vue +143 -0
  72. package/layer/app/components/oip-science/appel-a-projet/Cards.vue +162 -0
  73. package/layer/app/components/oip-science/clinical-trials/Cards.vue +382 -0
  74. package/layer/app/components/oip-science/clinical-trials/CardsGlobal.vue +273 -0
  75. package/layer/app/components/oip-science/clinical-trials/LinkedList.vue +366 -0
  76. package/layer/app/components/oip-science/clinical-trials/PairBadge.vue +25 -0
  77. package/layer/app/components/oip-science/clinical-trials/PairBody.vue +182 -0
  78. package/layer/app/components/oip-science/clinical-trials/SitesDrawer.vue +95 -0
  79. package/layer/app/components/oip-science/common/Form.vue +73 -0
  80. package/layer/app/components/oip-science/common/MatchAvgTag.vue +283 -0
  81. package/layer/app/components/oip-science/common/NoData.vue +16 -0
  82. package/layer/app/components/oip-science/common/ScoreBadge.vue +35 -0
  83. package/layer/app/components/oip-science/common/Source.vue +31 -0
  84. package/layer/app/components/oip-science/common/TableTags.vue +279 -0
  85. package/layer/app/components/oip-science/common/Timeline.vue +104 -0
  86. package/layer/app/components/oip-science/common/ToolTips.vue +89 -0
  87. package/layer/app/components/oip-science/cross/publicationPubmed.vue +484 -0
  88. package/layer/app/components/oip-science/crossData/CrossDataLocations.vue +71 -0
  89. package/layer/app/components/oip-science/ctgov/Investigators.vue +148 -0
  90. package/layer/app/components/oip-science/ctgov/Locations.vue +102 -0
  91. package/layer/app/components/oip-science/ctgov/Publications.vue +52 -0
  92. package/layer/app/components/oip-science/ctgov/Sponsors.vue +66 -0
  93. package/layer/app/components/oip-science/ctgov/StudyDescription.vue +1136 -0
  94. package/layer/app/components/oip-science/ctgov-centers/ClinicalTrials.vue +40 -0
  95. package/layer/app/components/oip-science/ctgov-centers/Publications.vue +50 -0
  96. package/layer/app/components/oip-science/ctgov-investigators/ClinicalTrials.vue +43 -0
  97. package/layer/app/components/oip-science/ctgov-investigators/Informations.vue +171 -0
  98. package/layer/app/components/oip-science/ctgov-investigators/Publications.vue +44 -0
  99. package/layer/app/components/oip-science/elastic/DateRangeFilter.vue +209 -0
  100. package/layer/app/components/oip-science/elastic/Filter.vue +719 -0
  101. package/layer/app/components/oip-science/elastic/MatchExplain.vue +152 -0
  102. package/layer/app/components/oip-science/establishments/Cards.vue +183 -0
  103. package/layer/app/components/oip-science/establishments/ClinicalTrials.vue +62 -0
  104. package/layer/app/components/oip-science/establishments/Hcps.vue +227 -0
  105. package/layer/app/components/oip-science/establishments/Informations.vue +263 -0
  106. package/layer/app/components/oip-science/establishments/LinkedList.vue +153 -0
  107. package/layer/app/components/oip-science/establishments/MatchedLabels.vue +194 -0
  108. package/layer/app/components/oip-science/establishments/PublicFinance.vue +62 -0
  109. package/layer/app/components/oip-science/euct/Investigators.vue +125 -0
  110. package/layer/app/components/oip-science/euct/Locations.vue +102 -0
  111. package/layer/app/components/oip-science/euct/Products.vue +101 -0
  112. package/layer/app/components/oip-science/euct/Publications.vue +89 -0
  113. package/layer/app/components/oip-science/euct/Sponsors.vue +86 -0
  114. package/layer/app/components/oip-science/euct/StudyDescription.vue +1075 -0
  115. package/layer/app/components/oip-science/euct-centers/ClinicalTrials.vue +40 -0
  116. package/layer/app/components/oip-science/euct-centers/Publications.vue +55 -0
  117. package/layer/app/components/oip-science/euct-investigators/ClinicalTrials.vue +41 -0
  118. package/layer/app/components/oip-science/euct-investigators/Informations.vue +171 -0
  119. package/layer/app/components/oip-science/euct-investigators/Publications.vue +51 -0
  120. package/layer/app/components/oip-science/hcps/AppelsAProjet.vue +62 -0
  121. package/layer/app/components/oip-science/hcps/Cards.vue +237 -0
  122. package/layer/app/components/oip-science/hcps/ClinicalTrials.vue +78 -0
  123. package/layer/app/components/oip-science/hcps/Informations.vue +296 -0
  124. package/layer/app/components/oip-science/hcps/LinkedList.vue +153 -0
  125. package/layer/app/components/oip-science/hcps/MatchedLabels.vue +207 -0
  126. package/layer/app/components/oip-science/hcps/Pubmed.vue +128 -0
  127. package/layer/app/components/oip-science/local/SliderTabs.vue +99 -0
  128. package/layer/app/components/oip-science/local/SliderTabsFull.vue +135 -0
  129. package/layer/app/components/oip-science/pubmed/Authors.vue +128 -0
  130. package/layer/app/components/oip-science/pubmed/Cards.vue +417 -0
  131. package/layer/app/components/oip-science/pubmed/ClinicalTrials.vue +71 -0
  132. package/layer/app/components/oip-science/resources/Phrc.vue +39 -0
  133. package/layer/app/composables/useOipChapterScroll.ts +43 -0
  134. package/layer/app/composables/useOipResourceHooks.ts +27 -0
  135. package/layer/app/composables/useOipResourceNav.ts +66 -0
  136. package/layer/app/composables/useOipResourceViews.ts +117 -0
  137. package/layer/app/composables/useOipResources.ts +83 -0
  138. package/layer/app/oip-drugs/PORTING.md +120 -0
  139. package/layer/app/oip-drugs/composables/useAssociationDetail.ts +220 -0
  140. package/layer/app/oip-drugs/composables/useAssociationExport.ts +63 -0
  141. package/layer/app/oip-drugs/composables/useAssociationLookup.ts +105 -0
  142. package/layer/app/oip-drugs/composables/useCardPrefOptions.ts +201 -0
  143. package/layer/app/oip-drugs/composables/useCurrency.ts +121 -0
  144. package/layer/app/oip-drugs/composables/useDirectusSources.ts +224 -0
  145. package/layer/app/oip-drugs/composables/useHeartBrandMatches.ts +112 -0
  146. package/layer/app/oip-drugs/composables/useHeartDetailSearches.ts +479 -0
  147. package/layer/app/oip-drugs/composables/useHeartIntlApprovals.ts +661 -0
  148. package/layer/app/oip-drugs/composables/useHeartLesIndication.ts +168 -0
  149. package/layer/app/oip-drugs/composables/useHeartMatchExplain.ts +231 -0
  150. package/layer/app/oip-drugs/composables/useHeartReports.ts +239 -0
  151. package/layer/app/oip-drugs/composables/useHeartSearchFields.ts +233 -0
  152. package/layer/app/oip-drugs/composables/useMergeByKey.ts +23 -0
  153. package/layer/app/oip-drugs/composables/useOipDrugsElastic.ts +621 -0
  154. package/layer/app/oip-drugs/composables/usePageEmpty.ts +58 -0
  155. package/layer/app/oip-drugs/composables/v-useSalesSource.ts +64 -0
  156. package/layer/app/oip-drugs/drugs.css +500 -0
  157. package/layer/app/oip-drugs/esIndices.ts +322 -0
  158. package/layer/app/oip-drugs/kinds.ts +264 -0
  159. package/layer/app/oip-drugs/locales/en.json +1571 -0
  160. package/layer/app/oip-drugs/locales/fr.json +1571 -0
  161. package/layer/app/oip-drugs/monographs/blincyto-has.vue +289 -0
  162. package/layer/app/oip-drugs/monographs/darzalex-has.vue +276 -0
  163. package/layer/app/oip-drugs/monographs/dupixent-has.vue +216 -0
  164. package/layer/app/oip-drugs/monographs/enhertu-has.vue +280 -0
  165. package/layer/app/oip-drugs/monographs/has-sein-2023.vue +1649 -0
  166. package/layer/app/oip-drugs/monographs/ibrance-has.vue +277 -0
  167. package/layer/app/oip-drugs/monographs/imfinzi-has.vue +293 -0
  168. package/layer/app/oip-drugs/monographs/keytruda-has.vue +334 -0
  169. package/layer/app/oip-drugs/monographs/kisqali-has.vue +201 -0
  170. package/layer/app/oip-drugs/monographs/opdivo-has.vue +231 -0
  171. package/layer/app/oip-drugs/monographs/ozempic-has.vue +186 -0
  172. package/layer/app/oip-drugs/monographs/tecentriq-has.vue +312 -0
  173. package/layer/app/oip-drugs/monographs/yescarta-has.vue +272 -0
  174. package/layer/app/oip-drugs/types/elastic.ts +113 -0
  175. package/layer/app/oip-drugs/types/es/agencies.ts +152 -0
  176. package/layer/app/oip-drugs/types/es/directory.ts +54 -0
  177. package/layer/app/oip-drugs/types/es/docByIndex.ts +151 -0
  178. package/layer/app/oip-drugs/types/es/openmedic.ts +67 -0
  179. package/layer/app/oip-drugs/types/es/outpatient.ts +71 -0
  180. package/layer/app/oip-drugs/types/es/portal.ts +130 -0
  181. package/layer/app/oip-drugs/types/es/retail.ts +107 -0
  182. package/layer/app/oip-drugs/types/es/statistics.ts +29 -0
  183. package/layer/app/oip-drugs/types/heart/CurrencyRate.ts +16 -0
  184. package/layer/app/oip-drugs/types/heart/ansm.ts +106 -0
  185. package/layer/app/oip-drugs/types/heart/tag.ts +29 -0
  186. package/layer/app/oip-drugs/utils/associationFields.ts +118 -0
  187. package/layer/app/oip-drugs/utils/contributions.ts +36 -0
  188. package/layer/app/oip-drugs/utils/countryMapping.ts +387 -0
  189. package/layer/app/oip-drugs/utils/esHighlight.ts +21 -0
  190. package/layer/app/oip-drugs/utils/exportSheets.ts +57 -0
  191. package/layer/app/oip-drugs/utils/heartIntlPrice.ts +228 -0
  192. package/layer/app/oip-drugs/utils/heartStyleAndFormat.ts +262 -0
  193. package/layer/app/oip-drugs/utils/heartStyleAnsm.ts +71 -0
  194. package/layer/app/oip-drugs/utils/latestDate.ts +31 -0
  195. package/layer/app/oip-drugs/utils/p-heartCardTags.ts +53 -0
  196. package/layer/app/oip-drugs/utils/priceExport.ts +269 -0
  197. package/layer/app/oip-drugs/utils/q-hasRatings.ts +43 -0
  198. package/layer/app/oip-drugs/utils/s-openCard.ts +20 -0
  199. package/layer/app/oip-drugs/utils/sourceUuids.ts +155 -0
  200. package/layer/app/oip-drugs/utils/styleAndFormat.ts +40 -0
  201. package/layer/app/oip-drugs/utils/v-salesSeries.ts +156 -0
  202. package/layer/app/oip-drugs/utils/v-scopeTags.ts +23 -0
  203. package/layer/app/oip-science/PORTING.md +93 -0
  204. package/layer/app/oip-science/assets/smartSelection.json +428 -0
  205. package/layer/app/oip-science/composables/useAuthorMatching.ts +342 -0
  206. package/layer/app/oip-science/composables/useCTCenter.ts +107 -0
  207. package/layer/app/oip-science/composables/useCTInvestigators.ts +107 -0
  208. package/layer/app/oip-science/composables/useEstablishmentCrossData.ts +139 -0
  209. package/layer/app/oip-science/composables/useEstablishmentMatching.ts +176 -0
  210. package/layer/app/oip-science/composables/useEuct.ts +418 -0
  211. package/layer/app/oip-science/composables/useEuctCenter.ts +107 -0
  212. package/layer/app/oip-science/composables/useEuctInvestigators.ts +107 -0
  213. package/layer/app/oip-science/composables/useGlobalClinicalTrials.ts +624 -0
  214. package/layer/app/oip-science/composables/useLinkedTrials.ts +83 -0
  215. package/layer/app/oip-science/composables/useOipElastic.ts +658 -0
  216. package/layer/app/oip-science/composables/useOipSearchExplain.ts +166 -0
  217. package/layer/app/oip-science/composables/usePubmed.ts +487 -0
  218. package/layer/app/oip-science/composables/useSearchFields.ts +153 -0
  219. package/layer/app/oip-science/indexConfig.json +794 -0
  220. package/layer/app/oip-science/locales/en.json +483 -0
  221. package/layer/app/oip-science/locales/fr.json +483 -0
  222. package/layer/app/oip-science/science.css +1182 -0
  223. package/layer/app/oip-science/utils/a-openCard.ts +23 -0
  224. package/layer/app/oip-science/utils/b-openCard.ts +35 -0
  225. package/layer/app/oip-science/utils/c-filterContext.ts +81 -0
  226. package/layer/app/oip-science/utils/f-types.ts +241 -0
  227. package/layer/app/oip-science/utils/frDiseaseTerms.ts +269 -0
  228. package/layer/app/oip-science/utils/matchMethod.ts +53 -0
  229. package/layer/app/oip-science/utils/sliderTabs.ts +13 -0
  230. package/layer/app/oip-science/utils/sources.ts +15 -0
  231. package/layer/app/oip-science/utils/styleAndFormat.ts +608 -0
  232. package/layer/app/oip-science/utils/toPresets.ts +7 -0
  233. package/layer/app/oip-science/utils/valueTable.ts +126 -0
  234. package/layer/app/plugins/oip-science-i18n.ts +17 -0
  235. package/layer/app/utils/oipFicheRoutes.ts +118 -0
  236. package/layer/app/utils/oipResources.ts +440 -0
  237. package/layer/nuxt.config.ts +22 -2
  238. package/layer/public/reports/blincyto-has.html +292 -0
  239. package/layer/public/reports/blincyto-has.pdf +0 -0
  240. package/layer/public/reports/darzalex-has.html +279 -0
  241. package/layer/public/reports/darzalex-has.pdf +0 -0
  242. package/layer/public/reports/dupixent-has.html +217 -0
  243. package/layer/public/reports/dupixent-has.pdf +0 -0
  244. package/layer/public/reports/enhertu-has.html +283 -0
  245. package/layer/public/reports/enhertu-has.pdf +0 -0
  246. package/layer/public/reports/has-sein-2023.html +1639 -0
  247. package/layer/public/reports/ibrance-has.html +280 -0
  248. package/layer/public/reports/ibrance-has.pdf +0 -0
  249. package/layer/public/reports/imfinzi-has.html +297 -0
  250. package/layer/public/reports/imfinzi-has.pdf +0 -0
  251. package/layer/public/reports/keytruda-has.html +337 -0
  252. package/layer/public/reports/keytruda-has.pdf +0 -0
  253. package/layer/public/reports/kisqali-has.html +202 -0
  254. package/layer/public/reports/kisqali-has.pdf +0 -0
  255. package/layer/public/reports/opdivo-has.html +232 -0
  256. package/layer/public/reports/opdivo-has.pdf +0 -0
  257. package/layer/public/reports/ozempic-has.html +188 -0
  258. package/layer/public/reports/ozempic-has.pdf +0 -0
  259. package/layer/public/reports/tecentriq-has.html +315 -0
  260. package/layer/public/reports/tecentriq-has.pdf +0 -0
  261. package/layer/public/reports/yescarta-has.html +275 -0
  262. package/layer/public/reports/yescarta-has.pdf +0 -0
  263. package/layer/server/api/oip/es.post.ts +99 -0
  264. package/layer/server/api/oip/pdf.get.ts +70 -0
  265. package/layer/server/api/oip/reports.get.ts +98 -0
  266. package/layer/server/api/oip/resource-types.get.ts +22 -0
  267. package/package.json +1 -1
@@ -0,0 +1,182 @@
1
+ <!--
2
+ Corps d'une carte double : le meme essai enregistre dans CT.gov et dans
3
+ EUCT (CTIS). Colonne gauche : le corps habituel de la carte (slot). Colonne
4
+ droite : le resume de l'essai jumeau, avec son statut et le lien vers sa
5
+ fiche. Partage par les cartes NCT et EUCT.
6
+ -->
7
+ <template>
8
+ <div class="ct_pair oip-science">
9
+ <div class="ct_pair_col">
10
+ <p class="ct_pair_label">{{ kind === 'euct' ? '🇺🇸 NCT' : '🇪🇺 EUCT' }}</p>
11
+ <slot />
12
+ </div>
13
+
14
+ <div class="ct_pair_col ct_pair_col--linked">
15
+ <div class="oip_row v-center space-between gap-8 wrap">
16
+ <p class="ct_pair_label">{{ kind === 'euct' ? '🇪🇺 EUCT' : '🇺🇸 NCT' }}</p>
17
+ <div class="oip_row v-center gap-4 wrap">
18
+ <RmTag v-if="status" :variant="setColorStatus(status)">{{ status }}</RmTag>
19
+ <RmTag v-if="phase && phase !== 'Not Applicable'" variant="violet">{{ phase }}</RmTag>
20
+ </div>
21
+ </div>
22
+
23
+ <div class="oip_grid gap-12">
24
+ <OipScienceCommonTableTags
25
+ v-if="conditions.length"
26
+ :isShortFirstColumn="true"
27
+ icon="FaceMask"
28
+ :title="$t('oipScience.common.conditions')"
29
+ :values="conditions"
30
+ :highlightQueries="queries"
31
+ :isFullResults="false"
32
+ />
33
+ <OipScienceCommonTableTags
34
+ v-if="treatments.length"
35
+ :isShortFirstColumn="true"
36
+ :icon="kind === 'euct' ? 'Pill' : 'Clipboard'"
37
+ :title="kind === 'euct' ? $t('oipScience.common.products') : $t('oipScience.common.interventions')"
38
+ :values="treatments"
39
+ :highlightQueries="queries"
40
+ :isFullResults="false"
41
+ />
42
+ <OipScienceCommonTableTags
43
+ v-if="sponsors.length"
44
+ :isShortFirstColumn="true"
45
+ icon="Briefcase"
46
+ :title="$t('oipScience.common.sponsors')"
47
+ :values="sponsors"
48
+ :highlightQueries="queries"
49
+ :isFullResults="false"
50
+ />
51
+ <OipScienceCommonTableTags
52
+ v-if="enrollment != null && enrollment !== ''"
53
+ :isShortFirstColumn="true"
54
+ icon="ListBullets"
55
+ :title="$t('oipScience.cards.enrollment')"
56
+ :values="[ifNoRes(enrollment, 'number')]"
57
+ :isFullResults="false"
58
+ />
59
+ </div>
60
+
61
+ <a class="ct_pair_link" :href="nav.href(linkedType, linkedId)" @click.stop="followCardLink($event, nav, linkedType, linkedId)">
62
+ {{ kind === 'euct' ? $t('oipScience.cards.openEuctCard') : $t('oipScience.cards.openNctCard') }}
63
+ <RmIcon name="ArrowRight" :size="14" />
64
+ </a>
65
+ </div>
66
+ </div>
67
+ </template>
68
+
69
+ <script setup lang="ts">
70
+ import { ifNoRes, setColorStatus } from '../../../oip-science/utils/styleAndFormat';
71
+ import { followCardLink } from '../../../oip-science/utils/b-openCard';
72
+
73
+ const props = defineProps<{
74
+ /** Registre de l'essai jumeau, affiche dans la colonne droite. */
75
+ kind: 'euct' | 'nct';
76
+ /** L'essai jumeau, tel que le charge useLinkedTrials. */
77
+ linked: any;
78
+ queries?: any;
79
+ }>();
80
+
81
+ const uniq = (list: (string | null | undefined)[]) =>
82
+ [...new Set(list.filter((x): x is string => !!x))];
83
+
84
+ const status = computed(() =>
85
+ props.kind === 'euct' ? props.linked?.global_status : props.linked?.overall_status,
86
+ );
87
+ const phase = computed(() =>
88
+ props.kind === 'euct' ? props.linked?.trial_phase : props.linked?.phase,
89
+ );
90
+
91
+ const conditions = computed(() =>
92
+ uniq(
93
+ ((props.kind === 'euct' ? props.linked?.medical_condition : props.linked?.conditions) || []).map(
94
+ (c: any) => c?.condition_name_en,
95
+ ),
96
+ ),
97
+ );
98
+
99
+ const treatments = computed(() =>
100
+ props.kind === 'euct'
101
+ ? uniq(
102
+ (props.linked?.products || []).map(
103
+ (p: any) =>
104
+ p?.productDictionaryInfo?.prodName || p?.productDictionaryInfo?.activeSubstanceName,
105
+ ),
106
+ )
107
+ : uniq((props.linked?.intervention || []).map((i: any) => i?.intervention_name)),
108
+ );
109
+
110
+ const sponsors = computed(() =>
111
+ props.kind === 'euct'
112
+ ? uniq((props.linked?.co_sponsors || []).map((s: any) => s?.organisation?.name))
113
+ : uniq((props.linked?.sponsors || []).map((s: any) => s?.sponsor_name)),
114
+ );
115
+
116
+ const enrollment = computed(() =>
117
+ props.kind === 'euct'
118
+ ? props.linked?.enrollment_status?.total_planned_enrollment
119
+ : props.linked?.enrollment,
120
+ );
121
+
122
+ // Fiche de l'essai jumeau, par la navigation du layer (page ou tiroir).
123
+ const nav = useOipResourceNav();
124
+ const linkedType = computed(() => (props.kind === 'euct' ? 'ClinicalTrialsEUCT' : 'ClinicalTrialsNCT'));
125
+ const linkedId = computed<string>(() =>
126
+ props.kind === 'euct' ? props.linked?.euct_number : props.linked?.nct_id,
127
+ );
128
+ </script>
129
+
130
+ <style scoped>
131
+ .ct_pair {
132
+ display: grid;
133
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
134
+ gap: var(--space-16);
135
+ }
136
+
137
+ .ct_pair_col {
138
+ display: flex;
139
+ flex-direction: column;
140
+ gap: var(--space-12);
141
+ min-width: 0;
142
+ }
143
+
144
+ /* L'essai jumeau dans le violet clair du bandeau de projet : on voit que
145
+ la carte en porte deux. */
146
+ .ct_pair_col--linked {
147
+ padding: var(--space-12) var(--space-16);
148
+ border: 1px solid color-mix(in srgb, var(--rm-shell-accent) 40%, transparent);
149
+ border-radius: var(--radius-lg);
150
+ background: color-mix(in srgb, var(--rm-shell-accent) 8%, var(--rm-shell-surface));
151
+ }
152
+
153
+ .ct_pair_label {
154
+ margin: 0;
155
+ color: var(--text-muted);
156
+ font-size: var(--font-11);
157
+ font-weight: var(--weight-bold);
158
+ letter-spacing: 0.08em;
159
+ text-transform: uppercase;
160
+ }
161
+
162
+ .ct_pair_link {
163
+ display: inline-flex;
164
+ align-items: center;
165
+ gap: var(--space-4);
166
+ margin-top: auto;
167
+ color: var(--color-primary);
168
+ font-size: var(--font-13);
169
+ font-weight: var(--weight-semibold);
170
+ text-decoration: none;
171
+ }
172
+
173
+ .ct_pair_link:hover {
174
+ text-decoration: underline;
175
+ }
176
+
177
+ @media (max-width: 900px) {
178
+ .ct_pair {
179
+ grid-template-columns: minmax(0, 1fr);
180
+ }
181
+ }
182
+ </style>
@@ -0,0 +1,95 @@
1
+ <!--
2
+ Les sites et les contacts d'un essai EUCT dans un pays. Ouvert depuis le
3
+ tableau de recrutement de la fiche et depuis les pastilles pays des cartes.
4
+ `country` vaut null tant que le tiroir est ferme.
5
+
6
+ RmDrawer se teleporte dans <body> : en-tete et contenu portent eux-memes
7
+ la classe `oip-science` pour garder les classes de FAS.
8
+ -->
9
+ <template>
10
+ <RmDrawer
11
+ v-model="open"
12
+ size="min(720px, 100vw)"
13
+ :label="$t('oipScience.trial.clinicalTrialsSite')"
14
+ :close-label="$t('oipScience.trial.close')"
15
+ >
16
+ <template #header>
17
+ <div class="oip-science">
18
+ <h2 class="kk_title_medium_2">{{ $t('oipScience.trial.clinicalTrialsSite') }}</h2>
19
+ </div>
20
+ </template>
21
+
22
+ <div v-if="country" class="oip-science oip_grid gap-24">
23
+ <div class="oip_row gap-8 v-center">
24
+ <h1 class="kk_title_medium_2">{{ getCountryEmoji(country) }}</h1>
25
+ <RmTag v-if="subjects != null" variant="gray">
26
+ {{ subjects }} {{ $t('oipScience.trial.subjects') }}
27
+ </RmTag>
28
+ <RmTag variant="gray">
29
+ {{ sites.length }} {{ sites.length > 1 ? $t('oipScience.trial.sites') : $t('oipScience.trial.site') }}
30
+ </RmTag>
31
+ </div>
32
+
33
+ <div v-if="sites.length > 0" class="oip_container_gray oip_grid gap-12">
34
+ <div v-for="(site, index) in sites" :key="index">
35
+ <h1 class="oip_subtitle">{{ site.organisation_name }}</h1>
36
+ <h2 class="oip_body">
37
+ {{ [site.street_address, site.post_code, site.city].filter(Boolean).join(', ') }}
38
+ </h2>
39
+ </div>
40
+ </div>
41
+
42
+ <div v-if="hcps.length > 0" class="oip_container_gray oip_grid gap-12">
43
+ <div v-for="(hcp, index) in hcps" :key="index">
44
+ <h1 class="oip_subtitle">
45
+ {{
46
+ [hcp.title, hcp.first_name, hcp.last_name]
47
+ .filter(Boolean)
48
+ .join(' ')
49
+ .concat(hcp.department_name ? ` - ${hcp.department_name}` : '')
50
+ }}
51
+ </h1>
52
+ <a
53
+ v-if="hcp.email"
54
+ :href="`mailto:${hcp.email}`"
55
+ class="kk_link_btn_underline oip_row v-center gap-4"
56
+ >
57
+ <RmIcon name="EnvelopeSimple" :size="16" color="var(--primary)" />
58
+ <span>{{ hcp.email }}</span>
59
+ </a>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </RmDrawer>
64
+ </template>
65
+
66
+ <script setup lang="ts">
67
+ import { getCountryEmoji } from '../../../oip-science/utils/styleAndFormat';
68
+
69
+ const props = defineProps<{
70
+ /** L'essai : il porte `countries`, `sites` et `hcps`. */
71
+ trial: any;
72
+ country: string | null;
73
+ }>();
74
+
75
+ const emit = defineEmits<{ 'update:country': [value: string | null] }>();
76
+
77
+ const open = computed({
78
+ get: () => !!props.country,
79
+ set: (value) => {
80
+ if (!value) emit('update:country', null);
81
+ },
82
+ });
83
+
84
+ const sites = computed<any[]>(() =>
85
+ (props.trial?.sites || []).filter((site: any) => site.country === props.country)
86
+ );
87
+ const hcps = computed<any[]>(() =>
88
+ (props.trial?.hcps || []).filter((hcp: any) => hcp.country === props.country)
89
+ );
90
+ const subjects = computed(
91
+ () =>
92
+ (props.trial?.countries || []).find((c: any) => c.country_name === props.country)
93
+ ?.planned_number_of_subjects
94
+ );
95
+ </script>
@@ -0,0 +1,73 @@
1
+ <template>
2
+ <div class="oip-science oip_grid gap-4">
3
+ <p class="oip_underline">{{ title }}</p>
4
+
5
+ <RmTextHighlight
6
+ v-if="highlightQueries !== null"
7
+ :queries="highlightQueries"
8
+ :text="getContent"
9
+ class="oip_body"
10
+ :style="`font-size: ${kpiFontSize}px`"
11
+ :clamp="isReadMore ? 3 : 0"
12
+ :lang="$i18n.locale === 'en' ? 'en' : 'fr'"
13
+ />
14
+ <p
15
+ v-else
16
+ class="oip_body"
17
+ :class="{ isKpi: isKpi }"
18
+ :style="`font-size: ${kpiFontSize}px`"
19
+ >
20
+ <RmTextHighlight
21
+ :text="getContent"
22
+ :style="`font-size: ${kpiFontSize}px`"
23
+ :clamp="isReadMore ? 3 : 0"
24
+ :lang="$i18n.locale === 'en' ? 'en' : 'fr'"
25
+ />
26
+ </p>
27
+ </div>
28
+ </template>
29
+
30
+ <script lang="ts" setup>
31
+ const props = defineProps({
32
+ title: {
33
+ type: String,
34
+ required: true,
35
+ },
36
+ content: {
37
+ type: String,
38
+ required: true,
39
+ },
40
+ isReadMore: {
41
+ type: Boolean,
42
+ required: false,
43
+ default: false,
44
+ },
45
+ isKpi: {
46
+ type: Boolean,
47
+ required: false,
48
+ default: false,
49
+ },
50
+ kpiFontSize: {
51
+ type: Number,
52
+ required: false,
53
+ },
54
+ highlightQueries: {
55
+ type: Array,
56
+ default: null,
57
+ },
58
+ });
59
+
60
+ const activeReadMore = ref(true);
61
+ const readMore = () => {
62
+ activeReadMore.value = !activeReadMore.value;
63
+ };
64
+
65
+ const getContent = computed(() => {
66
+ // if (props.content.length > 200 && activeReadMore.value && props.isReadMore) {
67
+ // return props.content.slice(0, 200) + '...';
68
+ // } else {
69
+ // return props.content;
70
+ // }
71
+ return props.content;
72
+ });
73
+ </script>
@@ -0,0 +1,283 @@
1
+ <template>
2
+ <template v-if="dedupedMatches.length > 0">
3
+ <div
4
+ ref="matchTagRef"
5
+ class="oip-science match-avg-wrapper"
6
+ @mouseenter="onTagEnter"
7
+ @mouseleave="onTagLeave"
8
+ >
9
+ <RmTag variant="blue">
10
+ Moyenne des matchs : {{ avgScorePct }}%
11
+ </RmTag>
12
+ </div>
13
+ <Teleport to="body">
14
+ <div
15
+ v-if="tooltipOpen"
16
+ class="oip-science match-avg-tooltip-portal"
17
+ :class="{ below: tooltipBelow }"
18
+ :style="tooltipStyle"
19
+ @mouseenter="onTooltipEnter"
20
+ @mouseleave="onTooltipLeave"
21
+ >
22
+ <div class="match-avg-tooltip-header">
23
+ <p class="match-avg-tooltip-header-title">Labels matchés</p>
24
+ <span class="match-avg-tooltip-count">{{ dedupedMatches.length }}</span>
25
+ </div>
26
+ <div class="match-avg-tooltip-list">
27
+ <div
28
+ v-for="(m, i) in dedupedMatches"
29
+ :key="i"
30
+ class="match-avg-tooltip-row"
31
+ >
32
+ <span class="match-avg-tooltip-name">{{ m.name }}</span>
33
+ <span
34
+ class="match-avg-tooltip-score"
35
+ :class="(m.score || 0) >= 0.8 ? 'high' : 'low'"
36
+ >{{ Math.round((m.score || 0) * 100) }}%</span>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ </Teleport>
41
+ </template>
42
+ </template>
43
+
44
+ <script setup lang="ts">
45
+ type MatchEntry = { name: string; score: number; match_method?: string };
46
+
47
+ const props = defineProps<{
48
+ nctName?: MatchEntry[] | null;
49
+ euctName?: MatchEntry[] | null;
50
+ pubmedName?: MatchEntry[] | null;
51
+ }>();
52
+
53
+ // Aggregate match labels, dedupe by name (keep best score), sort desc.
54
+ // HCP entries may use {first_name, last_name} instead of {name}, so we derive a label.
55
+ const dedupedMatches = computed(() => {
56
+ const raw = [
57
+ ...((props.nctName as any) || []),
58
+ ...((props.euctName as any) || []),
59
+ ...((props.pubmedName as any) || []),
60
+ ];
61
+ const all = raw
62
+ .map((m: any) => ({
63
+ name:
64
+ (m?.name && String(m.name).trim()) ||
65
+ [m?.first_name, m?.last_name].filter(Boolean).join(' ').trim() ||
66
+ null,
67
+ score: m?.score,
68
+ match_method: m?.match_method,
69
+ }))
70
+ .filter((m) => m.name && typeof m.score === 'number');
71
+ const byName = new Map<string, MatchEntry>();
72
+ for (const m of all) {
73
+ const existing = byName.get(m.name as string);
74
+ if (!existing || (m.score ?? 0) > (existing.score ?? 0)) {
75
+ byName.set(m.name as string, {
76
+ name: m.name as string,
77
+ score: m.score as number,
78
+ match_method: m.match_method,
79
+ });
80
+ }
81
+ }
82
+ return [...byName.values()].sort((a, b) => (b.score ?? 0) - (a.score ?? 0));
83
+ });
84
+
85
+ const avgScorePct = computed(() => {
86
+ if (dedupedMatches.value.length === 0) return 0;
87
+ const sum = dedupedMatches.value.reduce((acc, m) => acc + (m.score || 0), 0);
88
+ return Math.round((sum / dedupedMatches.value.length) * 100);
89
+ });
90
+
91
+ // Teleported tooltip state — escapes any overflow:hidden ancestor
92
+ const matchTagRef = ref<HTMLElement | null>(null);
93
+ const tooltipOpen = ref(false);
94
+ const tooltipBelow = ref(false);
95
+ const tooltipStyle = ref<Record<string, string>>({});
96
+ let hideTimer: ReturnType<typeof setTimeout> | null = null;
97
+
98
+ const TOOLTIP_GAP = 10;
99
+ const TOOLTIP_MAX_HEIGHT = 360;
100
+
101
+ function computePosition() {
102
+ const el = matchTagRef.value;
103
+ if (!el) return;
104
+ const rect = el.getBoundingClientRect();
105
+ const spaceAbove = rect.top;
106
+ const spaceBelow = window.innerHeight - rect.bottom;
107
+ const placeBelow = spaceAbove < TOOLTIP_MAX_HEIGHT && spaceBelow > spaceAbove;
108
+ tooltipBelow.value = placeBelow;
109
+ const left = rect.left + rect.width / 2 + window.scrollX;
110
+ if (placeBelow) {
111
+ tooltipStyle.value = {
112
+ top: `${rect.bottom + window.scrollY + TOOLTIP_GAP}px`,
113
+ left: `${left}px`,
114
+ };
115
+ } else {
116
+ tooltipStyle.value = {
117
+ top: `${rect.top + window.scrollY - TOOLTIP_GAP}px`,
118
+ left: `${left}px`,
119
+ };
120
+ }
121
+ }
122
+
123
+ function onTagEnter() {
124
+ if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; }
125
+ computePosition();
126
+ tooltipOpen.value = true;
127
+ }
128
+
129
+ function onTagLeave() {
130
+ scheduleHide();
131
+ }
132
+
133
+ function onTooltipEnter() {
134
+ if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; }
135
+ }
136
+
137
+ function onTooltipLeave() {
138
+ scheduleHide();
139
+ }
140
+
141
+ function scheduleHide() {
142
+ if (hideTimer) clearTimeout(hideTimer);
143
+ hideTimer = setTimeout(() => {
144
+ tooltipOpen.value = false;
145
+ }, 120);
146
+ }
147
+
148
+ onBeforeUnmount(() => {
149
+ if (hideTimer) clearTimeout(hideTimer);
150
+ tooltipOpen.value = false;
151
+ });
152
+ </script>
153
+
154
+ <style scoped>
155
+ .match-avg-wrapper {
156
+ display: inline-block;
157
+ cursor: help;
158
+ }
159
+ </style>
160
+
161
+ <style>
162
+ .match-avg-tooltip-portal {
163
+ position: absolute;
164
+ transform: translate(-50%, -100%);
165
+ background: var(--bg-surface);
166
+ color: var(--text-primary);
167
+ border: 1px solid var(--border-default);
168
+ border-radius: 10px;
169
+ padding: 0;
170
+ font-size: 12px;
171
+ font-weight: 400;
172
+ line-height: 1.4;
173
+ width: max-content;
174
+ min-width: 280px;
175
+ max-width: 380px;
176
+ text-align: left;
177
+ z-index: 9999;
178
+ box-shadow: 0 12px 32px rgba(15, 23, 42, 0.14), 0 2px 6px rgba(15, 23, 42, 0.06);
179
+ }
180
+ .match-avg-tooltip-portal.below {
181
+ transform: translate(-50%, 0);
182
+ }
183
+ .match-avg-tooltip-portal::before {
184
+ content: '';
185
+ position: absolute;
186
+ left: 0;
187
+ right: 0;
188
+ height: 12px;
189
+ top: 100%;
190
+ }
191
+ .match-avg-tooltip-portal.below::before {
192
+ top: auto;
193
+ bottom: 100%;
194
+ }
195
+ .match-avg-tooltip-portal::after {
196
+ content: '';
197
+ position: absolute;
198
+ left: 50%;
199
+ transform: translateX(-50%);
200
+ width: 0;
201
+ height: 0;
202
+ border: 7px solid transparent;
203
+ border-top-color: #ffffff;
204
+ top: 100%;
205
+ pointer-events: none;
206
+ }
207
+ .match-avg-tooltip-portal.below::after {
208
+ top: auto;
209
+ bottom: 100%;
210
+ border-top-color: transparent;
211
+ border-bottom-color: #ffffff;
212
+ }
213
+ .match-avg-tooltip-header {
214
+ display: flex;
215
+ align-items: center;
216
+ justify-content: space-between;
217
+ gap: 12px;
218
+ padding: 10px 14px;
219
+ border-bottom: 1px solid var(--border-default);
220
+ background: var(--bg-surface-2);
221
+ border-top-left-radius: 10px;
222
+ border-top-right-radius: 10px;
223
+ }
224
+ .match-avg-tooltip-header-title {
225
+ font-size: 13px;
226
+ font-weight: 600;
227
+ color: var(--text-primary);
228
+ margin: 0;
229
+ }
230
+ .match-avg-tooltip-count {
231
+ display: inline-flex;
232
+ align-items: center;
233
+ justify-content: center;
234
+ background: var(--primary);
235
+ color: #ffffff;
236
+ font-size: 11px;
237
+ font-weight: 700;
238
+ padding: 2px 9px;
239
+ border-radius: 999px;
240
+ min-width: 24px;
241
+ font-variant-numeric: tabular-nums;
242
+ line-height: 1.4;
243
+ }
244
+ .match-avg-tooltip-list {
245
+ max-height: 280px;
246
+ overflow-y: auto;
247
+ padding: 6px 0;
248
+ }
249
+ .match-avg-tooltip-row {
250
+ display: flex;
251
+ justify-content: space-between;
252
+ gap: 12px;
253
+ align-items: center;
254
+ padding: 7px 14px;
255
+ }
256
+ .match-avg-tooltip-row + .match-avg-tooltip-row {
257
+ border-top: 1px solid var(--border-soft);
258
+ }
259
+ .match-avg-tooltip-name {
260
+ flex: 1;
261
+ white-space: normal;
262
+ word-break: break-word;
263
+ color: var(--text-secondary);
264
+ font-size: 12px;
265
+ line-height: 1.4;
266
+ }
267
+ .match-avg-tooltip-score {
268
+ font-weight: 600;
269
+ font-size: 11px;
270
+ font-variant-numeric: tabular-nums;
271
+ white-space: nowrap;
272
+ padding: 2px 8px;
273
+ border-radius: 999px;
274
+ }
275
+ .match-avg-tooltip-score.high {
276
+ background: var(--color-success-bg);
277
+ color: var(--color-success);
278
+ }
279
+ .match-avg-tooltip-score.low {
280
+ background: var(--color-warning-bg);
281
+ color: var(--color-warning);
282
+ }
283
+ </style>
@@ -0,0 +1,16 @@
1
+ <template>
2
+ <RmEmptyState class="oip-science" :title="text ?? $t('oipScience.common.noDataTitle')" :text="subText ?? $t('oipScience.common.noDataText')" />
3
+ </template>
4
+
5
+ <script lang="ts" setup>
6
+ const props = defineProps({
7
+ text: {
8
+ type: String,
9
+ default: undefined,
10
+ },
11
+ subText: {
12
+ type: String,
13
+ default: undefined,
14
+ },
15
+ });
16
+ </script>
@@ -0,0 +1,35 @@
1
+ <!--
2
+ Score de rapprochement (0..1) d'une entite reconnue (RPPS / FINESS), dans la
3
+ rangee de pastilles d'une carte. Vert a partir de 85 %, orange en dessous.
4
+ -->
5
+ <template>
6
+ <span
7
+ v-if="score != null && !isNaN(score)"
8
+ class="oip-science score-badge"
9
+ :class="score >= 0.85 ? 'score-high' : 'score-low'"
10
+ :data-tooltip="$t('oipScience.common.matchingScoreTooltip')"
11
+ >{{ $t('oipScience.common.confidenceScore') }} · {{ Math.round(score * 100) }}%</span>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ defineProps<{ score?: number | null }>();
16
+ </script>
17
+
18
+ <style scoped>
19
+ .score-badge {
20
+ display: inline-block;
21
+ padding: 2px 8px;
22
+ border-radius: 12px;
23
+ font-size: 12px;
24
+ font-weight: 600;
25
+ cursor: help;
26
+ }
27
+ .score-high {
28
+ background: var(--color-success-bg);
29
+ color: var(--color-success);
30
+ }
31
+ .score-low {
32
+ background: var(--color-warning-bg);
33
+ color: var(--color-warning);
34
+ }
35
+ </style>