@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,314 @@
1
+ <!--
2
+ Une publication du Journal Officiel pour une presentation (CIP/UCD).
3
+
4
+ Meme en-tete que les autres cartes de resultat du hub (marque, MITM, LES) :
5
+ le titre au palier des cartes, le laboratoire et le circuit en sous-titre, le
6
+ compte en pastille a droite, la source en pied. L'export de la carte se
7
+ demande par la roue crantee, comme partout ailleurs, pas par un bouton qui
8
+ lui serait propre.
9
+
10
+ Porte de `heart/joCard.vue` de fr_drugs_app.
11
+ -->
12
+ <template>
13
+ <RmTableCard class="oip-drugs" :lang="dateLang" :payload="exportPayload">
14
+ <template #header>
15
+ <div class="jo_identity">
16
+ <h3 class="rm-table-card__title">{{ product.label }}</h3>
17
+ <p class="jo_meta">
18
+ {{ product.manufacturer }}
19
+ <template v-if="product.distribution_channel">
20
+ · {{ product.distribution_channel }}
21
+ </template>
22
+ </p>
23
+ <p v-if="lastPrice?.jorf_code || lastPrice?.nor_code" class="jo_refs">
24
+ <span v-if="lastPrice?.jorf_code">JORF {{ lastPrice.jorf_code }}</span>
25
+ <span v-if="lastPrice?.jorf_code && lastPrice?.nor_code"> · </span>
26
+ <span v-if="lastPrice?.nor_code">NOR {{ lastPrice.nor_code }}</span>
27
+ </p>
28
+ </div>
29
+
30
+ <div class="jo_actions">
31
+ <!-- Emplacement libre pour un appelant (la fiche projet y pose son
32
+ bouton « Commenter ») avant le bouton projet : vide partout ailleurs. -->
33
+ <slot name="hubr-actions" />
34
+ <!-- La liste JO passe par le second cluster, qui range l'`_id` du
35
+ document dans `id` (cf. mapHits de useOipDrugsElastic). -->
36
+ <component
37
+ :is="projectButton"
38
+ v-if="projectButton"
39
+ :resource-id="product._id ?? product.id"
40
+ type="FrDrugPriceJo"
41
+ :label="product.label"
42
+ compact
43
+ />
44
+ </div>
45
+
46
+ <div class="jo_facts">
47
+ <span class="jo_count">{{ countLabel }}</span>
48
+ <span v-if="lastPrice?.announce_date" class="jo_date">
49
+ {{ locale === "fr" ? "Dernière publication" : "Latest publication" }} :
50
+ <RmDate :date="lastPrice.announce_date" :lang="dateLang" />
51
+ </span>
52
+ </div>
53
+ </template>
54
+
55
+ <RmTable :columns="table.columns" :rows="table.rows">
56
+ <template v-for="key in table.typeKeys.Date" :key="key" #[key]="{ value }">
57
+ <RmDate :lang="dateLang" :date="value" />
58
+ </template>
59
+ <template v-for="key in table.typeKeys.Code" :key="key" #[key]="{ value }">
60
+ <span>{{ value.join(" - ") }}</span>
61
+ </template>
62
+ <!-- Memes montants que le suivi des prix : format monetaire complet, et
63
+ « NA » quand le tarif n'existe pas (un « € » seul se lisait comme
64
+ un prix nul). Un `<p>` ici prenait la marge du reset et decalait la
65
+ cellule d'une demi-ligne par rapport a ses voisines. -->
66
+ <template v-for="key in table.typeKeys.Price" :key="key" #[key]="{ value }">
67
+ <span>{{ money(value) }}</span>
68
+ </template>
69
+ </RmTable>
70
+
71
+ <!-- Meme pied que les cartes de marque, MITM et LES : la source, et rien
72
+ d'autre. La gouttiere vient de RmTableCard. -->
73
+ <div class="jo_footer">
74
+ <OipDrugsHeartElasticSourceContainerElastic
75
+ :uuid="HEART_SOURCE_UUIDS.ACTUALITE_JO"
76
+ :last-label="$t('oipDrugs.heart.sources.lastPrice')"
77
+ :last-date="lastPrice?.announce_date"
78
+ />
79
+ </div>
80
+ </RmTableCard>
81
+ </template>
82
+
83
+ <script setup lang="ts">
84
+ import { HEART_SOURCE_UUIDS } from "../../../oip-drugs/utils/sourceUuids";
85
+ import { valueTable } from "../../../utils/valueTable";
86
+
87
+ const props = defineProps({
88
+ product: {
89
+ required: true,
90
+ type: Object
91
+ },
92
+ inDetails: {
93
+ type: Boolean,
94
+ default: false
95
+ }
96
+ });
97
+ const { locale } = useI18n();
98
+ const { projectButton } = useOipResourceHooks();
99
+
100
+ const dateLang = computed(() => (locale.value === "fr" ? "fr" : "en"));
101
+
102
+ const lastPrice = computed(() => {
103
+ return props.product.jo?.length > 0
104
+ ? [...props.product.jo].sort(
105
+ (a: any, b: any) =>
106
+ new Date(b.announce_date).getTime() -
107
+ new Date(a.announce_date).getTime()
108
+ )[0]
109
+ : props.product.jo?.[0];
110
+ });
111
+
112
+ /** Meme formatage monetaire que les cartes du suivi des prix. */
113
+ function money(value: any) {
114
+ if (value === null || value === undefined || value === "" || value === "-") return "NA";
115
+ return new Intl.NumberFormat(locale.value === "en" ? "en-US" : "fr-FR", {
116
+ style: "currency",
117
+ currency: "EUR",
118
+ maximumFractionDigits: 2,
119
+ }).format(Number(value));
120
+ }
121
+
122
+ const countLabel = computed(() => {
123
+ const count = props.product.jo?.length ?? 0;
124
+ return locale.value === "fr"
125
+ ? `${count} publication${count > 1 ? "s" : ""} JO`
126
+ : `${count} JO publication${count > 1 ? "s" : ""}`;
127
+ });
128
+
129
+ /**
130
+ * Ce que la roue crantee ne peut pas lire du rendu : l'historique JO complet
131
+ * de la presentation, quand le tableau n'en montre que le tarif courant.
132
+ */
133
+ const exportPayload = computed(() => ({
134
+ title: `France JO - ${props.product.label}`,
135
+ subtitle: `🇫🇷 JO: ${props.product.label}`,
136
+ limit: 10000,
137
+ sheets: [
138
+ {
139
+ name: locale.value === "fr" ? "Données JO" : "JO Data",
140
+ data: props.product.jo ?? [],
141
+ schema: [
142
+ { column: locale.value === 'fr' ? 'Code NOR' : 'NOR Code', type: String, value: (a: any) => a.nor_code, width: 20 },
143
+ { column: locale.value === 'fr' ? 'Code JORF' : 'JORF Code', type: String, value: (a: any) => a.jorf_code, width: 20 },
144
+ { column: locale.value === 'fr' ? "Date d'application" : 'Application Date', type: Date, format: 'mmm d yyyy', value: (a: any) => new Date(a.application_date), width: 15 },
145
+ { column: locale.value === 'fr' ? 'PFHT' : 'Ex-Factory Price', type: Number, value: (a: any) => a.factory_price, width: 15 },
146
+ { column: locale.value === 'fr' ? 'Prix Public' : 'Public Price', type: Number, value: (a: any) => a.public_price, width: 15 },
147
+ { column: locale.value === 'fr' ? 'Prix TFR' : 'TFR Price', type: Number, value: (a: any) => a.reference_price, width: 15 },
148
+ ],
149
+ },
150
+ ],
151
+ }));
152
+
153
+ const buildTable = computed(() => {
154
+ const isCIP = props.product.distribution_channel.includes("CIP");
155
+ return props.inDetails
156
+ ? {
157
+ head:
158
+ locale.value === "en"
159
+ ? [
160
+ `NOR/JORF Code`,
161
+ "JO Date",
162
+ "Application Date",
163
+ "Ex-Factory Price",
164
+ "Public Price",
165
+ "TFR Price"
166
+ ]
167
+ : [
168
+ `Code NORF/JORF`,
169
+ "JO Date",
170
+ "Date d'Application",
171
+ "PFHT",
172
+ "Prix Public",
173
+ "Prix TFR"
174
+ ],
175
+ data: props.product?.jo?.map((a: any) => {
176
+ return {
177
+ values: [
178
+ [a.nor_code, a.jorf_code],
179
+ a.announce_date,
180
+ a.application_date,
181
+ a.factory_price,
182
+ a.public_price,
183
+ a.reference_price
184
+ ]
185
+ };
186
+ }) ?? [{ values: [] }],
187
+
188
+ type: ["Code", "Date", "Date", "Price", "Price", "Price"],
189
+ style: {
190
+ align: ["left", "left", "left", "right", "right", "right"] as const,
191
+ size: [2, 2, 2, 2, 2, 2]
192
+ }
193
+ }
194
+ : {
195
+ head:
196
+ locale.value === "en"
197
+ ? [
198
+ `${isCIP ? "CIP Code" : "UCD Code"}`,
199
+ "Application Date",
200
+ "Ex-Factory Price",
201
+ "Public Price",
202
+ "TFR Price"
203
+ ]
204
+ : [
205
+ `${isCIP ? "Code CIP" : "Code UCD"}`,
206
+ "Date d'Application",
207
+ "PFHT",
208
+ "Prix Public",
209
+ "Prix TFR"
210
+ ],
211
+ data: [
212
+ props.product.jo?.map((a: any) => {
213
+ return {
214
+ values: [
215
+ props.product.local_drug_id,
216
+ a.application_date,
217
+ a.factory_price,
218
+ a.public_price,
219
+ a.reference_price
220
+ ]
221
+ };
222
+ })?.[0] ?? [{ values: [] }]
223
+ ],
224
+ type: ["", "Date", "Price", "Price", "Price"],
225
+ style: {
226
+ align: ["left", "left", "right", "right", "right"] as const,
227
+ size: [3, 3, 2, 2, 2]
228
+ }
229
+ };
230
+ });
231
+
232
+ // Le tableau du JO etait ecrit pour `arg-table` : des libelles de tete et des
233
+ // lignes `{ values: [...] }`. La conversion en colonnes de RmTable est
234
+ // mecanique, cf. `valueTable`.
235
+ const table = computed(() => {
236
+ const converted = valueTable(
237
+ buildTable.value.head,
238
+ buildTable.value.data,
239
+ buildTable.value.type,
240
+ buildTable.value.style,
241
+ );
242
+ // Une carte de liste ne porte qu'une ligne : ses fleches de tri n'ont rien a
243
+ // trier. La fiche, elle, empile tout l'historique et se trie.
244
+ return props.inDetails
245
+ ? converted
246
+ : {
247
+ ...converted,
248
+ columns: converted.columns.map((column: any) => ({ ...column, sortable: false })),
249
+ };
250
+ });
251
+ </script>
252
+
253
+ <style scoped>
254
+ .jo_identity {
255
+ display: flex;
256
+ flex-direction: column;
257
+ gap: 2px;
258
+ min-width: 0;
259
+ flex: 1 1 auto;
260
+ }
261
+
262
+ .jo_meta {
263
+ margin: 0;
264
+ overflow: hidden;
265
+ color: var(--text-secondary);
266
+ font-size: var(--font-13);
267
+ text-overflow: ellipsis;
268
+ white-space: nowrap;
269
+ }
270
+
271
+ .jo_refs {
272
+ margin: 0;
273
+ color: var(--text-secondary);
274
+ font-size: var(--font-12);
275
+ }
276
+
277
+ .jo_actions {
278
+ display: flex;
279
+ flex: 0 0 auto;
280
+ align-items: flex-start;
281
+ gap: var(--space-8);
282
+ }
283
+
284
+ .jo_facts {
285
+ display: flex;
286
+ flex: 0 0 auto;
287
+ flex-direction: column;
288
+ align-items: flex-end;
289
+ gap: 2px;
290
+ }
291
+
292
+ /* Meme pastille que le compte de presentations des cartes de marque. */
293
+ .jo_count {
294
+ padding: var(--space-2) var(--space-8);
295
+ border-radius: 999px;
296
+ background: color-mix(in srgb, var(--color-primary) 8%, transparent);
297
+ color: var(--color-primary);
298
+ font-size: var(--font-12);
299
+ font-weight: 700;
300
+ white-space: nowrap;
301
+ }
302
+
303
+ .jo_date {
304
+ color: var(--text-secondary);
305
+ font-size: var(--font-12);
306
+ white-space: nowrap;
307
+ }
308
+
309
+ .jo_footer {
310
+ border-top: 1px solid var(--border-default);
311
+ background: var(--bg-surface-2);
312
+ font-size: var(--font-13);
313
+ }
314
+ </style>
@@ -0,0 +1,326 @@
1
+ <template>
2
+ <span class="oip-drugs" :class="haveReadMore && needsMore ? 'crop_display' : ''">
3
+ <span
4
+ :id="id"
5
+ :class="crop && !showMore ? 'crop' : ''"
6
+ :style="crop && !showMore ? `-webkit-line-clamp: ${lines}` : ''"
7
+ v-html="renderText"
8
+ />
9
+ <div
10
+ v-if="crop && haveReadMore && needsMore"
11
+ @click="toggleMore"
12
+ class="read_more"
13
+ >
14
+ {{ getReadMore }}
15
+ </div>
16
+ </span>
17
+ </template>
18
+
19
+ <script lang="ts">
20
+ export default defineComponent({
21
+ name: "heartTextHighlight",
22
+ props: {
23
+ text: {
24
+ type: String,
25
+ required: true,
26
+ },
27
+ queries: {
28
+ type: Array as PropType<string[]>,
29
+ default: null,
30
+ },
31
+ genes: {
32
+ type: Array as PropType<any[]>,
33
+ default: null,
34
+ },
35
+ crop: {
36
+ type: Boolean,
37
+ default: false,
38
+ },
39
+ lines: {
40
+ type: Number,
41
+ default: 3,
42
+ },
43
+ haveReadMore: {
44
+ type: Boolean,
45
+ default: false,
46
+ },
47
+ id: {
48
+ type: String,
49
+ default: null,
50
+ },
51
+ customReadMore: {
52
+ type: Object as () => { more: string; less: string },
53
+ default: null,
54
+ },
55
+ /**
56
+ * Surlignage des pages d'indication (LES) : fr_drugs_app le lisait dans
57
+ * le chemin (`/indication`), le layer le recoit en prop.
58
+ */
59
+ indication: {
60
+ type: Boolean,
61
+ default: undefined,
62
+ },
63
+ lang: {
64
+ type: String as () => "en" | "fr" | "es",
65
+ default: "en",
66
+ validator: (value: string) => {
67
+ return ["en", "fr", "es"].includes(value);
68
+ },
69
+ },
70
+ },
71
+ emits: ["onShowMore", "onShowLess"],
72
+ setup(props, { emit }) {
73
+ // Sous une fiche indication (cf. IndicationFiche), sans prop explicite.
74
+ const indicationPage = inject<boolean>("oipDrugsIndicationPage", false);
75
+ const showMore = ref(false);
76
+ const indic = ref<HTMLElement | null>(null);
77
+
78
+ const getReadMore = computed(() => {
79
+ if (props.customReadMore !== null) {
80
+ return showMore.value
81
+ ? props.customReadMore.less
82
+ : props.customReadMore.more;
83
+ } else {
84
+ const texts = {
85
+ more: { en: "Read more", fr: "Voir plus", es: "Ver más" },
86
+ less: { en: "Read less", fr: "Voir moins", es: "Ver menos" },
87
+ };
88
+ return showMore.value ? texts.less[props.lang] : texts.more[props.lang];
89
+ }
90
+ });
91
+
92
+ function removeAccents(str: string): string {
93
+ return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
94
+ }
95
+
96
+ function addSingleAccent(
97
+ word: string,
98
+ char: string,
99
+ accentedChar: string
100
+ ): string[] {
101
+ let variants: string[] = [];
102
+ for (let i = 0; i < word.length; i++) {
103
+ if (word[i]!.toLowerCase() === char) {
104
+ variants.push(word.slice(0, i) + accentedChar + word.slice(i + 1));
105
+ }
106
+ }
107
+ return variants;
108
+ }
109
+
110
+ function addAccents(word: string): string[] {
111
+ let variants: string[] = [word];
112
+ if (!word.toLowerCase().includes("é")) {
113
+ variants = variants.concat(addSingleAccent(word, "e", "é"));
114
+ }
115
+ if (!word.toLowerCase().includes("ï")) {
116
+ variants = variants.concat(addSingleAccent(word, "i", "ï"));
117
+ }
118
+ return variants;
119
+ }
120
+
121
+ function handleEndingS(word: string): string[] {
122
+ let variants: string[] = [word];
123
+ if (word.toLowerCase().endsWith("s")) {
124
+ variants.push(word.slice(0, -1));
125
+ } else {
126
+ variants.push(word + "s");
127
+ }
128
+ return variants;
129
+ }
130
+
131
+ function capitalizeFirstLetter(str: string): string {
132
+ return str.charAt(0).toUpperCase() + str.slice(1);
133
+ }
134
+
135
+ /**
136
+ * Les termes arrivent sous deux formes selon la barre de recherche qui les
137
+ * a produits : une chaine, ou un operateur `{ type, value }`. On ramene
138
+ * tout a des mots, et on ecarte les termes exclus — surligner ce que
139
+ * l'utilisateur a demande d'exclure n'aurait pas de sens.
140
+ */
141
+ function toWords(arr: any[]): string[] {
142
+ return (arr ?? [])
143
+ .filter((item) => item !== null && item !== undefined)
144
+ .filter((item) => typeof item !== "object" || item.type !== "not")
145
+ .map((item) => (typeof item === "object" ? item.value : item))
146
+ .filter((value): value is string => typeof value === "string" && value.trim() !== "")
147
+ // Une recherche a plusieurs mots est une phrase avec slop, ou un ET
148
+ // flou : les mots matchent separes (« cancer … de l’ovaire »). On
149
+ // les surligne donc un par un, sans les mots-outils (« de », « la »).
150
+ .flatMap((value) => {
151
+ const words = value.trim().split(/\s+/);
152
+ return words.length > 1 ? words.filter((word) => word.length >= 3) : words;
153
+ });
154
+ }
155
+
156
+ function processArray(input: any[]): string[] {
157
+ let result: string[] = [];
158
+ const arr = toWords(input);
159
+
160
+ if (arr !== null && arr.length > 0) {
161
+ arr.forEach((word) => {
162
+ // Add original word
163
+ result.push(word);
164
+
165
+ // Handle accents removal
166
+ const wordWithoutAccents = removeAccents(word);
167
+ if (word !== wordWithoutAccents) {
168
+ result.push(wordWithoutAccents);
169
+ }
170
+
171
+ // Handle adding accents to e and i if no accents already exist
172
+ const accentedVariants = addAccents(word);
173
+ accentedVariants.forEach((accentedWord) => {
174
+ result.push(accentedWord);
175
+ });
176
+
177
+ // Handle ending s for original word, word without accents, and accented variants
178
+ const wordsToProcess = [
179
+ word,
180
+ wordWithoutAccents,
181
+ ...accentedVariants,
182
+ ];
183
+ wordsToProcess.forEach((w) => {
184
+ const sVariants = handleEndingS(w);
185
+ sVariants.forEach((sVariant) => {
186
+ if (!result.includes(sVariant)) {
187
+ result.push(sVariant);
188
+ }
189
+ });
190
+ });
191
+
192
+ // Add uppercase variants of each processed word
193
+ const processedWords = [...result];
194
+ processedWords.forEach((w) => {
195
+ const capitalized = capitalizeFirstLetter(w);
196
+ if (!result.includes(capitalized)) {
197
+ result.push(capitalized);
198
+ }
199
+ });
200
+ });
201
+ }
202
+
203
+ return result;
204
+ }
205
+
206
+ function toggleMore() {
207
+ showMore.value = !showMore.value;
208
+
209
+ if (showMore.value) {
210
+ emit("onShowMore");
211
+ } else {
212
+ emit("onShowLess");
213
+ }
214
+ }
215
+
216
+ function escapeRegExp(str: string) {
217
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
218
+ }
219
+
220
+ const HTML_ESCAPES: Record<string, string> = {
221
+ "&": "&amp;",
222
+ "<": "&lt;",
223
+ ">": "&gt;",
224
+ '"': "&quot;",
225
+ "'": "&#39;",
226
+ };
227
+ function escapeHtml(str: string) {
228
+ return str.replace(/[&<>"']/g, (c) => HTML_ESCAPES[c] ?? c);
229
+ }
230
+
231
+ /**
232
+ * Le texte part dans un `v-html` : il vient d'Elasticsearch brut, on
233
+ * l'echappe. Seul prefixe HTML tolere : le statut d'avis que la carte
234
+ * d'acces precoce colle devant l'essentiel (`has_fav_access` /
235
+ * `has_defav_access`), rendu tel quel et laisse hors du surlignage.
236
+ */
237
+ const STATUS_PREFIX = /^<span class="has_(?:de)?fav_access">[^<]*<\/span>/;
238
+
239
+ const needsMore = computed(() => {
240
+ const fullHeight = ref(0);
241
+ const originalHeight = ref(0);
242
+
243
+ if (indic.value !== null) {
244
+ indic.value.classList.remove("crop");
245
+
246
+ fullHeight.value = indic.value.offsetHeight;
247
+ indic.value.classList.add("crop");
248
+ originalHeight.value = indic.value.offsetHeight;
249
+ }
250
+ return fullHeight.value > originalHeight.value;
251
+ });
252
+
253
+ const renderText = computed(() => {
254
+ const prefix = STATUS_PREFIX.exec(props.text ?? "")?.[0] ?? "";
255
+ let returnText = escapeHtml((props.text ?? "").slice(prefix.length));
256
+
257
+ if (props.queries !== null && props.queries.length > 0) {
258
+ const sepQueries = props.queries;
259
+ // Les termes sont echappes comme le texte, pour se retrouver dedans.
260
+ const queries = processArray(sepQueries)
261
+ .filter((query) => query.trim() !== "")
262
+ .map((query) => escapeRegExp(escapeHtml(query)));
263
+
264
+ // Plus rien a surligner (que des termes exclus, par exemple) : une
265
+ // alternation vide matcherait chaque position du texte.
266
+ if (queries.length === 0) return prefix + returnText;
267
+
268
+ // Create a single regex to match any of the queries, case-insensitive.
269
+ // `$` : un terme en toute fin de texte se surligne aussi.
270
+ const regex = new RegExp(
271
+ (props.indication ?? indicationPage)
272
+ ? `\\b(${queries.join("|")})(?=\\s|\\d|\\W|\\w|\\b)`
273
+ : `\\b(${queries.join("|")})(?=[s\\d\\W]|$)`,
274
+ "gi" // case-insensitive matching
275
+ );
276
+
277
+ returnText = returnText.replace(regex, (match) => {
278
+ return `<mark>${match}</mark>`;
279
+ });
280
+ }
281
+
282
+ return prefix + returnText;
283
+ });
284
+
285
+ onMounted(() => {
286
+ indic.value = document.getElementById(props.id);
287
+ });
288
+
289
+ return {
290
+ showMore,
291
+ needsMore,
292
+ renderText,
293
+ getReadMore,
294
+ toggleMore,
295
+ };
296
+ },
297
+ });
298
+ </script>
299
+
300
+ <style scoped>
301
+ .crop {
302
+ display: -webkit-box;
303
+ -webkit-box-orient: vertical;
304
+ overflow: hidden;
305
+ }
306
+
307
+ .crop_display {
308
+ display: flex;
309
+ flex-direction: column;
310
+ gap: 8px;
311
+ align-items: flex-start;
312
+ }
313
+
314
+ .read_more {
315
+ font-size: 14px;
316
+ font-style: normal;
317
+ font-weight: 500;
318
+ line-height: normal;
319
+ text-decoration-line: underline;
320
+ cursor: pointer;
321
+ color: var(--primary);
322
+ }
323
+ .read_more:hover {
324
+ color: var(--primary-hover) !important;
325
+ }
326
+ </style>