@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.
Files changed (265) hide show
  1. package/dist/module.json +1 -1
  2. package/layer/app/app.config.ts +6 -0
  3. package/layer/app/components/OipPubmedSummary.vue +223 -0
  4. package/layer/app/components/OipResourceDrawer.vue +331 -0
  5. package/layer/app/components/OipResourceDrawerLink.vue +40 -0
  6. package/layer/app/components/OipResourceField.vue +41 -0
  7. package/layer/app/components/OipResourceGenericCard.vue +54 -0
  8. package/layer/app/components/OipResourceSource.vue +50 -0
  9. package/layer/app/components/OipResourceTags.vue +65 -0
  10. package/layer/app/components/OipResourceView.vue +107 -0
  11. package/layer/app/components/oip-drugs/fiches/AssociationFiche.vue +775 -0
  12. package/layer/app/components/oip-drugs/fiches/BrandFiche.vue +1111 -0
  13. package/layer/app/components/oip-drugs/fiches/IndicationFiche.vue +182 -0
  14. package/layer/app/components/oip-drugs/fiches/JoFiche.vue +101 -0
  15. package/layer/app/components/oip-drugs/fiches/PndsFiche.vue +279 -0
  16. package/layer/app/components/oip-drugs/heart/JoCard.vue +314 -0
  17. package/layer/app/components/oip-drugs/heart/TextHighlight.vue +326 -0
  18. package/layer/app/components/oip-drugs/heart/ansm/BrandCard.vue +187 -0
  19. package/layer/app/components/oip-drugs/heart/ansm/CisDetail.vue +363 -0
  20. package/layer/app/components/oip-drugs/heart/ansm/CisTable.vue +167 -0
  21. package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyCard.vue +373 -0
  22. package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyFiche.vue +372 -0
  23. package/layer/app/components/oip-drugs/heart/associations/TranscriptionCard.vue +55 -0
  24. package/layer/app/components/oip-drugs/heart/elastic/SourceContainerElastic.vue +103 -0
  25. package/layer/app/components/oip-drugs/heart/france/BrandGroupCard.vue +450 -0
  26. package/layer/app/components/oip-drugs/heart/mitm/Card.vue +199 -0
  27. package/layer/app/components/oip-drugs/heart/mitm/Source.vue +23 -0
  28. package/layer/app/components/oip-drugs/heart/mitm/details/Overview.vue +381 -0
  29. package/layer/app/components/oip-drugs/heart/put/Card.vue +212 -0
  30. package/layer/app/components/oip-drugs/heart/results/AccesCard.vue +696 -0
  31. package/layer/app/components/oip-drugs/heart/results/AlertCard.vue +342 -0
  32. package/layer/app/components/oip-drugs/heart/results/AsmrCursor.vue +229 -0
  33. package/layer/app/components/oip-drugs/heart/results/EvalCard.vue +370 -0
  34. package/layer/app/components/oip-drugs/heart/results/IndicCard.vue +743 -0
  35. package/layer/app/components/oip-drugs/heart/results/MatchExplain.vue +243 -0
  36. package/layer/app/components/oip-drugs/heart/results/PrefRows.vue +105 -0
  37. package/layer/app/components/oip-drugs/heart/results/details/HasHistory.vue +487 -0
  38. package/layer/app/components/oip-drugs/heart/results/details/InternationalCountryCard.vue +461 -0
  39. package/layer/app/components/oip-drugs/heart/results/details/InternationalOverview.vue +200 -0
  40. package/layer/app/components/oip-drugs/heart/results/details/InternationalPriceDrawer.vue +647 -0
  41. package/layer/app/components/oip-drugs/heart/results/details/Monograph.vue +128 -0
  42. package/layer/app/components/oip-drugs/heart/results/details/OpinionDrawer.vue +535 -0
  43. package/layer/app/components/oip-drugs/heart/results/details/PresentationDrawer.vue +738 -0
  44. package/layer/app/components/oip-drugs/heart/results/details/Price.vue +227 -0
  45. package/layer/app/components/oip-drugs/heart/results/details/PriceOverview.vue +1261 -0
  46. package/layer/app/components/oip-drugs/heart/results/details/RecordDrawer.vue +494 -0
  47. package/layer/app/components/oip-drugs/heart/results/details/RecordList.vue +426 -0
  48. package/layer/app/components/oip-drugs/heart/results/les/Card.vue +340 -0
  49. package/layer/app/components/oip-drugs/heart/results/les/Drawer.vue +67 -0
  50. package/layer/app/components/oip-drugs/heart/results/les/IndicationDetail.vue +149 -0
  51. package/layer/app/components/oip-drugs/heart/results/les/Source.vue +20 -0
  52. package/layer/app/components/oip-drugs/heart/results/les/SourceTag.vue +39 -0
  53. package/layer/app/components/oip-drugs/heart/searchPage/DynamicSource.vue +29 -0
  54. package/layer/app/components/oip-drugs/medicam/market/Chart.vue +121 -0
  55. package/layer/app/components/oip-drugs/outpatient/ProductEvolution.vue +236 -0
  56. package/layer/app/components/oip-drugs/resources/Ansm.vue +75 -0
  57. package/layer/app/components/oip-drugs/resources/Association.vue +30 -0
  58. package/layer/app/components/oip-drugs/resources/Brand.vue +65 -0
  59. package/layer/app/components/oip-drugs/resources/Contribution.vue +216 -0
  60. package/layer/app/components/oip-drugs/resources/Hospital.vue +151 -0
  61. package/layer/app/components/oip-drugs/resources/Indication.vue +54 -0
  62. package/layer/app/components/oip-drugs/resources/Jo.vue +57 -0
  63. package/layer/app/components/oip-drugs/resources/Medicam.vue +140 -0
  64. package/layer/app/components/oip-drugs/resources/Mitm.vue +84 -0
  65. package/layer/app/components/oip-drugs/resources/NationalAssembly.vue +93 -0
  66. package/layer/app/components/oip-drugs/resources/Outpatient.vue +97 -0
  67. package/layer/app/components/oip-drugs/resources/Pnds.vue +24 -0
  68. package/layer/app/components/oip-drugs/resources/Price.vue +106 -0
  69. package/layer/app/components/oip-drugs/resources/Put.vue +143 -0
  70. package/layer/app/components/oip-science/appel-a-projet/Cards.vue +162 -0
  71. package/layer/app/components/oip-science/clinical-trials/Cards.vue +382 -0
  72. package/layer/app/components/oip-science/clinical-trials/CardsGlobal.vue +273 -0
  73. package/layer/app/components/oip-science/clinical-trials/LinkedList.vue +366 -0
  74. package/layer/app/components/oip-science/clinical-trials/PairBadge.vue +25 -0
  75. package/layer/app/components/oip-science/clinical-trials/PairBody.vue +182 -0
  76. package/layer/app/components/oip-science/clinical-trials/SitesDrawer.vue +95 -0
  77. package/layer/app/components/oip-science/common/Form.vue +73 -0
  78. package/layer/app/components/oip-science/common/MatchAvgTag.vue +283 -0
  79. package/layer/app/components/oip-science/common/NoData.vue +16 -0
  80. package/layer/app/components/oip-science/common/ScoreBadge.vue +35 -0
  81. package/layer/app/components/oip-science/common/Source.vue +31 -0
  82. package/layer/app/components/oip-science/common/TableTags.vue +279 -0
  83. package/layer/app/components/oip-science/common/Timeline.vue +104 -0
  84. package/layer/app/components/oip-science/common/ToolTips.vue +89 -0
  85. package/layer/app/components/oip-science/cross/publicationPubmed.vue +484 -0
  86. package/layer/app/components/oip-science/crossData/CrossDataLocations.vue +71 -0
  87. package/layer/app/components/oip-science/ctgov/Investigators.vue +148 -0
  88. package/layer/app/components/oip-science/ctgov/Locations.vue +102 -0
  89. package/layer/app/components/oip-science/ctgov/Publications.vue +52 -0
  90. package/layer/app/components/oip-science/ctgov/Sponsors.vue +66 -0
  91. package/layer/app/components/oip-science/ctgov/StudyDescription.vue +1136 -0
  92. package/layer/app/components/oip-science/ctgov-centers/ClinicalTrials.vue +40 -0
  93. package/layer/app/components/oip-science/ctgov-centers/Publications.vue +50 -0
  94. package/layer/app/components/oip-science/ctgov-investigators/ClinicalTrials.vue +43 -0
  95. package/layer/app/components/oip-science/ctgov-investigators/Informations.vue +171 -0
  96. package/layer/app/components/oip-science/ctgov-investigators/Publications.vue +44 -0
  97. package/layer/app/components/oip-science/elastic/DateRangeFilter.vue +209 -0
  98. package/layer/app/components/oip-science/elastic/Filter.vue +719 -0
  99. package/layer/app/components/oip-science/elastic/MatchExplain.vue +152 -0
  100. package/layer/app/components/oip-science/establishments/Cards.vue +183 -0
  101. package/layer/app/components/oip-science/establishments/ClinicalTrials.vue +62 -0
  102. package/layer/app/components/oip-science/establishments/Hcps.vue +227 -0
  103. package/layer/app/components/oip-science/establishments/Informations.vue +263 -0
  104. package/layer/app/components/oip-science/establishments/LinkedList.vue +153 -0
  105. package/layer/app/components/oip-science/establishments/MatchedLabels.vue +194 -0
  106. package/layer/app/components/oip-science/establishments/PublicFinance.vue +62 -0
  107. package/layer/app/components/oip-science/euct/Investigators.vue +125 -0
  108. package/layer/app/components/oip-science/euct/Locations.vue +102 -0
  109. package/layer/app/components/oip-science/euct/Products.vue +101 -0
  110. package/layer/app/components/oip-science/euct/Publications.vue +89 -0
  111. package/layer/app/components/oip-science/euct/Sponsors.vue +86 -0
  112. package/layer/app/components/oip-science/euct/StudyDescription.vue +1075 -0
  113. package/layer/app/components/oip-science/euct-centers/ClinicalTrials.vue +40 -0
  114. package/layer/app/components/oip-science/euct-centers/Publications.vue +55 -0
  115. package/layer/app/components/oip-science/euct-investigators/ClinicalTrials.vue +41 -0
  116. package/layer/app/components/oip-science/euct-investigators/Informations.vue +171 -0
  117. package/layer/app/components/oip-science/euct-investigators/Publications.vue +51 -0
  118. package/layer/app/components/oip-science/hcps/AppelsAProjet.vue +62 -0
  119. package/layer/app/components/oip-science/hcps/Cards.vue +237 -0
  120. package/layer/app/components/oip-science/hcps/ClinicalTrials.vue +78 -0
  121. package/layer/app/components/oip-science/hcps/Informations.vue +296 -0
  122. package/layer/app/components/oip-science/hcps/LinkedList.vue +153 -0
  123. package/layer/app/components/oip-science/hcps/MatchedLabels.vue +207 -0
  124. package/layer/app/components/oip-science/hcps/Pubmed.vue +128 -0
  125. package/layer/app/components/oip-science/local/SliderTabs.vue +99 -0
  126. package/layer/app/components/oip-science/local/SliderTabsFull.vue +135 -0
  127. package/layer/app/components/oip-science/pubmed/Authors.vue +128 -0
  128. package/layer/app/components/oip-science/pubmed/Cards.vue +417 -0
  129. package/layer/app/components/oip-science/pubmed/ClinicalTrials.vue +71 -0
  130. package/layer/app/components/oip-science/resources/Phrc.vue +39 -0
  131. package/layer/app/composables/useOipChapterScroll.ts +43 -0
  132. package/layer/app/composables/useOipResourceHooks.ts +27 -0
  133. package/layer/app/composables/useOipResourceNav.ts +66 -0
  134. package/layer/app/composables/useOipResourceViews.ts +117 -0
  135. package/layer/app/composables/useOipResources.ts +83 -0
  136. package/layer/app/oip-drugs/PORTING.md +120 -0
  137. package/layer/app/oip-drugs/composables/useAssociationDetail.ts +220 -0
  138. package/layer/app/oip-drugs/composables/useAssociationExport.ts +63 -0
  139. package/layer/app/oip-drugs/composables/useAssociationLookup.ts +105 -0
  140. package/layer/app/oip-drugs/composables/useCardPrefOptions.ts +201 -0
  141. package/layer/app/oip-drugs/composables/useCurrency.ts +121 -0
  142. package/layer/app/oip-drugs/composables/useDirectusSources.ts +224 -0
  143. package/layer/app/oip-drugs/composables/useHeartBrandMatches.ts +112 -0
  144. package/layer/app/oip-drugs/composables/useHeartDetailSearches.ts +479 -0
  145. package/layer/app/oip-drugs/composables/useHeartIntlApprovals.ts +661 -0
  146. package/layer/app/oip-drugs/composables/useHeartLesIndication.ts +168 -0
  147. package/layer/app/oip-drugs/composables/useHeartMatchExplain.ts +231 -0
  148. package/layer/app/oip-drugs/composables/useHeartReports.ts +239 -0
  149. package/layer/app/oip-drugs/composables/useHeartSearchFields.ts +233 -0
  150. package/layer/app/oip-drugs/composables/useMergeByKey.ts +23 -0
  151. package/layer/app/oip-drugs/composables/useOipDrugsElastic.ts +621 -0
  152. package/layer/app/oip-drugs/composables/usePageEmpty.ts +58 -0
  153. package/layer/app/oip-drugs/composables/v-useSalesSource.ts +64 -0
  154. package/layer/app/oip-drugs/drugs.css +500 -0
  155. package/layer/app/oip-drugs/esIndices.ts +322 -0
  156. package/layer/app/oip-drugs/kinds.ts +264 -0
  157. package/layer/app/oip-drugs/locales/en.json +1571 -0
  158. package/layer/app/oip-drugs/locales/fr.json +1571 -0
  159. package/layer/app/oip-drugs/monographs/blincyto-has.vue +289 -0
  160. package/layer/app/oip-drugs/monographs/darzalex-has.vue +276 -0
  161. package/layer/app/oip-drugs/monographs/dupixent-has.vue +216 -0
  162. package/layer/app/oip-drugs/monographs/enhertu-has.vue +280 -0
  163. package/layer/app/oip-drugs/monographs/has-sein-2023.vue +1649 -0
  164. package/layer/app/oip-drugs/monographs/ibrance-has.vue +277 -0
  165. package/layer/app/oip-drugs/monographs/imfinzi-has.vue +293 -0
  166. package/layer/app/oip-drugs/monographs/keytruda-has.vue +334 -0
  167. package/layer/app/oip-drugs/monographs/kisqali-has.vue +201 -0
  168. package/layer/app/oip-drugs/monographs/opdivo-has.vue +231 -0
  169. package/layer/app/oip-drugs/monographs/ozempic-has.vue +186 -0
  170. package/layer/app/oip-drugs/monographs/tecentriq-has.vue +312 -0
  171. package/layer/app/oip-drugs/monographs/yescarta-has.vue +272 -0
  172. package/layer/app/oip-drugs/types/elastic.ts +113 -0
  173. package/layer/app/oip-drugs/types/es/agencies.ts +152 -0
  174. package/layer/app/oip-drugs/types/es/directory.ts +54 -0
  175. package/layer/app/oip-drugs/types/es/docByIndex.ts +151 -0
  176. package/layer/app/oip-drugs/types/es/openmedic.ts +67 -0
  177. package/layer/app/oip-drugs/types/es/outpatient.ts +71 -0
  178. package/layer/app/oip-drugs/types/es/portal.ts +130 -0
  179. package/layer/app/oip-drugs/types/es/retail.ts +107 -0
  180. package/layer/app/oip-drugs/types/es/statistics.ts +29 -0
  181. package/layer/app/oip-drugs/types/heart/CurrencyRate.ts +16 -0
  182. package/layer/app/oip-drugs/types/heart/ansm.ts +106 -0
  183. package/layer/app/oip-drugs/types/heart/tag.ts +29 -0
  184. package/layer/app/oip-drugs/utils/associationFields.ts +118 -0
  185. package/layer/app/oip-drugs/utils/contributions.ts +36 -0
  186. package/layer/app/oip-drugs/utils/countryMapping.ts +387 -0
  187. package/layer/app/oip-drugs/utils/esHighlight.ts +21 -0
  188. package/layer/app/oip-drugs/utils/exportSheets.ts +57 -0
  189. package/layer/app/oip-drugs/utils/heartIntlPrice.ts +228 -0
  190. package/layer/app/oip-drugs/utils/heartStyleAndFormat.ts +262 -0
  191. package/layer/app/oip-drugs/utils/heartStyleAnsm.ts +71 -0
  192. package/layer/app/oip-drugs/utils/latestDate.ts +31 -0
  193. package/layer/app/oip-drugs/utils/p-heartCardTags.ts +53 -0
  194. package/layer/app/oip-drugs/utils/priceExport.ts +269 -0
  195. package/layer/app/oip-drugs/utils/q-hasRatings.ts +43 -0
  196. package/layer/app/oip-drugs/utils/s-openCard.ts +20 -0
  197. package/layer/app/oip-drugs/utils/sourceUuids.ts +155 -0
  198. package/layer/app/oip-drugs/utils/styleAndFormat.ts +40 -0
  199. package/layer/app/oip-drugs/utils/v-salesSeries.ts +156 -0
  200. package/layer/app/oip-drugs/utils/v-scopeTags.ts +23 -0
  201. package/layer/app/oip-science/PORTING.md +93 -0
  202. package/layer/app/oip-science/assets/smartSelection.json +428 -0
  203. package/layer/app/oip-science/composables/useAuthorMatching.ts +342 -0
  204. package/layer/app/oip-science/composables/useCTCenter.ts +107 -0
  205. package/layer/app/oip-science/composables/useCTInvestigators.ts +107 -0
  206. package/layer/app/oip-science/composables/useEstablishmentCrossData.ts +139 -0
  207. package/layer/app/oip-science/composables/useEstablishmentMatching.ts +176 -0
  208. package/layer/app/oip-science/composables/useEuct.ts +418 -0
  209. package/layer/app/oip-science/composables/useEuctCenter.ts +107 -0
  210. package/layer/app/oip-science/composables/useEuctInvestigators.ts +107 -0
  211. package/layer/app/oip-science/composables/useGlobalClinicalTrials.ts +624 -0
  212. package/layer/app/oip-science/composables/useLinkedTrials.ts +83 -0
  213. package/layer/app/oip-science/composables/useOipElastic.ts +658 -0
  214. package/layer/app/oip-science/composables/useOipSearchExplain.ts +166 -0
  215. package/layer/app/oip-science/composables/usePubmed.ts +487 -0
  216. package/layer/app/oip-science/composables/useSearchFields.ts +153 -0
  217. package/layer/app/oip-science/indexConfig.json +794 -0
  218. package/layer/app/oip-science/locales/en.json +483 -0
  219. package/layer/app/oip-science/locales/fr.json +483 -0
  220. package/layer/app/oip-science/science.css +1182 -0
  221. package/layer/app/oip-science/utils/a-openCard.ts +23 -0
  222. package/layer/app/oip-science/utils/b-openCard.ts +35 -0
  223. package/layer/app/oip-science/utils/c-filterContext.ts +81 -0
  224. package/layer/app/oip-science/utils/f-types.ts +241 -0
  225. package/layer/app/oip-science/utils/frDiseaseTerms.ts +269 -0
  226. package/layer/app/oip-science/utils/matchMethod.ts +53 -0
  227. package/layer/app/oip-science/utils/sliderTabs.ts +13 -0
  228. package/layer/app/oip-science/utils/sources.ts +15 -0
  229. package/layer/app/oip-science/utils/styleAndFormat.ts +608 -0
  230. package/layer/app/oip-science/utils/toPresets.ts +7 -0
  231. package/layer/app/oip-science/utils/valueTable.ts +126 -0
  232. package/layer/app/plugins/oip-science-i18n.ts +17 -0
  233. package/layer/app/utils/oipFicheRoutes.ts +118 -0
  234. package/layer/app/utils/oipResources.ts +440 -0
  235. package/layer/nuxt.config.ts +22 -2
  236. package/layer/public/reports/blincyto-has.html +292 -0
  237. package/layer/public/reports/blincyto-has.pdf +0 -0
  238. package/layer/public/reports/darzalex-has.html +279 -0
  239. package/layer/public/reports/darzalex-has.pdf +0 -0
  240. package/layer/public/reports/dupixent-has.html +217 -0
  241. package/layer/public/reports/dupixent-has.pdf +0 -0
  242. package/layer/public/reports/enhertu-has.html +283 -0
  243. package/layer/public/reports/enhertu-has.pdf +0 -0
  244. package/layer/public/reports/has-sein-2023.html +1639 -0
  245. package/layer/public/reports/ibrance-has.html +280 -0
  246. package/layer/public/reports/ibrance-has.pdf +0 -0
  247. package/layer/public/reports/imfinzi-has.html +297 -0
  248. package/layer/public/reports/imfinzi-has.pdf +0 -0
  249. package/layer/public/reports/keytruda-has.html +337 -0
  250. package/layer/public/reports/keytruda-has.pdf +0 -0
  251. package/layer/public/reports/kisqali-has.html +202 -0
  252. package/layer/public/reports/kisqali-has.pdf +0 -0
  253. package/layer/public/reports/opdivo-has.html +232 -0
  254. package/layer/public/reports/opdivo-has.pdf +0 -0
  255. package/layer/public/reports/ozempic-has.html +188 -0
  256. package/layer/public/reports/ozempic-has.pdf +0 -0
  257. package/layer/public/reports/tecentriq-has.html +315 -0
  258. package/layer/public/reports/tecentriq-has.pdf +0 -0
  259. package/layer/public/reports/yescarta-has.html +275 -0
  260. package/layer/public/reports/yescarta-has.pdf +0 -0
  261. package/layer/server/api/oip/es.post.ts +99 -0
  262. package/layer/server/api/oip/pdf.get.ts +70 -0
  263. package/layer/server/api/oip/reports.get.ts +98 -0
  264. package/layer/server/api/oip/resource-types.get.ts +22 -0
  265. package/package.json +1 -1
