@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,461 @@
1
+ <!--
2
+ Un pays du regard international : ses prix publies, puis ses decisions.
3
+
4
+ La carte reprend la lecture de l'onglet Prix France : le prix d'abord, en
5
+ cartes de presentation, puis la liste des decisions en tableau. Elle ne
6
+ calcule rien : la page lui donne des lignes de prix deja converties et des
7
+ decisions deja normalisees, elle les met en page et signale ce qu'on ouvre.
8
+ -->
9
+ <script setup lang="ts">
10
+ import type { HeartIntlPriceRow } from "../../../../../oip-drugs/utils/heartIntlPrice";
11
+ import type { HeartIntlRecord } from "../../../../../oip-drugs/composables/useHeartIntlApprovals";
12
+
13
+ const props = defineProps<{
14
+ /** Code ISO du pays (ou « EU » pour l'Union europeenne). */
15
+ iso: string;
16
+ /** Nom du pays, dans la langue lue. */
17
+ label: string;
18
+ /** L'agence dont viennent les decisions : EMA, G-BA, AIFA... */
19
+ agency: string;
20
+ /** Les presentations tarifees du pays. Vide pour l'Union europeenne. */
21
+ prices: HeartIntlPriceRow[];
22
+ /** Les decisions du registre, deja normalisees. */
23
+ records: HeartIntlRecord[];
24
+ /** Fiche Directus du referentiel de prix du pays. */
25
+ priceSource?: string | null;
26
+ /** Fiche Directus du registre d'autorisation du pays. */
27
+ approvalSource?: string | null;
28
+ loading?: boolean;
29
+ }>();
30
+
31
+ const emit = defineEmits<{
32
+ (event: "select-price", row: HeartIntlPriceRow): void;
33
+ (event: "select-record", record: HeartIntlRecord): void;
34
+ }>();
35
+
36
+ const { locale } = useI18n();
37
+ const en = computed(() => locale.value === "en");
38
+
39
+ const money = (value: number | null | undefined, currency = "EUR") =>
40
+ typeof value === "number"
41
+ ? `${Math.round(value).toLocaleString("fr-FR")} ${currency === "EUR" ? "€" : currency}`
42
+ : "-";
43
+
44
+ const percent = (value: number | null | undefined) =>
45
+ typeof value === "number"
46
+ ? `${value > 0 ? "+" : ""}${value.toLocaleString("fr-FR", { maximumFractionDigits: 1 })} %`
47
+ : "";
48
+
49
+ /** La derniere date connue du pays, prix et decisions confondus. */
50
+ const lastUpdate = computed(() => {
51
+ const dates = [
52
+ ...props.prices.map((row) => row.date),
53
+ ...props.records.map((record) => record.date),
54
+ ]
55
+ .filter(Boolean)
56
+ .sort((a: any, b: any) => new Date(b).getTime() - new Date(a).getTime());
57
+ return dates[0] ?? null;
58
+ });
59
+
60
+ /** Tous les registres ne rendent pas un verdict : l'AIFA classe, le G-BA juge. */
61
+ const hasOutcome = computed(() => props.records.some((record) => record.status));
62
+
63
+ /** La date du dernier tarif du pays, citee par sa source. */
64
+ const lastPriceDates = computed(() => props.prices.map((row) => row.date));
65
+
66
+ const columns = computed(() => [
67
+ { key: "date", label: "Date", minWidth: "110px", rowHeader: true },
68
+ { key: "typeLabel", label: en.value ? "Type" : "Nature", minWidth: "150px" },
69
+ { key: "title", label: en.value ? "Subject" : "Objet", minWidth: "260px" },
70
+ ...(hasOutcome.value
71
+ ? [{ key: "status", label: en.value ? "Outcome" : "Résultat", minWidth: "130px" }]
72
+ : []),
73
+ ]);
74
+
75
+ /** La teinte de l'etiquette de resultat. Romulus n'a pas de rouge : rose. */
76
+ const tone = (record: HeartIntlRecord) =>
77
+ record.statusTone === "good" ? "green" : record.statusTone === "bad" ? "rose" : "gray";
78
+ </script>
79
+
80
+ <template>
81
+ <article class="ic">
82
+ <header class="ic_head">
83
+ <RmFlag :code="iso" :height="18" :title="label" />
84
+ <h3 class="ic_country">{{ label }}</h3>
85
+ <RmTag variant="gray">{{ agency }}</RmTag>
86
+ <span class="ic_counts">
87
+ <template v-if="prices.length">
88
+ {{ prices.length }}
89
+ {{ en ? (prices.length > 1 ? "presentations" : "presentation") : "présentation" + (prices.length > 1 ? "s" : "") }}
90
+ </template>
91
+ <template v-if="prices.length && records.length"> · </template>
92
+ <template v-if="records.length">
93
+ {{ records.length }}
94
+ {{ en ? (records.length > 1 ? "decisions" : "decision") : "décision" + (records.length > 1 ? "s" : "") }}
95
+ </template>
96
+ </span>
97
+ <span v-if="lastUpdate" class="ic_last">
98
+ <RmIcon name="Calendar" :size="14" />
99
+ <RmDate :date="lastUpdate" :lang="en ? 'en' : 'fr'" compact />
100
+ </span>
101
+ </header>
102
+
103
+ <!-- Bandeau prix : une carte par presentation tarifee, le prix en grand. -->
104
+ <section v-if="prices.length" class="ic_band">
105
+ <p class="ic_band_title">
106
+ {{ en ? "Published prices" : "Prix publiés" }}
107
+ <span class="ic_band_sub">
108
+ {{
109
+ en
110
+ ? "latest applicable price, converted to euros"
111
+ : "dernier prix applicable, converti en euros"
112
+ }}
113
+ </span>
114
+ </p>
115
+
116
+ <!-- Une marque peut porter vingt presentations dans un pays : le bandeau
117
+ defile horizontalement plutot que d'empiler des rangees ou de cacher
118
+ le reste derriere un bouton. -->
119
+ <div
120
+ class="ic_scroller"
121
+ tabindex="0"
122
+ role="group"
123
+ :aria-label="en ? `Published prices in ${label}` : `Prix publiés en ${label}`"
124
+ >
125
+ <button
126
+ v-for="row in prices"
127
+ :key="row.key"
128
+ type="button"
129
+ class="ic_price_card"
130
+ @click="emit('select-price', row)"
131
+ >
132
+ <span class="ic_price_head">
133
+ <span class="ic_price_name">{{ row.product }}</span>
134
+ <RmTag v-if="row.category" variant="gray">{{ row.category }}</RmTag>
135
+ </span>
136
+
137
+ <span v-if="row.date" class="ic_price_date">
138
+ <RmIcon name="Calendar" :size="13" />
139
+ {{ en ? "Price of" : "Tarif au" }}
140
+ <RmDate :date="row.date" :lang="en ? 'en' : 'fr'" compact />
141
+ </span>
142
+
143
+ <span class="ic_price_value">
144
+ {{ money(row.priceEur) }}
145
+ <span class="ic_price_type">{{ row.typeLabel }}</span>
146
+ </span>
147
+
148
+ <span class="ic_price_fields">
149
+ <span class="ic_field">
150
+ <span class="ic_field_label">{{ en ? "Local price" : "Prix local" }}</span>
151
+ <span class="ic_field_value">{{ money(row.priceLocal, row.currency) }}</span>
152
+ </span>
153
+ <span v-if="row.variation" class="ic_field">
154
+ <span class="ic_field_label">{{ en ? "Last change" : "Dernière variation" }}</span>
155
+ <span class="ic_field_value" :class="row.variation < 0 ? 'is_down' : 'is_up'">
156
+ <RmIcon :name="row.variation < 0 ? 'TrendDown' : 'TrendUp'" :size="13" />
157
+ {{ money(row.variation, row.currency) }}
158
+ <template v-if="row.variationPct !== null">({{ percent(row.variationPct) }})</template>
159
+ </span>
160
+ </span>
161
+ <span v-if="row.manufacturer" class="ic_field">
162
+ <span class="ic_field_label">{{ en ? "Manufacturer" : "Fabricant" }}</span>
163
+ <span class="ic_field_value">{{ row.manufacturer }}</span>
164
+ </span>
165
+ <span v-if="row.localId" class="ic_field">
166
+ <span class="ic_field_label">{{ en ? "Local code" : "Code local" }}</span>
167
+ <span class="ic_field_value ic_mono">{{ row.localId }}</span>
168
+ </span>
169
+ </span>
170
+ </button>
171
+ </div>
172
+
173
+ </section>
174
+
175
+ <!-- Decisions du registre : indications, avis, autorisations. -->
176
+ <section class="ic_records">
177
+ <p class="ic_band_title">
178
+ {{ en ? "Indications and decisions" : "Indications et décisions" }}
179
+ <span class="ic_band_sub">{{ agency }}</span>
180
+ </p>
181
+
182
+ <RmTable
183
+ v-if="records.length"
184
+ :columns="columns"
185
+ :rows="records"
186
+ row-key="key"
187
+ compact
188
+ flat
189
+ :page-size="5"
190
+ max-height="auto"
191
+ :caption="`${label} · ${agency}`"
192
+ :empty-text="en ? 'No decision' : 'Aucune décision'"
193
+ @row-click="(row: any) => emit('select-record', row as HeartIntlRecord)"
194
+ >
195
+ <template #date="{ row }">
196
+ <RmDate v-if="row.date" :date="row.date" :lang="en ? 'en' : 'fr'" compact />
197
+ <span v-else>-</span>
198
+ </template>
199
+
200
+ <template #typeLabel="{ row }">
201
+ <span class="ic_type">
202
+ <RmIcon :name="row.icon" :size="14" />
203
+ {{ row.typeLabel }}
204
+ </span>
205
+ <span v-if="row.badge" class="ic_ref">{{ row.badge }}</span>
206
+ </template>
207
+
208
+ <template #title="{ row }">
209
+ <span class="ic_record_title">{{ row.title }}</span>
210
+ <span v-if="row.excerpt" class="ic_record_excerpt">{{ row.excerpt }}</span>
211
+ </template>
212
+
213
+ <template #status="{ row }">
214
+ <RmTag v-if="row.status" :variant="tone(row as HeartIntlRecord)">{{ row.status }}</RmTag>
215
+ <span v-else>-</span>
216
+ </template>
217
+ </RmTable>
218
+
219
+ <p v-else-if="loading" class="ic_empty">
220
+ {{ en ? "Loading decisions…" : "Chargement des décisions…" }}
221
+ </p>
222
+ <p v-else class="ic_empty">
223
+ {{ en ? `No decision published by ${agency}.` : `Aucune décision publiée par ${agency}.` }}
224
+ </p>
225
+ </section>
226
+
227
+ <!-- Sources du pays, en pied de carte comme sur les autres pages. -->
228
+ <footer v-if="priceSource || approvalSource" class="ic_sources">
229
+ <oip-drugs-heart-elastic-source-container-elastic
230
+ v-if="priceSource"
231
+ :uuid="priceSource"
232
+ prefer-nickname
233
+ :kind="approvalSource ? (en ? 'Price source' : 'Source prix') : undefined"
234
+ :last-label="$t('oipDrugs.heart.sources.lastPrice')"
235
+ :last-date="lastPriceDates"
236
+ />
237
+ <oip-drugs-heart-elastic-source-container-elastic
238
+ v-if="approvalSource"
239
+ :uuid="approvalSource"
240
+ prefer-nickname
241
+ :kind="en ? 'Approvals source' : 'Source décisions'"
242
+ />
243
+ </footer>
244
+ </article>
245
+ </template>
246
+
247
+ <style scoped>
248
+ .ic {
249
+ display: flex;
250
+ flex-direction: column;
251
+ gap: 16px;
252
+ width: 100%;
253
+ background: var(--white);
254
+ border: 1px solid var(--light, #ececf2);
255
+ border-radius: 14px;
256
+ padding: 20px;
257
+ }
258
+
259
+ .ic_head {
260
+ display: flex;
261
+ align-items: center;
262
+ flex-wrap: wrap;
263
+ gap: 10px;
264
+ padding-bottom: 12px;
265
+ border-bottom: 1px solid var(--light, #ececf2);
266
+ }
267
+
268
+ .ic_country {
269
+ font-size: 16px;
270
+ font-weight: 700;
271
+ color: var(--darkest);
272
+ }
273
+
274
+ .ic_counts,
275
+ .ic_last {
276
+ font-size: 12px;
277
+ color: var(--medium);
278
+ }
279
+
280
+ .ic_last {
281
+ display: inline-flex;
282
+ align-items: center;
283
+ gap: 4px;
284
+ margin-left: auto;
285
+ }
286
+
287
+ .ic_band_title {
288
+ display: flex;
289
+ align-items: baseline;
290
+ gap: 8px;
291
+ font-size: 13px;
292
+ font-weight: 700;
293
+ color: var(--darkest);
294
+ margin-bottom: 10px;
295
+ }
296
+
297
+ .ic_band_sub {
298
+ font-size: 11px;
299
+ font-weight: 500;
300
+ color: var(--medium);
301
+ }
302
+
303
+ .ic_scroller {
304
+ display: flex;
305
+ gap: 12px;
306
+ overflow-x: auto;
307
+ padding-bottom: 6px;
308
+ scroll-snap-type: x proximity;
309
+ scrollbar-width: thin;
310
+ }
311
+
312
+ .ic_scroller:focus-visible {
313
+ outline: 2px solid var(--primary);
314
+ outline-offset: 2px;
315
+ }
316
+
317
+ .ic_price_card {
318
+ flex: 0 0 260px;
319
+ scroll-snap-align: start;
320
+ display: flex;
321
+ flex-direction: column;
322
+ gap: 8px;
323
+ text-align: left;
324
+ font-family: inherit;
325
+ background: var(--lightest, #f7f7fb);
326
+ border: 1px solid var(--light, #ececf2);
327
+ border-radius: 12px;
328
+ padding: 14px;
329
+ cursor: pointer;
330
+ transition: border-color 0.15s, box-shadow 0.15s;
331
+ }
332
+
333
+ .ic_price_card:hover {
334
+ border-color: var(--primary);
335
+ box-shadow: 0 2px 10px rgb(0 0 0 / 6%);
336
+ }
337
+
338
+ .ic_price_head {
339
+ display: flex;
340
+ align-items: flex-start;
341
+ justify-content: space-between;
342
+ gap: 8px;
343
+ }
344
+
345
+ .ic_price_name {
346
+ font-size: 12px;
347
+ font-weight: 700;
348
+ color: var(--darkest);
349
+ line-height: 1.3;
350
+ }
351
+
352
+ .ic_price_date {
353
+ display: inline-flex;
354
+ align-items: center;
355
+ gap: 4px;
356
+ font-size: 11px;
357
+ color: var(--medium);
358
+ }
359
+
360
+ .ic_price_value {
361
+ display: flex;
362
+ align-items: baseline;
363
+ gap: 8px;
364
+ font-family: var(--font-serif, inherit);
365
+ font-size: 24px;
366
+ font-weight: 700;
367
+ color: var(--primary);
368
+ }
369
+
370
+ .ic_price_type {
371
+ font-family: inherit;
372
+ font-size: 11px;
373
+ font-weight: 600;
374
+ color: var(--medium);
375
+ }
376
+
377
+ .ic_price_fields {
378
+ display: grid;
379
+ grid-template-columns: 1fr 1fr;
380
+ gap: 8px 12px;
381
+ padding-top: 8px;
382
+ border-top: 1px solid var(--light, #ececf2);
383
+ }
384
+
385
+ .ic_field {
386
+ display: flex;
387
+ flex-direction: column;
388
+ gap: 2px;
389
+ min-width: 0;
390
+ }
391
+
392
+ .ic_field_label {
393
+ font-size: 10px;
394
+ font-weight: 600;
395
+ letter-spacing: 0.02em;
396
+ text-transform: uppercase;
397
+ color: var(--medium);
398
+ }
399
+
400
+ .ic_field_value {
401
+ display: inline-flex;
402
+ align-items: center;
403
+ gap: 4px;
404
+ font-size: 12px;
405
+ font-weight: 600;
406
+ color: var(--darkest);
407
+ overflow-wrap: anywhere;
408
+ }
409
+
410
+ .ic_field_value.is_down {
411
+ color: var(--success, #1a7f5a);
412
+ }
413
+
414
+ .ic_field_value.is_up {
415
+ color: var(--danger, #c0392b);
416
+ }
417
+
418
+ .ic_mono {
419
+ font-variant-numeric: tabular-nums;
420
+ font-weight: 500;
421
+ }
422
+
423
+ .ic_type {
424
+ display: inline-flex;
425
+ align-items: center;
426
+ gap: 6px;
427
+ white-space: nowrap;
428
+ }
429
+
430
+ .ic_ref {
431
+ display: block;
432
+ font-size: 10px;
433
+ color: var(--medium);
434
+ white-space: nowrap;
435
+ }
436
+
437
+ .ic_record_title {
438
+ display: block;
439
+ font-weight: 600;
440
+ color: var(--darkest);
441
+ }
442
+
443
+ .ic_record_excerpt {
444
+ display: block;
445
+ font-size: 11px;
446
+ color: var(--medium);
447
+ }
448
+
449
+ .ic_empty {
450
+ font-size: 12px;
451
+ color: var(--medium);
452
+ }
453
+
454
+ .ic_sources {
455
+ display: flex;
456
+ flex-direction: column;
457
+ gap: 4px;
458
+ padding-top: 12px;
459
+ border-top: 1px solid var(--light, #ececf2);
460
+ }
461
+ </style>
@@ -0,0 +1,200 @@
1
+ <!--
2
+ Regard international sur une marque : une carte par pays.
3
+
4
+ Chaque carte porte ce que le pays publie sur le produit : ses prix d'abord
5
+ (suivi mondial `ope__ww_price_tracker__tekkare`, converti en euros), puis ses
6
+ decisions (AIFA, AEMPS, G-BA, MHRA, EMA, FDA). Ouvrir une presentation ou une
7
+ decision ouvre le meme tiroir que les autres onglets de la fiche.
8
+
9
+ Deux precautions valent pour toute la lecture :
10
+
11
+ - Les conditionnements ne sont pas harmonises d'un pays a l'autre (un flacon
12
+ ici, deux la, 50 mg ailleurs) : on compare des presentations, jamais des
13
+ pays entre eux.
14
+ - Chaque pays publie ses propres types de prix. Le prix montre suit un ordre
15
+ fixe (fabricant, grossiste, public...) pour porter la meme notion partout
16
+ ou elle existe.
17
+ -->
18
+ <script setup lang="ts">
19
+ import * as currencyModule from "../../../../../oip-drugs/composables/useCurrency";
20
+ import { HEART_INTL_PRICE_CURRENCY, heartIntlPriceRows } from "../../../../../oip-drugs/utils/heartIntlPrice";
21
+ import type { HeartIntlPriceRow } from "../../../../../oip-drugs/utils/heartIntlPrice";
22
+ import type { HeartIntlRecord } from "../../../../../oip-drugs/composables/useHeartIntlApprovals";
23
+ import { HEART_INTL_COUNTRIES } from "../../../../../oip-drugs/composables/useHeartIntlApprovals";
24
+ import { heartFormatDate } from "../../../../../oip-drugs/utils/heartStyleAndFormat";
25
+
26
+ const props = defineProps<{
27
+ /** Les documents du suivi mondial des prix pour la marque. */
28
+ docs: any[];
29
+ /** Les decisions des registres etrangers, par code pays. */
30
+ records: Record<string, HeartIntlRecord[]>;
31
+ /** Nom de la marque, pour la ligne de portee. */
32
+ brand?: string;
33
+ /** Les registres sont encore en vol. */
34
+ loading?: boolean;
35
+ }>();
36
+
37
+ const { locale } = useI18n();
38
+ const en = computed(() => locale.value === "en");
39
+
40
+ // Ex-CurrencyStore (lot P) : le composable charge lui-meme les taux, avec
41
+ // la meme API (`getCurrency(code)`). Export nomme ou par defaut, selon le
42
+ // portage du socle.
43
+ const useCurrency = ((currencyModule as any).useCurrency ?? (currencyModule as any).default) as () => any;
44
+ const currencyStore = useCurrency();
45
+
46
+ /** Taux de la devise locale vers l'euro, charge par useCurrency. */
47
+ function eurRate(iso: string) {
48
+ const code = HEART_INTL_PRICE_CURRENCY[iso];
49
+ if (!code || code === "EUR") return 1;
50
+ return currencyStore.getCurrency(code)?.eur_rate ?? null;
51
+ }
52
+
53
+ const priceRows = computed(() => heartIntlPriceRows(props.docs ?? [], eurRate));
54
+
55
+ const pricesByCountry = computed(() => {
56
+ const groups: Record<string, HeartIntlPriceRow[]> = {};
57
+ for (const row of priceRows.value) {
58
+ (groups[row.iso] ??= []).push(row);
59
+ }
60
+ return groups;
61
+ });
62
+
63
+ /** Une carte par pays qui a quelque chose a dire. */
64
+ const countries = computed(() =>
65
+ HEART_INTL_COUNTRIES.map((country) => ({
66
+ iso: country.iso,
67
+ key: country.key,
68
+ agency: country.agency,
69
+ label: en.value ? country.en : country.fr,
70
+ prices: pricesByCountry.value[country.key] ?? [],
71
+ records: props.records?.[country.key] ?? [],
72
+ priceSource: country.priceSource,
73
+ approvalSource: country.approvalSource,
74
+ })).filter((country) => country.prices.length > 0 || country.records.length > 0),
75
+ );
76
+
77
+ const decisionCount = computed(() =>
78
+ countries.value.reduce((sum, country) => sum + country.records.length, 0),
79
+ );
80
+
81
+ const lastUpdate = computed(() => {
82
+ const dates = [
83
+ ...priceRows.value.map((row) => row.date),
84
+ ...countries.value.flatMap((country) => country.records.map((record) => record.date)),
85
+ ]
86
+ .filter(Boolean)
87
+ .sort((a: any, b: any) => new Date(b).getTime() - new Date(a).getTime());
88
+ return dates[0] ?? null;
89
+ });
90
+
91
+ const kpis = computed(() => [
92
+ {
93
+ icon: "GlobeHemisphereWest",
94
+ value: countries.value.length,
95
+ legend: en.value ? "Countries covered" : "Pays couverts",
96
+ },
97
+ {
98
+ icon: "Package",
99
+ value: priceRows.value.length,
100
+ legend: en.value ? "Priced presentations" : "Présentations tarifées",
101
+ },
102
+ {
103
+ icon: "Stamp",
104
+ value: decisionCount.value,
105
+ legend: en.value ? "Decisions" : "Décisions",
106
+ },
107
+ {
108
+ icon: "Calendar",
109
+ value: lastUpdate.value ? heartFormatDate(lastUpdate.value) : "-",
110
+ legend: en.value ? "Latest update" : "Dernière mise à jour",
111
+ },
112
+ ]);
113
+
114
+ /** La decision ouverte dans le tiroir des enregistrements. */
115
+ const drawerRecord = ref<HeartIntlRecord | null>(null);
116
+
117
+ function openRecord(record: HeartIntlRecord) {
118
+ drawerRecord.value = record;
119
+ }
120
+
121
+ /** La presentation ouverte dans le tiroir des prix. */
122
+ const drawerPrice = ref<HeartIntlPriceRow | null>(null);
123
+
124
+ /** Son pays, nomme comme la carte le nomme. */
125
+ const drawerPriceCountry = computed(
126
+ () => countries.value.find((country) => country.key === drawerPrice.value?.iso)?.label,
127
+ );
128
+
129
+ </script>
130
+
131
+ <template>
132
+ <section class="io">
133
+ <RmKpiBar :kpis="kpis" :loading="loading && countries.length === 0" />
134
+
135
+ <oip-drugs-heart-results-details-international-country-card
136
+ v-for="country in countries"
137
+ :key="country.key"
138
+ :iso="country.iso"
139
+ :label="country.label"
140
+ :agency="country.agency"
141
+ :prices="country.prices"
142
+ :records="country.records"
143
+ :price-source="country.priceSource"
144
+ :approval-source="country.approvalSource"
145
+ :loading="loading"
146
+ @select-price="drawerPrice = $event"
147
+ @select-record="openRecord"
148
+ />
149
+
150
+ <p v-if="!countries.length && !loading" class="io_none">
151
+ <RmIcon name="Info" :size="16" />
152
+ {{
153
+ en
154
+ ? `Nothing published abroad for ${brand ?? ""} in the countries covered.`
155
+ : `Rien de publié à l'étranger pour ${brand ?? ""} dans les pays couverts.`
156
+ }}
157
+ </p>
158
+
159
+ <!-- Une decision s'ouvre dans le tiroir commun aux autres onglets. -->
160
+ <oip-drugs-heart-results-details-record-drawer
161
+ :record="drawerRecord"
162
+ :source-label="drawerRecord?.sourceLabel"
163
+ @close="drawerRecord = null"
164
+ />
165
+
166
+ <!-- Une presentation a son propre tiroir : elle porte un historique de
167
+ prix, que les rubriques de texte du tiroir commun ne rendaient pas. -->
168
+ <oip-drugs-heart-results-details-international-price-drawer
169
+ :row="drawerPrice"
170
+ :country-label="drawerPriceCountry"
171
+ @close="drawerPrice = null"
172
+ />
173
+ </section>
174
+ </template>
175
+
176
+ <style scoped>
177
+ .io {
178
+ display: flex;
179
+ flex-direction: column;
180
+ gap: 16px;
181
+ width: 100%;
182
+ }
183
+
184
+ .io_none {
185
+ display: flex;
186
+ align-items: center;
187
+ gap: 8px;
188
+ font-size: 13px;
189
+ color: var(--dark);
190
+ }
191
+
192
+ .io_none {
193
+ padding: 24px 0;
194
+ }
195
+
196
+
197
+
198
+
199
+
200
+ </style>