@@ -0,0 +1,128 @@
1
+ <!--
2
+ Monographie reglementaire d'une marque (portage de
3
+ heart/results/details/monograph.vue de fr_drugs_app).
4
+
5
+ Chaque monographie est un composant de `app/oip-drugs/monographs/`, charge a
6
+ la demande. Le bouton PDF rejoint la barre d'outils de la page
7
+ (`#oip-rail-pdf`) quand elle existe ; dans un tiroir, ou sans rail, il reste
8
+ en tete du document.
9
+ -->
10
+ <script setup lang="ts">
11
+ import { OIP_RESOURCE_NAV } from "../../../../../composables/useOipResourceNav";
12
+
13
+ const props = defineProps<{
14
+ /** Identifiant du document, sans extension (« keytruda-has »). */
15
+ id: string;
16
+ /** PDF equivalent, propose en tete de page quand il existe. */
17
+ pdf?: string | null;
18
+ }>();
19
+
20
+ const { locale } = useI18n();
21
+
22
+ // Un import statique chargerait les treize documents pour n'en montrer qu'un.
23
+ const modules = import.meta.glob("../../../../../oip-drugs/monographs/*.vue");
24
+
25
+ const view = computed(() => {
26
+ const loader = modules[`../../../../../oip-drugs/monographs/${props.id}.vue`];
27
+ return loader ? defineAsyncComponent(loader as any) : null;
28
+ });
29
+
30
+ // Le tiroir d'un projet fournit sa navigation : la fiche y est embarquee, le
31
+ // rail de la page dessous n'est pas le sien.
32
+ const embedded = inject(OIP_RESOURCE_NAV, null) !== null;
33
+
34
+ /** Le rail de la page porte-t-il l'emplacement du PDF ? */
35
+ const railTarget = ref(false);
36
+ onMounted(() => {
37
+ railTarget.value = !embedded && !!document.getElementById("oip-rail-pdf");
38
+ });
39
+ </script>
40
+
41
+ <template>
42
+ <section class="mg oip-drugs">
43
+ <!-- Le telechargement est une commande de page : il rejoint l'export et les
44
+ scopes dans la barre d'outils, plutot que de flotter sur le document. -->
45
+ <Teleport v-if="pdf && railTarget" to="#oip-rail-pdf" defer>
46
+ <a
47
+ class="mg_pdf"
48
+ :href="pdf"
49
+ download
50
+ target="_blank"
51
+ rel="noopener noreferrer"
52
+ :title="locale === 'en' ? 'Download the PDF' : 'Télécharger le PDF'"
53
+ >
54
+ <RmIcon name="FilePdf" :size="18" />
55
+ </a>
56
+ </Teleport>
57
+ <div v-else-if="pdf" class="mg_inline">
58
+ <a
59
+ class="mg_pdf"
60
+ :href="pdf"
61
+ download
62
+ target="_blank"
63
+ rel="noopener noreferrer"
64
+ :title="locale === 'en' ? 'Download the PDF' : 'Télécharger le PDF'"
65
+ >
66
+ <RmIcon name="FilePdf" :size="18" />
67
+ </a>
68
+ </div>
69
+
70
+ <component :is="view" v-if="view" />
71
+
72
+ <p v-else class="mg_empty">
73
+ {{ locale === "en" ? "Monograph unavailable." : "Monographie indisponible." }}
74
+ </p>
75
+ </section>
76
+ </template>
77
+
78
+ <style scoped>
79
+ .mg {
80
+ box-sizing: border-box;
81
+ width: 100%;
82
+ padding-inline: 32px;
83
+ }
84
+
85
+ /* Hors rail, le bouton se pose en tete du document, a droite. */
86
+ .mg_inline {
87
+ display: flex;
88
+ justify-content: flex-end;
89
+ padding-block: 8px;
90
+ }
91
+
92
+ /* Meme tuile que l'export et les scopes du rail. */
93
+ .mg_pdf {
94
+ display: flex;
95
+ align-items: center;
96
+ justify-content: center;
97
+ width: 32px;
98
+ height: 32px;
99
+ border-radius: 8px;
100
+ background: var(--rm-shell-subtle);
101
+ color: var(--rm-shell-text-dark);
102
+ transition: background-color 0.15s linear;
103
+ }
104
+
105
+ .mg_pdf:hover {
106
+ background: var(--rm-shell-border);
107
+ }
108
+
109
+ /* Le bandeau du document portait une image de fond distante, opaque et bornee
110
+ a la colonne de lecture : un degrade leger, etale d'un bord a l'autre de la
111
+ zone de contenu, laisse le titre respirer. */
112
+ .mg :deep(.hero),
113
+ .mg :deep(.cover) {
114
+ margin-inline: -32px;
115
+ padding-inline: 32px;
116
+ background: linear-gradient(
117
+ 115deg,
118
+ rgba(77, 204, 247, 0.12),
119
+ rgba(160, 107, 245, 0.14) 45%,
120
+ rgba(255, 92, 240, 0.1)
121
+ );
122
+ }
123
+
124
+ .mg_empty {
125
+ color: var(--medium);
126
+ font-size: 14px;
127
+ }
128
+ </style>
@@ -0,0 +1,535 @@
1
+ <!--
2
+ Tiroir d'un avis HAS.
3
+
4
+ Ouvrir un avis ne doit pas faire perdre sa place dans l'historique : le detail
5
+ arrive par la droite, au-dessus de la liste, et se referme au clic ailleurs ou
6
+ sur Echap. Les rubriques de l'avis (indication, population cible, strategie
7
+ therapeutique...) deviennent des onglets — celles qui n'ont rien a dire ne
8
+ s'affichent pas.
9
+ -->
10
+ <script setup lang="ts">
11
+ import { hasRatingTags } from "../../../../../oip-drugs/utils/q-hasRatings";
12
+ const props = defineProps<{
13
+ /** L'avis a detailler, ou null quand le tiroir est ferme. */
14
+ opinion: any | null;
15
+ /** Date de derniere verification de la source, telle qu'affichee en pied. */
16
+ checkedAt?: string | null;
17
+ /** Termes de la recherche qui a mene a la fiche, surlignes dans l'avis. */
18
+ searchTerms?: string[];
19
+ }>();
20
+
21
+ const emit = defineEmits<{ (event: "close"): void }>();
22
+
23
+ const { locale } = useI18n();
24
+
25
+ const AWAITING_TRANSLATION = /^\s*translation coming soon/i;
26
+
27
+ function text(field: string) {
28
+ const opinion = props.opinion;
29
+ if (!opinion) return "";
30
+ const candidates =
31
+ locale.value === "en" ? [`${field}_en`, field] : [field, `${field}_en`];
32
+ for (const key of candidates) {
33
+ const value = opinion[key];
34
+ if (typeof value === "string" && value.trim() && !AWAITING_TRANSLATION.test(value)) {
35
+ return value.trim();
36
+ }
37
+ }
38
+ return "";
39
+ }
40
+
41
+ const typeLabel = computed(() => {
42
+ if (props.opinion?.opinion_type === "early_access_decision") {
43
+ return locale.value === "en" ? "Early access" : "Accès précoce";
44
+ }
45
+ if (props.opinion?.opinion_type === "eval") {
46
+ return locale.value === "en" ? "Assessment" : "Évaluation";
47
+ }
48
+ return locale.value === "en" ? "Opinion" : "Avis";
49
+ });
50
+
51
+ const title = computed(
52
+ () =>
53
+ text("disease_name") ||
54
+ text("relevant_indication") ||
55
+ text("indication") ||
56
+ typeLabel.value,
57
+ );
58
+
59
+ const ratings = computed(() => hasRatingTags(props.opinion, locale.value));
60
+
61
+ const status = computed(() => {
62
+ const value = text("opinion_status");
63
+ return value ? value.charAt(0).toUpperCase() + value.slice(1) : "";
64
+ });
65
+
66
+ /** Rubriques disponibles pour cet avis : celles qui portent un texte. */
67
+ const sections = computed(() =>
68
+ [
69
+ {
70
+ key: "essential",
71
+ label: locale.value === "en" ? "Key point" : "Essentiel",
72
+ body: text("recommendation") || text("conclusion") || text("essential"),
73
+ },
74
+ {
75
+ key: "indication",
76
+ label: "Indication",
77
+ body: text("indication") || text("relevant_indication"),
78
+ },
79
+ {
80
+ key: "population",
81
+ label: locale.value === "en" ? "Target population" : "Population cible",
82
+ body: text("target_population") || text("syn_target_pop"),
83
+ },
84
+ {
85
+ key: "strategy",
86
+ label: locale.value === "en" ? "Therapeutic strategy" : "Stratégie thérapeutique",
87
+ body: text("therapeutic_strategy_place"),
88
+ },
89
+ {
90
+ key: "conclusion",
91
+ label: locale.value === "en" ? "Conclusion" : "Conclusion",
92
+ body: text("conclusion"),
93
+ },
94
+ ].filter((section) => section.body),
95
+ );
96
+
97
+ const active = ref(0);
98
+
99
+ // Un autre avis s'ouvre : on revient sur la premiere rubrique, sinon le tiroir
100
+ // s'ouvre sur un onglet qui n'existe pas pour lui.
101
+ watch(() => props.opinion, () => (active.value = 0));
102
+
103
+ function links(field: string): string[] {
104
+ const value = props.opinion?.[field];
105
+ return Array.isArray(value) ? value.filter(Boolean) : [];
106
+ }
107
+
108
+ /**
109
+ * Le nom du document, tire de la fin de l'URL : le prefixe technique (le
110
+ * numero de fiche HAS, le code de l'avis) ne dit rien au lecteur.
111
+ */
112
+ function docName(url: string) {
113
+ const last = decodeURIComponent(
114
+ (url.split("?")[0] ?? "").replace(/\/+$/, "").split("/").pop() ?? "",
115
+ );
116
+ const name = (last.replace(/\.pdf$/i, "").split("__").pop() ?? "")
117
+ .replace(/[-_]+/g, " ")
118
+ .trim();
119
+ return name ? name.charAt(0).toUpperCase() + name.slice(1) : url;
120
+ }
121
+
122
+ /** Deux copies du meme document ne font qu'une ligne : le nom sert de cle. */
123
+ function unique(urls: string[]) {
124
+ const seen = new Set<string>();
125
+ return urls.filter((url) => {
126
+ const key = docName(url).toLowerCase().replace(/[^a-z0-9]/g, "");
127
+ if (seen.has(key)) return false;
128
+ seen.add(key);
129
+ return true;
130
+ });
131
+ }
132
+
133
+ /**
134
+ * Tous les documents de l'avis, ranges par origine : nos copies d'un cote (des
135
+ * PDF, servis meme quand le site de la HAS ne repond pas), les pages de la HAS
136
+ * de l'autre. Chaque famille melange l'avis lui-meme, sa synthese, la
137
+ * transcription du debat et les contributions d'associations.
138
+ */
139
+ const documentGroups = computed(() => {
140
+ const groups = [
141
+ {
142
+ key: "tekkare",
143
+ label: locale.value === "en" ? "Tekkare copies" : "Copies Tekkare",
144
+ format: "pdf",
145
+ action: "preview" as const,
146
+ urls: unique([...links("tekkare_info_links"), ...links("tekkare_contribution_links")]),
147
+ },
148
+ {
149
+ key: "has",
150
+ label: locale.value === "en" ? "HAS website" : "Site de la HAS",
151
+ format: "",
152
+ action: "open" as const,
153
+ urls: unique([
154
+ ...links("has_info_links"),
155
+ ...links("has_contribution_links"),
156
+ ...links("info_links"),
157
+ ]),
158
+ },
159
+ ];
160
+ return groups.filter((group) => group.urls.length);
161
+ });
162
+
163
+ const documentCount = computed(() =>
164
+ documentGroups.value.reduce((total, group) => total + group.urls.length, 0),
165
+ );
166
+
167
+ /**
168
+ * Nos copies s'ouvrent en apercu, dans la meme visionneuse que les documents
169
+ * du rail : ce sont des PDF, et `/api/oip/pdf` les sert comme tels. Les liens de
170
+ * la HAS pointent une fiche de son site, pas un fichier : eux partent dans un
171
+ * onglet.
172
+ */
173
+ const previewOpen = ref(false);
174
+ const previewSrc = ref<string | null>(null);
175
+ const previewExternal = ref<string | null>(null);
176
+ const previewTitle = ref("");
177
+
178
+ function openDocument(group: { key: string }, url: string) {
179
+ if (group.key !== "tekkare") {
180
+ window.open(url, "_blank", "noopener");
181
+ return;
182
+ }
183
+ previewSrc.value = `/api/oip/pdf?url=${encodeURIComponent(url)}`;
184
+ previewExternal.value = url;
185
+ previewTitle.value = docName(url);
186
+ previewOpen.value = true;
187
+ }
188
+
189
+ function onKeydown(event: KeyboardEvent) {
190
+ // L'apercu se ferme en premier : sans ce garde, Echap refermait les deux
191
+ // tiroirs d'un coup et on ressortait de l'avis qu'on lisait.
192
+ if (event.key === "Escape" && !previewOpen.value) emit("close");
193
+ }
194
+
195
+ onMounted(() => document.addEventListener("keydown", onKeydown));
196
+ onBeforeUnmount(() => document.removeEventListener("keydown", onKeydown));
197
+ </script>
198
+
199
+ <template>
200
+ <!-- Pose dans `body` : un ancetre du contenu de page etablit un bloc conteneur
201
+ pour les elements `fixed`, et le voile s'arretait au bord de la colonne
202
+ au lieu de couvrir le cadre entier. -->
203
+ <Teleport to="body">
204
+ <Transition name="drawer">
205
+ <div v-if="opinion" class="odr oip-drugs" @click.self="emit('close')">
206
+ <aside class="odr_panel">
207
+ <header class="odr_head">
208
+ <p class="odr_type">
209
+ <RmIcon
210
+ :name="opinion.opinion_type === 'early_access_decision' ? 'Lightning' : 'FileText'"
211
+ :size="16"
212
+ />
213
+ {{ typeLabel }}
214
+ </p>
215
+ <button
216
+ type="button"
217
+ class="odr_close"
218
+ :aria-label="locale === 'en' ? 'Close' : 'Fermer'"
219
+ @click="emit('close')"
220
+ >
221
+ <RmIcon name="X" :size="16" />
222
+ </button>
223
+ </header>
224
+
225
+ <div class="odr_body">
226
+ <p class="odr_date"><RmDate :date="opinion.decision_date" :lang="locale" /></p>
227
+ <h2 class="odr_title">
228
+ <OipDrugsHeartTextHighlight :text="title ?? ''" :queries="searchTerms ?? []" />
229
+ </h2>
230
+
231
+ <div v-if="ratings.length" class="odr_ratings">
232
+ <RmTag v-for="tag in ratings" :key="tag.key" :variant="tag.variant">
233
+ {{ tag.label }}
234
+ </RmTag>
235
+ </div>
236
+
237
+ <div v-if="sections.length > 1" class="odr_tabs">
238
+ <button
239
+ v-for="(section, index) in sections"
240
+ :key="section.key"
241
+ type="button"
242
+ class="odr_tab"
243
+ :class="{ is_active: index === active }"
244
+ @click="active = index"
245
+ >
246
+ {{ section.label }}
247
+ </button>
248
+ </div>
249
+
250
+ <p v-if="status" class="odr_status">{{ status }}</p>
251
+
252
+ <p class="odr_text">
253
+ <OipDrugsHeartTextHighlight :text="sections[active]?.body ?? ''" :queries="searchTerms ?? []" />
254
+ </p>
255
+
256
+ <!-- Les documents ne suivent pas les onglets : ils valent pour
257
+ l'avis entier, et les chercher rubrique par rubrique revenait
258
+ a ne pas les trouver. -->
259
+ <section v-if="documentCount" class="odr_docs">
260
+ <h3 class="odr_docs_title">
261
+ Documents
262
+ <span class="odr_docs_count">{{ documentCount }}</span>
263
+ </h3>
264
+
265
+ <div v-for="group in documentGroups" :key="group.key" class="odr_docs_group">
266
+ <p class="odr_docs_label">{{ group.label }}</p>
267
+ <RmFileRow
268
+ v-for="url in group.urls"
269
+ :key="url"
270
+ :name="docName(url)"
271
+ :format="group.format"
272
+ :action="group.action"
273
+ @select="openDocument(group, url)"
274
+ />
275
+ </div>
276
+ </section>
277
+ </div>
278
+
279
+ <footer class="odr_foot">
280
+ <span>
281
+ Source :
282
+ <a
283
+ v-if="opinion.has_url"
284
+ :href="opinion.has_url"
285
+ target="_blank"
286
+ rel="noopener noreferrer"
287
+ class="odr_foot_link"
288
+ >HAS</a
289
+ >
290
+ <b v-else>HAS</b>
291
+ </span>
292
+ <span v-if="checkedAt">
293
+ ·
294
+ {{ locale === "en" ? "Last updated" : "Dernière mise à jour le" }}
295
+ <RmDate :date="checkedAt" :lang="locale" />
296
+ </span>
297
+ </footer>
298
+ </aside>
299
+
300
+ <!-- Le voile de l'avis couvre le cadre entier : l'apercu se pose
301
+ au-dessus de lui, sinon il s'ouvre derriere. -->
302
+ <OipDocumentPreview
303
+ v-model="previewOpen"
304
+ :src="previewSrc"
305
+ :title="previewTitle"
306
+ :external-src="previewExternal"
307
+ :download-src="previewExternal"
308
+ :z-index="320"
309
+ />
310
+ </div>
311
+ </Transition>
312
+ </Teleport>
313
+ </template>
314
+
315
+ <style scoped>
316
+ /* Le voile epouse le cadre de la coque (marge de 8px, coins arrondis) plutot
317
+ que la fenetre : sinon il deborde sur la bordure du cadre. */
318
+ .odr {
319
+ position: fixed;
320
+ inset: var(--rm-shell-inset, 8px);
321
+ border-radius: var(--rm-shell-radius, 12px);
322
+ overflow: hidden;
323
+ z-index: 300;
324
+ display: flex;
325
+ justify-content: flex-end;
326
+ background: rgba(30, 41, 59, 0.35);
327
+ -webkit-backdrop-filter: blur(2px);
328
+ backdrop-filter: blur(2px);
329
+ }
330
+
331
+ .odr_panel {
332
+ display: flex;
333
+ flex-direction: column;
334
+ /* Les textes d'avis font plusieurs paragraphes : a 560px, chaque ligne etait
335
+ trop courte pour se lire d'une traite. */
336
+ width: min(760px, 100%);
337
+ height: 100%;
338
+ background: var(--white);
339
+ box-shadow: var(--overlay-shadow);
340
+ }
341
+
342
+ .odr_head {
343
+ display: flex;
344
+ align-items: center;
345
+ justify-content: space-between;
346
+ gap: 16px;
347
+ padding: 16px 20px;
348
+ border-bottom: 1px solid var(--light);
349
+ }
350
+
351
+ .odr_type {
352
+ display: flex;
353
+ align-items: center;
354
+ gap: 8px;
355
+ color: var(--primary);
356
+ font-size: 15px;
357
+ font-weight: 700;
358
+ }
359
+
360
+ .odr_close {
361
+ display: flex;
362
+ align-items: center;
363
+ justify-content: center;
364
+ width: 32px;
365
+ height: 32px;
366
+ border: 1px solid var(--light);
367
+ border-radius: 8px;
368
+ background: var(--white);
369
+ color: var(--dark);
370
+ cursor: pointer;
371
+ }
372
+
373
+ .odr_close:hover {
374
+ background: var(--lightest);
375
+ }
376
+
377
+ .odr_body {
378
+ flex: 1 1 auto;
379
+ overflow-y: auto;
380
+ display: flex;
381
+ flex-direction: column;
382
+ gap: 12px;
383
+ padding: 20px;
384
+ }
385
+
386
+ /* Le corps est une colonne flex qui defile : sans ce garde, le texte de l'avis
387
+ ecrase les blocs au-dessus de lui — le titre tombait litteralement a zero de
388
+ haut et disparaissait. */
389
+ .odr_date,
390
+ .odr_title,
391
+ .odr_ratings,
392
+ .odr_tabs,
393
+ .odr_status,
394
+ .odr_docs {
395
+ flex: 0 0 auto;
396
+ }
397
+
398
+ .odr_date {
399
+ color: var(--medium);
400
+ font-size: 14px;
401
+ }
402
+
403
+ /* Meme borne que sur la carte : le titre peut etre une indication entiere.
404
+ Le `-webkit-box` du clamp ne tient pas ici — une regle exterieure impose
405
+ `display: flow-root` aux titres et la boite tombait a zero de haut — donc on
406
+ borne a deux interlignes, ce qui ne depend d'aucun mode d'affichage. */
407
+ .odr_title {
408
+ max-height: 2.6em;
409
+ overflow: hidden;
410
+ font-size: 20px;
411
+ font-weight: 800;
412
+ line-height: 1.3;
413
+ }
414
+
415
+ .odr_ratings {
416
+ display: flex;
417
+ flex-wrap: wrap;
418
+ gap: 6px;
419
+ }
420
+
421
+ .odr_tabs {
422
+ display: flex;
423
+ flex-wrap: wrap;
424
+ gap: 4px;
425
+ border-bottom: 1px solid var(--light);
426
+ }
427
+
428
+ .odr_tab {
429
+ padding: 8px 12px;
430
+ border: none;
431
+ border-bottom: 2px solid transparent;
432
+ background: none;
433
+ color: var(--medium);
434
+ font-size: 14px;
435
+ font-weight: 600;
436
+ cursor: pointer;
437
+ }
438
+
439
+ .odr_tab.is_active {
440
+ border-bottom-color: var(--primary);
441
+ background: color-mix(in srgb, var(--primary) 6%, transparent);
442
+ color: var(--primary);
443
+ }
444
+
445
+ .odr_status {
446
+ align-self: flex-start;
447
+ padding: 2px 8px;
448
+ border-radius: 4px;
449
+ background: rgba(34, 197, 94, 0.14);
450
+ color: var(--system-success-hover);
451
+ font-size: 13px;
452
+ font-weight: 600;
453
+ }
454
+
455
+ .odr_text {
456
+ color: var(--dark);
457
+ font-size: 15px;
458
+ line-height: 1.6;
459
+ white-space: pre-line;
460
+ }
461
+
462
+ .odr_docs {
463
+ display: flex;
464
+ flex-direction: column;
465
+ gap: 12px;
466
+ margin-top: 8px;
467
+ padding-top: 16px;
468
+ border-top: 1px solid var(--light);
469
+ }
470
+
471
+ .odr_docs_title {
472
+ display: flex;
473
+ align-items: center;
474
+ gap: 8px;
475
+ font-size: 15px;
476
+ font-weight: 800;
477
+ }
478
+
479
+ .odr_docs_count {
480
+ padding: 1px 8px;
481
+ border-radius: 999px;
482
+ background: var(--lightest);
483
+ color: var(--medium);
484
+ font-size: 12px;
485
+ font-weight: 700;
486
+ }
487
+
488
+ .odr_docs_group {
489
+ display: flex;
490
+ flex-direction: column;
491
+ gap: 2px;
492
+ }
493
+
494
+ .odr_docs_label {
495
+ color: var(--medium);
496
+ font-size: 13px;
497
+ font-weight: 700;
498
+ }
499
+
500
+ .odr_foot {
501
+ display: flex;
502
+ align-items: center;
503
+ gap: 6px;
504
+ padding: 14px 20px;
505
+ border-top: 1px solid var(--light);
506
+ background: var(--demi-fond);
507
+ color: var(--medium);
508
+ font-size: 13px;
509
+ }
510
+
511
+ .odr_foot_link {
512
+ color: var(--primary);
513
+ font-weight: 700;
514
+ }
515
+
516
+ .drawer-enter-active,
517
+ .drawer-leave-active {
518
+ transition: opacity 0.2s ease;
519
+ }
520
+
521
+ .drawer-enter-active .odr_panel,
522
+ .drawer-leave-active .odr_panel {
523
+ transition: transform 0.2s ease;
524
+ }
525
+
526
+ .drawer-enter-from,
527
+ .drawer-leave-to {
528
+ opacity: 0;
529
+ }
530
+
531
+ .drawer-enter-from .odr_panel,
532
+ .drawer-leave-to .odr_panel {
533
+ transform: translateX(24px);
534
+ }
535
+ </style>