@tekkare/romulus 0.1.250 → 0.1.252

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/BarChart.vue +1 -0
  3. package/dist/runtime/components/ChoroplethMap.vue +4 -1
  4. package/dist/runtime/internal/geo/fr-regions.json +1 -1
  5. package/dist/runtime/utils/chart.d.ts +6 -3
  6. package/dist/runtime/utils/chart.js +15 -2
  7. package/layer/app/app.config.ts +6 -0
  8. package/layer/app/components/OipPubmedSummary.vue +223 -0
  9. package/layer/app/components/OipResourceDrawer.vue +331 -0
  10. package/layer/app/components/OipResourceDrawerLink.vue +40 -0
  11. package/layer/app/components/OipResourceField.vue +41 -0
  12. package/layer/app/components/OipResourceGenericCard.vue +54 -0
  13. package/layer/app/components/OipResourceSource.vue +50 -0
  14. package/layer/app/components/OipResourceTags.vue +65 -0
  15. package/layer/app/components/OipResourceView.vue +107 -0
  16. package/layer/app/components/oip-drugs/fiches/AssociationFiche.vue +775 -0
  17. package/layer/app/components/oip-drugs/fiches/BrandFiche.vue +1111 -0
  18. package/layer/app/components/oip-drugs/fiches/IndicationFiche.vue +182 -0
  19. package/layer/app/components/oip-drugs/fiches/JoFiche.vue +101 -0
  20. package/layer/app/components/oip-drugs/fiches/PndsFiche.vue +279 -0
  21. package/layer/app/components/oip-drugs/heart/JoCard.vue +314 -0
  22. package/layer/app/components/oip-drugs/heart/TextHighlight.vue +326 -0
  23. package/layer/app/components/oip-drugs/heart/ansm/BrandCard.vue +187 -0
  24. package/layer/app/components/oip-drugs/heart/ansm/CisDetail.vue +363 -0
  25. package/layer/app/components/oip-drugs/heart/ansm/CisTable.vue +167 -0
  26. package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyCard.vue +373 -0
  27. package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyFiche.vue +372 -0
  28. package/layer/app/components/oip-drugs/heart/associations/TranscriptionCard.vue +55 -0
  29. package/layer/app/components/oip-drugs/heart/elastic/SourceContainerElastic.vue +103 -0
  30. package/layer/app/components/oip-drugs/heart/france/BrandGroupCard.vue +450 -0
  31. package/layer/app/components/oip-drugs/heart/mitm/Card.vue +199 -0
  32. package/layer/app/components/oip-drugs/heart/mitm/Source.vue +23 -0
  33. package/layer/app/components/oip-drugs/heart/mitm/details/Overview.vue +381 -0
  34. package/layer/app/components/oip-drugs/heart/put/Card.vue +212 -0
  35. package/layer/app/components/oip-drugs/heart/results/AccesCard.vue +696 -0
  36. package/layer/app/components/oip-drugs/heart/results/AlertCard.vue +342 -0
  37. package/layer/app/components/oip-drugs/heart/results/AsmrCursor.vue +229 -0
  38. package/layer/app/components/oip-drugs/heart/results/EvalCard.vue +370 -0
  39. package/layer/app/components/oip-drugs/heart/results/IndicCard.vue +743 -0
  40. package/layer/app/components/oip-drugs/heart/results/MatchExplain.vue +243 -0
  41. package/layer/app/components/oip-drugs/heart/results/PrefRows.vue +105 -0
  42. package/layer/app/components/oip-drugs/heart/results/details/HasHistory.vue +487 -0
  43. package/layer/app/components/oip-drugs/heart/results/details/InternationalCountryCard.vue +461 -0
  44. package/layer/app/components/oip-drugs/heart/results/details/InternationalOverview.vue +200 -0
  45. package/layer/app/components/oip-drugs/heart/results/details/InternationalPriceDrawer.vue +647 -0
  46. package/layer/app/components/oip-drugs/heart/results/details/Monograph.vue +128 -0
  47. package/layer/app/components/oip-drugs/heart/results/details/OpinionDrawer.vue +535 -0
  48. package/layer/app/components/oip-drugs/heart/results/details/PresentationDrawer.vue +738 -0
  49. package/layer/app/components/oip-drugs/heart/results/details/Price.vue +227 -0
  50. package/layer/app/components/oip-drugs/heart/results/details/PriceOverview.vue +1261 -0
  51. package/layer/app/components/oip-drugs/heart/results/details/RecordDrawer.vue +494 -0
  52. package/layer/app/components/oip-drugs/heart/results/details/RecordList.vue +426 -0
  53. package/layer/app/components/oip-drugs/heart/results/les/Card.vue +340 -0
  54. package/layer/app/components/oip-drugs/heart/results/les/Drawer.vue +67 -0
  55. package/layer/app/components/oip-drugs/heart/results/les/IndicationDetail.vue +149 -0
  56. package/layer/app/components/oip-drugs/heart/results/les/Source.vue +20 -0
  57. package/layer/app/components/oip-drugs/heart/results/les/SourceTag.vue +39 -0
  58. package/layer/app/components/oip-drugs/heart/searchPage/DynamicSource.vue +29 -0
  59. package/layer/app/components/oip-drugs/medicam/market/Chart.vue +121 -0
  60. package/layer/app/components/oip-drugs/outpatient/ProductEvolution.vue +236 -0
  61. package/layer/app/components/oip-drugs/resources/Ansm.vue +75 -0
  62. package/layer/app/components/oip-drugs/resources/Association.vue +30 -0
  63. package/layer/app/components/oip-drugs/resources/Brand.vue +65 -0
  64. package/layer/app/components/oip-drugs/resources/Contribution.vue +216 -0
  65. package/layer/app/components/oip-drugs/resources/Hospital.vue +151 -0
  66. package/layer/app/components/oip-drugs/resources/Indication.vue +54 -0
  67. package/layer/app/components/oip-drugs/resources/Jo.vue +57 -0
  68. package/layer/app/components/oip-drugs/resources/Medicam.vue +140 -0
  69. package/layer/app/components/oip-drugs/resources/Mitm.vue +84 -0
  70. package/layer/app/components/oip-drugs/resources/NationalAssembly.vue +93 -0
  71. package/layer/app/components/oip-drugs/resources/Outpatient.vue +97 -0
  72. package/layer/app/components/oip-drugs/resources/Pnds.vue +24 -0
  73. package/layer/app/components/oip-drugs/resources/Price.vue +106 -0
  74. package/layer/app/components/oip-drugs/resources/Put.vue +143 -0
  75. package/layer/app/components/oip-science/appel-a-projet/Cards.vue +162 -0
  76. package/layer/app/components/oip-science/clinical-trials/Cards.vue +382 -0
  77. package/layer/app/components/oip-science/clinical-trials/CardsGlobal.vue +273 -0
  78. package/layer/app/components/oip-science/clinical-trials/LinkedList.vue +366 -0
  79. package/layer/app/components/oip-science/clinical-trials/PairBadge.vue +25 -0
  80. package/layer/app/components/oip-science/clinical-trials/PairBody.vue +182 -0
  81. package/layer/app/components/oip-science/clinical-trials/SitesDrawer.vue +95 -0
  82. package/layer/app/components/oip-science/common/Form.vue +73 -0
  83. package/layer/app/components/oip-science/common/MatchAvgTag.vue +283 -0
  84. package/layer/app/components/oip-science/common/NoData.vue +16 -0
  85. package/layer/app/components/oip-science/common/ScoreBadge.vue +35 -0
  86. package/layer/app/components/oip-science/common/Source.vue +31 -0
  87. package/layer/app/components/oip-science/common/TableTags.vue +279 -0
  88. package/layer/app/components/oip-science/common/Timeline.vue +104 -0
  89. package/layer/app/components/oip-science/common/ToolTips.vue +89 -0
  90. package/layer/app/components/oip-science/cross/publicationPubmed.vue +484 -0
  91. package/layer/app/components/oip-science/crossData/CrossDataLocations.vue +71 -0
  92. package/layer/app/components/oip-science/ctgov/Investigators.vue +148 -0
  93. package/layer/app/components/oip-science/ctgov/Locations.vue +102 -0
  94. package/layer/app/components/oip-science/ctgov/Publications.vue +52 -0
  95. package/layer/app/components/oip-science/ctgov/Sponsors.vue +66 -0
  96. package/layer/app/components/oip-science/ctgov/StudyDescription.vue +1136 -0
  97. package/layer/app/components/oip-science/ctgov-centers/ClinicalTrials.vue +40 -0
  98. package/layer/app/components/oip-science/ctgov-centers/Publications.vue +50 -0
  99. package/layer/app/components/oip-science/ctgov-investigators/ClinicalTrials.vue +43 -0
  100. package/layer/app/components/oip-science/ctgov-investigators/Informations.vue +171 -0
  101. package/layer/app/components/oip-science/ctgov-investigators/Publications.vue +44 -0
  102. package/layer/app/components/oip-science/elastic/DateRangeFilter.vue +209 -0
  103. package/layer/app/components/oip-science/elastic/Filter.vue +719 -0
  104. package/layer/app/components/oip-science/elastic/MatchExplain.vue +152 -0
  105. package/layer/app/components/oip-science/establishments/Cards.vue +183 -0
  106. package/layer/app/components/oip-science/establishments/ClinicalTrials.vue +62 -0
  107. package/layer/app/components/oip-science/establishments/Hcps.vue +227 -0
  108. package/layer/app/components/oip-science/establishments/Informations.vue +263 -0
  109. package/layer/app/components/oip-science/establishments/LinkedList.vue +153 -0
  110. package/layer/app/components/oip-science/establishments/MatchedLabels.vue +194 -0
  111. package/layer/app/components/oip-science/establishments/PublicFinance.vue +62 -0
  112. package/layer/app/components/oip-science/euct/Investigators.vue +125 -0
  113. package/layer/app/components/oip-science/euct/Locations.vue +102 -0
  114. package/layer/app/components/oip-science/euct/Products.vue +101 -0
  115. package/layer/app/components/oip-science/euct/Publications.vue +89 -0
  116. package/layer/app/components/oip-science/euct/Sponsors.vue +86 -0
  117. package/layer/app/components/oip-science/euct/StudyDescription.vue +1075 -0
  118. package/layer/app/components/oip-science/euct-centers/ClinicalTrials.vue +40 -0
  119. package/layer/app/components/oip-science/euct-centers/Publications.vue +55 -0
  120. package/layer/app/components/oip-science/euct-investigators/ClinicalTrials.vue +41 -0
  121. package/layer/app/components/oip-science/euct-investigators/Informations.vue +171 -0
  122. package/layer/app/components/oip-science/euct-investigators/Publications.vue +51 -0
  123. package/layer/app/components/oip-science/hcps/AppelsAProjet.vue +62 -0
  124. package/layer/app/components/oip-science/hcps/Cards.vue +237 -0
  125. package/layer/app/components/oip-science/hcps/ClinicalTrials.vue +78 -0
  126. package/layer/app/components/oip-science/hcps/Informations.vue +296 -0
  127. package/layer/app/components/oip-science/hcps/LinkedList.vue +153 -0
  128. package/layer/app/components/oip-science/hcps/MatchedLabels.vue +207 -0
  129. package/layer/app/components/oip-science/hcps/Pubmed.vue +128 -0
  130. package/layer/app/components/oip-science/local/SliderTabs.vue +99 -0
  131. package/layer/app/components/oip-science/local/SliderTabsFull.vue +135 -0
  132. package/layer/app/components/oip-science/pubmed/Authors.vue +128 -0
  133. package/layer/app/components/oip-science/pubmed/Cards.vue +417 -0
  134. package/layer/app/components/oip-science/pubmed/ClinicalTrials.vue +71 -0
  135. package/layer/app/components/oip-science/resources/Phrc.vue +39 -0
  136. package/layer/app/composables/useOipChapterScroll.ts +43 -0
  137. package/layer/app/composables/useOipResourceHooks.ts +27 -0
  138. package/layer/app/composables/useOipResourceNav.ts +66 -0
  139. package/layer/app/composables/useOipResourceViews.ts +117 -0
  140. package/layer/app/composables/useOipResources.ts +83 -0
  141. package/layer/app/oip-drugs/PORTING.md +120 -0
  142. package/layer/app/oip-drugs/composables/useAssociationDetail.ts +220 -0
  143. package/layer/app/oip-drugs/composables/useAssociationExport.ts +63 -0
  144. package/layer/app/oip-drugs/composables/useAssociationLookup.ts +105 -0
  145. package/layer/app/oip-drugs/composables/useCardPrefOptions.ts +201 -0
  146. package/layer/app/oip-drugs/composables/useCurrency.ts +121 -0
  147. package/layer/app/oip-drugs/composables/useDirectusSources.ts +224 -0
  148. package/layer/app/oip-drugs/composables/useHeartBrandMatches.ts +112 -0
  149. package/layer/app/oip-drugs/composables/useHeartDetailSearches.ts +479 -0
  150. package/layer/app/oip-drugs/composables/useHeartIntlApprovals.ts +661 -0
  151. package/layer/app/oip-drugs/composables/useHeartLesIndication.ts +168 -0
  152. package/layer/app/oip-drugs/composables/useHeartMatchExplain.ts +231 -0
  153. package/layer/app/oip-drugs/composables/useHeartReports.ts +239 -0
  154. package/layer/app/oip-drugs/composables/useHeartSearchFields.ts +233 -0
  155. package/layer/app/oip-drugs/composables/useMergeByKey.ts +23 -0
  156. package/layer/app/oip-drugs/composables/useOipDrugsElastic.ts +621 -0
  157. package/layer/app/oip-drugs/composables/usePageEmpty.ts +58 -0
  158. package/layer/app/oip-drugs/composables/v-useSalesSource.ts +64 -0
  159. package/layer/app/oip-drugs/drugs.css +500 -0
  160. package/layer/app/oip-drugs/esIndices.ts +322 -0
  161. package/layer/app/oip-drugs/kinds.ts +264 -0
  162. package/layer/app/oip-drugs/locales/en.json +1571 -0
  163. package/layer/app/oip-drugs/locales/fr.json +1571 -0
  164. package/layer/app/oip-drugs/monographs/blincyto-has.vue +289 -0
  165. package/layer/app/oip-drugs/monographs/darzalex-has.vue +276 -0
  166. package/layer/app/oip-drugs/monographs/dupixent-has.vue +216 -0
  167. package/layer/app/oip-drugs/monographs/enhertu-has.vue +280 -0
  168. package/layer/app/oip-drugs/monographs/has-sein-2023.vue +1649 -0
  169. package/layer/app/oip-drugs/monographs/ibrance-has.vue +277 -0
  170. package/layer/app/oip-drugs/monographs/imfinzi-has.vue +293 -0
  171. package/layer/app/oip-drugs/monographs/keytruda-has.vue +334 -0
  172. package/layer/app/oip-drugs/monographs/kisqali-has.vue +201 -0
  173. package/layer/app/oip-drugs/monographs/opdivo-has.vue +231 -0
  174. package/layer/app/oip-drugs/monographs/ozempic-has.vue +186 -0
  175. package/layer/app/oip-drugs/monographs/tecentriq-has.vue +312 -0
  176. package/layer/app/oip-drugs/monographs/yescarta-has.vue +272 -0
  177. package/layer/app/oip-drugs/types/elastic.ts +113 -0
  178. package/layer/app/oip-drugs/types/es/agencies.ts +152 -0
  179. package/layer/app/oip-drugs/types/es/directory.ts +54 -0
  180. package/layer/app/oip-drugs/types/es/docByIndex.ts +151 -0
  181. package/layer/app/oip-drugs/types/es/openmedic.ts +67 -0
  182. package/layer/app/oip-drugs/types/es/outpatient.ts +71 -0
  183. package/layer/app/oip-drugs/types/es/portal.ts +130 -0
  184. package/layer/app/oip-drugs/types/es/retail.ts +107 -0
  185. package/layer/app/oip-drugs/types/es/statistics.ts +29 -0
  186. package/layer/app/oip-drugs/types/heart/CurrencyRate.ts +16 -0
  187. package/layer/app/oip-drugs/types/heart/ansm.ts +106 -0
  188. package/layer/app/oip-drugs/types/heart/tag.ts +29 -0
  189. package/layer/app/oip-drugs/utils/associationFields.ts +118 -0
  190. package/layer/app/oip-drugs/utils/contributions.ts +36 -0
  191. package/layer/app/oip-drugs/utils/countryMapping.ts +387 -0
  192. package/layer/app/oip-drugs/utils/esHighlight.ts +21 -0
  193. package/layer/app/oip-drugs/utils/exportSheets.ts +57 -0
  194. package/layer/app/oip-drugs/utils/heartIntlPrice.ts +228 -0
  195. package/layer/app/oip-drugs/utils/heartStyleAndFormat.ts +262 -0
  196. package/layer/app/oip-drugs/utils/heartStyleAnsm.ts +71 -0
  197. package/layer/app/oip-drugs/utils/latestDate.ts +31 -0
  198. package/layer/app/oip-drugs/utils/p-heartCardTags.ts +53 -0
  199. package/layer/app/oip-drugs/utils/priceExport.ts +269 -0
  200. package/layer/app/oip-drugs/utils/q-hasRatings.ts +43 -0
  201. package/layer/app/oip-drugs/utils/s-openCard.ts +20 -0
  202. package/layer/app/oip-drugs/utils/sourceUuids.ts +155 -0
  203. package/layer/app/oip-drugs/utils/styleAndFormat.ts +40 -0
  204. package/layer/app/oip-drugs/utils/v-salesSeries.ts +156 -0
  205. package/layer/app/oip-drugs/utils/v-scopeTags.ts +23 -0
  206. package/layer/app/oip-science/PORTING.md +93 -0
  207. package/layer/app/oip-science/assets/smartSelection.json +428 -0
  208. package/layer/app/oip-science/composables/useAuthorMatching.ts +342 -0
  209. package/layer/app/oip-science/composables/useCTCenter.ts +107 -0
  210. package/layer/app/oip-science/composables/useCTInvestigators.ts +107 -0
  211. package/layer/app/oip-science/composables/useEstablishmentCrossData.ts +139 -0
  212. package/layer/app/oip-science/composables/useEstablishmentMatching.ts +176 -0
  213. package/layer/app/oip-science/composables/useEuct.ts +418 -0
  214. package/layer/app/oip-science/composables/useEuctCenter.ts +107 -0
  215. package/layer/app/oip-science/composables/useEuctInvestigators.ts +107 -0
  216. package/layer/app/oip-science/composables/useGlobalClinicalTrials.ts +624 -0
  217. package/layer/app/oip-science/composables/useLinkedTrials.ts +83 -0
  218. package/layer/app/oip-science/composables/useOipElastic.ts +658 -0
  219. package/layer/app/oip-science/composables/useOipSearchExplain.ts +166 -0
  220. package/layer/app/oip-science/composables/usePubmed.ts +487 -0
  221. package/layer/app/oip-science/composables/useSearchFields.ts +153 -0
  222. package/layer/app/oip-science/indexConfig.json +794 -0
  223. package/layer/app/oip-science/locales/en.json +483 -0
  224. package/layer/app/oip-science/locales/fr.json +483 -0
  225. package/layer/app/oip-science/science.css +1182 -0
  226. package/layer/app/oip-science/utils/a-openCard.ts +23 -0
  227. package/layer/app/oip-science/utils/b-openCard.ts +35 -0
  228. package/layer/app/oip-science/utils/c-filterContext.ts +81 -0
  229. package/layer/app/oip-science/utils/f-types.ts +241 -0
  230. package/layer/app/oip-science/utils/frDiseaseTerms.ts +269 -0
  231. package/layer/app/oip-science/utils/matchMethod.ts +53 -0
  232. package/layer/app/oip-science/utils/sliderTabs.ts +13 -0
  233. package/layer/app/oip-science/utils/sources.ts +15 -0
  234. package/layer/app/oip-science/utils/styleAndFormat.ts +608 -0
  235. package/layer/app/oip-science/utils/toPresets.ts +7 -0
  236. package/layer/app/oip-science/utils/valueTable.ts +126 -0
  237. package/layer/app/plugins/oip-science-i18n.ts +17 -0
  238. package/layer/app/utils/oipFicheRoutes.ts +118 -0
  239. package/layer/app/utils/oipResources.ts +440 -0
  240. package/layer/nuxt.config.ts +22 -2
  241. package/layer/public/reports/blincyto-has.html +292 -0
  242. package/layer/public/reports/blincyto-has.pdf +0 -0
  243. package/layer/public/reports/darzalex-has.html +279 -0
  244. package/layer/public/reports/darzalex-has.pdf +0 -0
  245. package/layer/public/reports/dupixent-has.html +217 -0
  246. package/layer/public/reports/dupixent-has.pdf +0 -0
  247. package/layer/public/reports/enhertu-has.html +283 -0
  248. package/layer/public/reports/enhertu-has.pdf +0 -0
  249. package/layer/public/reports/has-sein-2023.html +1639 -0
  250. package/layer/public/reports/ibrance-has.html +280 -0
  251. package/layer/public/reports/ibrance-has.pdf +0 -0
  252. package/layer/public/reports/imfinzi-has.html +297 -0
  253. package/layer/public/reports/imfinzi-has.pdf +0 -0
  254. package/layer/public/reports/keytruda-has.html +337 -0
  255. package/layer/public/reports/keytruda-has.pdf +0 -0
  256. package/layer/public/reports/kisqali-has.html +202 -0
  257. package/layer/public/reports/kisqali-has.pdf +0 -0
  258. package/layer/public/reports/opdivo-has.html +232 -0
  259. package/layer/public/reports/opdivo-has.pdf +0 -0
  260. package/layer/public/reports/ozempic-has.html +188 -0
  261. package/layer/public/reports/ozempic-has.pdf +0 -0
  262. package/layer/public/reports/tecentriq-has.html +315 -0
  263. package/layer/public/reports/tecentriq-has.pdf +0 -0
  264. package/layer/public/reports/yescarta-has.html +275 -0
  265. package/layer/public/reports/yescarta-has.pdf +0 -0
  266. package/layer/server/api/oip/es.post.ts +99 -0
  267. package/layer/server/api/oip/pdf.get.ts +70 -0
  268. package/layer/server/api/oip/reports.get.ts +98 -0
  269. package/layer/server/api/oip/resource-types.get.ts +22 -0
  270. package/package.json +1 -1
@@ -0,0 +1,1075 @@
1
+ <!--
2
+ Onglet « Description de l'etude » d'un essai EUCT (portage de la page
3
+ /euct/[id]/study-description de France Augmented Science).
4
+ -->
5
+ <template>
6
+ <div ref="root" class="oip-science">
7
+ <div v-if="resultsQueryEuct && !isLoadingQueryEuct">
8
+ <div class="oip_row gap-16">
9
+ <div class="chapters_col">
10
+ <RmChaptersNav
11
+ class="chapters_sticky"
12
+ :chapters="chaptersList"
13
+ :title="$t('oipScience.trial.chapters')"
14
+ :offset="chaptersOffset"
15
+ :style="{ top: `${stickyTop}px` }"
16
+ @select="scrollToChapter"
17
+ />
18
+ </div>
19
+
20
+ <div class="oip_widthFill">
21
+ <div class="oip_grid gap-16">
22
+ <!-- SUMMARY -->
23
+ <div class="oip_card oip_grid gap-24" id="summary">
24
+ <div class="oip_row">
25
+ <div class="oip_grid gap-4">
26
+ <div class="oip_row v-center gap-8 wrap">
27
+ <h1 class="kk_title_medium_2">{{ $t('oipScience.trial.summary') }}</h1>
28
+ <span
29
+ class="trial-id-badge"
30
+ v-if="resultsQueryEuct.euct_number"
31
+ >EUCT&nbsp;: {{ resultsQueryEuct.euct_number }}</span>
32
+ <span
33
+ class="trial-id-badge"
34
+ v-if="resultsQueryEuct.nct_number"
35
+ >NCT&nbsp;: {{ resultsQueryEuct.nct_number }}</span>
36
+ </div>
37
+ <OipScienceCommonSource :sources="['EUCT']" :lang="lang" />
38
+ </div>
39
+ </div>
40
+ <div class="oip_row v-center gap-8">
41
+ <RmTag
42
+ :variant="setColorStatus(resultsQueryEuct.global_status)"
43
+ >
44
+ {{ ifNoRes(resultsQueryEuct.global_status) }}
45
+ </RmTag>
46
+
47
+ <p
48
+ class="kk_text"
49
+ :class="setColorStatus(resultsQueryEuct.global_status)"
50
+ >
51
+ {{ ifNoRes(resultsQueryEuct.trial_phase) }}
52
+ </p>
53
+ </div>
54
+
55
+ <OipScienceCommonForm
56
+ :title="$t('oipScience.trial.fullTitle')"
57
+ :content="ifNoRes(resultsQueryEuct.full_title)"
58
+ :highlightQueries="highlight"
59
+ class="oip_body"
60
+ ></OipScienceCommonForm>
61
+
62
+ <OipScienceCommonForm
63
+ :title="$t('oipScience.trial.publicTitle')"
64
+ :content="ifNoRes(resultsQueryEuct.public_title)"
65
+ :highlightQueries="highlight"
66
+ class="oip_body"
67
+ ></OipScienceCommonForm>
68
+ </div>
69
+
70
+ <!-- DETAILED DESCRIPTION -->
71
+ <div class="oip_card oip_grid gap-24" id="trial-details">
72
+ <div class="oip_grid gap-4">
73
+ <h1 class="kk_title_medium_2">{{ $t('oipScience.trial.trialDetails') }}</h1>
74
+ </div>
75
+
76
+ <OipScienceCommonForm
77
+ :title="$t('oipScience.trial.trialPhase')"
78
+ :content="ifNoRes(resultsQueryEuct.trial_phase)"
79
+ :highlightQueries="highlight"
80
+ ></OipScienceCommonForm>
81
+
82
+ <OipScienceCommonForm
83
+ :title="$t('oipScience.trial.trialCategory')"
84
+ :content="ifNoRes(resultsQueryEuct.trial_category)"
85
+ :highlightQueries="highlight"
86
+ ></OipScienceCommonForm>
87
+
88
+ <OipScienceCommonForm
89
+ :title="$t('oipScience.trial.trialCategoryJustification')"
90
+ :content="ifNoRes(resultsQueryEuct.trial_category_justification)"
91
+ :highlightQueries="highlight"
92
+ ></OipScienceCommonForm>
93
+
94
+ <div class="oip_grid gap-4">
95
+ <h1 class="kk_title_medium_2">{{ $t('oipScience.trial.trialDuration') }}</h1>
96
+ </div>
97
+
98
+ <div class="oip_row v-bottom">
99
+ <OipScienceCommonTimeline
100
+ :title="$t('oipScience.trial.estimatedRecruitmentStartEu')"
101
+ :content="
102
+ ifNoRes(
103
+ resultsQueryEuct.estimated_recruitment_start_date_in_eu,
104
+ 'date'
105
+ )
106
+ "
107
+ />
108
+ <OipScienceCommonTimeline
109
+ :title="$t('oipScience.trial.estimatedEndEu')"
110
+ :content="
111
+ ifNoRes(
112
+ resultsQueryEuct.estimated_trial_end_date_in_eu,
113
+ 'date'
114
+ )
115
+ "
116
+ />
117
+ <OipScienceCommonTimeline
118
+ :title="$t('oipScience.trial.estimatedGlobalEnd')"
119
+ :content="
120
+ ifNoRes(
121
+ resultsQueryEuct.estimated_trial_global_end_date,
122
+ 'date'
123
+ )
124
+ "
125
+ />
126
+ </div>
127
+ </div>
128
+
129
+ <!-- OIP Discovery Tags -->
130
+ <div class="oip_card oip_grid gap-24" id="tags">
131
+ <div class="oip_grid gap-4">
132
+ <h1 class="kk_title_medium_2">{{ $t('oipScience.trial.oipDiscoveryTags') }}</h1>
133
+ </div>
134
+ <OipScienceCommonTableTags
135
+ :isVertical="true"
136
+ icon="Bandaids"
137
+ :title="$t('oipScience.trial.rareDiseases')"
138
+ :values="getRareDiseases"
139
+ :highlightQueries="highlight"
140
+ ></OipScienceCommonTableTags>
141
+ </div>
142
+
143
+ <!-- Conditions -->
144
+ <div class="oip_card oip_grid gap-24" id="conditions">
145
+ <div class="oip_grid gap-4">
146
+ <h1 class="kk_title_medium_2">{{ $t('oipScience.trial.conditions') }}</h1>
147
+ </div>
148
+
149
+ <OipScienceCommonTableTags
150
+ :isVertical="true"
151
+ icon="FaceMask"
152
+ :title="$t('oipScience.trial.conditions')"
153
+ :values="
154
+ resultsQueryEuct.medical_condition?.map(
155
+ (val) => val.condition_name_en
156
+ ) || []
157
+ "
158
+ :highlightQueries="highlight"
159
+ ></OipScienceCommonTableTags>
160
+
161
+ <OipScienceCommonTableTags
162
+ :isVertical="true"
163
+ icon="Clipboard"
164
+ :title="$t('oipScience.trial.therapeuticArea')"
165
+ :values="
166
+ resultsQueryEuct.therapeutic_area?.map((val) => val) || []
167
+ "
168
+ :highlightQueries="highlight"
169
+ ></OipScienceCommonTableTags>
170
+ </div>
171
+
172
+ <!-- Products -->
173
+ <div class="oip_card oip_grid gap-24" id="products">
174
+ <div class="oip_grid gap-4">
175
+ <h1 class="kk_title_medium_2">{{ $t('oipScience.trial.products') }}</h1>
176
+ </div>
177
+
178
+ <OipScienceCommonTableTags
179
+ :isVertical="true"
180
+ icon="Pill"
181
+ :title="$t('oipScience.trial.products')"
182
+ :values="
183
+ resultsQueryEuct.products?.map((val) =>
184
+ val.productDictionaryInfo?.prodName
185
+ ? val.productDictionaryInfo?.prodName
186
+ : val.productDictionaryInfo?.activeSubstanceName
187
+ ) || []
188
+ "
189
+ :highlightQueries="highlight"
190
+ ></OipScienceCommonTableTags>
191
+ </div>
192
+
193
+ <!-- Main Objective -->
194
+ <div class="oip_card oip_grid gap-24" id="objectives">
195
+ <div class="oip_grid gap-4">
196
+ <h1 class="kk_title_medium_2">{{ $t('oipScience.trial.mainObjectiveTitle') }}</h1>
197
+ </div>
198
+
199
+ <OipScienceCommonTableTags
200
+ :isVertical="true"
201
+ :title="$t('oipScience.trial.trialsScope')"
202
+ :values="ifNoRes(resultsQueryEuct?.trial_scope) || []"
203
+ :highlightQueries="highlight"
204
+ class="oip_body"
205
+ ></OipScienceCommonTableTags>
206
+
207
+ <OipScienceCommonForm
208
+ :title="$t('oipScience.trial.mainObjective')"
209
+ :content="ifNoRes(resultsQueryEuct.main_objective_en)"
210
+ :highlightQueries="highlight"
211
+ ></OipScienceCommonForm>
212
+
213
+ <div class="oip_grid gap-4">
214
+ <h1 class="kk_title_medium_2">{{ $t('oipScience.trial.secondaryObjectives') }}</h1>
215
+ </div>
216
+
217
+ <div
218
+ v-if="
219
+ resultsQueryEuct?.secondary_objective &&
220
+ resultsQueryEuct?.secondary_objective.length > 0
221
+ "
222
+ class="oip_grid gap-4"
223
+ >
224
+ <div
225
+ v-for="(
226
+ obj, index
227
+ ) in resultsQueryEuct?.secondary_objective.sort(
228
+ (a, b) =>
229
+ (typeof a.objective_number === 'number'
230
+ ? a.objective_number
231
+ : 0) -
232
+ (typeof b.objective_number === 'number'
233
+ ? b.objective_number
234
+ : 0)
235
+ )"
236
+ :key="index"
237
+ >
238
+ <!-- <span class="oip_body">
239
+ {{ obj.objective_number }}. {{ obj.second_objective_en }}
240
+ </span> -->
241
+
242
+ <RmTextHighlight
243
+ :queries="highlight"
244
+ :text="obj.objective_number + '. ' + obj.second_objective_en"
245
+ class="oip_body"
246
+ />
247
+ </div>
248
+ </div>
249
+ <p v-else class="oip_body">{{ ifNoRes(null) }}</p>
250
+ </div>
251
+
252
+ <!-- CRITERIAS-->
253
+ <div class="oip_card oip_grid gap-24" id="eligibility-criteria">
254
+ <div class="oip_grid gap-4">
255
+ <h1 class="kk_title_medium_2">{{ $t('oipScience.trial.eligibilityCriteria') }}</h1>
256
+ </div>
257
+
258
+ <div class="grid gap-16">
259
+ <OipScienceCommonForm
260
+ :title="$t('oipScience.trial.totalEnrollmentReminder')"
261
+ :content="
262
+ ifNoRes(
263
+ resultsQueryEuct.enrollment_status
264
+ ?.total_planned_enrollment,
265
+ 'number'
266
+ ) + ' ' + $t('oipScience.trial.subjects')
267
+ "
268
+ :highlightQueries="highlight"
269
+ />
270
+ <OipScienceCommonForm
271
+ :title="$t('oipScience.trial.ageRange')"
272
+ :content="
273
+ ifNoRes(resultsQueryEuct.subject_population?.age_range)
274
+ "
275
+ :highlightQueries="highlight"
276
+ />
277
+
278
+ <OipScienceCommonTableTags
279
+ :isVertical="true"
280
+ :title="$t('oipScience.trial.ageRangeSecondary')"
281
+ :values="
282
+ Array.isArray(
283
+ resultsQueryEuct.subject_population
284
+ ?.age_range_secondary_identifier
285
+ )
286
+ ? resultsQueryEuct.subject_population?.age_range_secondary_identifier.map(
287
+ (val) => val
288
+ )
289
+ : resultsQueryEuct.subject_population
290
+ ?.age_range_secondary_identifier
291
+ ? [
292
+ resultsQueryEuct.subject_population
293
+ ?.age_range_secondary_identifier,
294
+ ]
295
+ : []
296
+ "
297
+ :highlightQueries="highlight"
298
+ ></OipScienceCommonTableTags>
299
+
300
+ <OipScienceCommonTableTags
301
+ :isVertical="true"
302
+ :title="$t('oipScience.trial.genders')"
303
+ :values="[
304
+ getSexe(
305
+ resultsQueryEuct.subject_population?.has_female,
306
+ resultsQueryEuct.subject_population?.has_male
307
+ ),
308
+ ]"
309
+ :highlightQueries="highlight"
310
+ ></OipScienceCommonTableTags>
311
+
312
+ <!-- INCLUSIONS CRITERIAS -->
313
+ <h1 class="kk_title_medium_2">{{ $t('oipScience.trial.inclusionCriteria') }}</h1>
314
+ <div
315
+ v-if="
316
+ resultsQueryEuct?.principal_inclusion_criteria &&
317
+ resultsQueryEuct?.principal_inclusion_criteria.length > 0
318
+ "
319
+ class="oip_grid gap-4"
320
+ >
321
+ <div
322
+ v-for="(
323
+ obj, index
324
+ ) in resultsQueryEuct?.principal_inclusion_criteria.sort(
325
+ (a, b) =>
326
+ (typeof a?.criteria_number === 'number'
327
+ ? a.criteria_number
328
+ : 0) -
329
+ (typeof b?.criteria_number === 'number'
330
+ ? b.criteria_number
331
+ : 0)
332
+ )"
333
+ :key="index"
334
+ >
335
+ <RmTextHighlight
336
+ :text="obj.criteria_number + '. ' + obj.inclusion_criteria_en"
337
+ class="oip_body"
338
+ />
339
+ </div>
340
+ </div>
341
+ <p v-else class="oip_body">{{ ifNoRes(null) }}</p>
342
+
343
+ <!-- EXCLUSION CRITERIAS -->
344
+
345
+ <h1 class="kk_title_medium_2">{{ $t('oipScience.trial.exclusionCriteria') }}</h1>
346
+ <div
347
+ v-if="
348
+ resultsQueryEuct?.principal_exclusion_criteria &&
349
+ resultsQueryEuct?.principal_exclusion_criteria.length > 0
350
+ "
351
+ class="oip_grid gap-4"
352
+ >
353
+ <div
354
+ v-for="(
355
+ obj, index
356
+ ) in resultsQueryEuct?.principal_exclusion_criteria.sort(
357
+ (a, b) =>
358
+ (typeof a?.criteria_number === 'number'
359
+ ? a.criteria_number
360
+ : 0) -
361
+ (typeof b?.criteria_number === 'number'
362
+ ? b.criteria_number
363
+ : 0)
364
+ )"
365
+ :key="index"
366
+ >
367
+ <RmTextHighlight
368
+ :text="obj.criteria_number + '. ' + obj.exclusion_criteria_en"
369
+ class="oip_body"
370
+ />
371
+ </div>
372
+ </div>
373
+ <p v-else class="oip_body">{{ ifNoRes(null) }}</p>
374
+ </div>
375
+ </div>
376
+
377
+ <!-- ENROLLMENTS -->
378
+ <div class="oip_card oip_grid gap-24" id="enrolment">
379
+ <div class="oip_grid gap-4">
380
+ <h1 class="kk_title_medium_2">{{ $t('oipScience.trial.enrollment') }}</h1>
381
+ </div>
382
+
383
+ <OipScienceCommonForm
384
+ :title="$t('oipScience.trial.totalGlobalEnrollment')"
385
+ :content="
386
+ ifNoRes(
387
+ resultsQueryEuct.enrollment_status?.total_planned_enrollment,
388
+ 'number'
389
+ ) + ' ' + $t('oipScience.trial.subjects')
390
+ "
391
+ />
392
+ <div>
393
+ <h1 class="oip_underline">{{ $t('oipScience.trial.enrollmentInsideEurope') }}</h1>
394
+
395
+ <RmTable :columns="countriesTable.columns" :rows="countriesTable.rows">
396
+ <template v-for="key in countriesTable.typeKeys.Country" :key="key" #[key]="{ value }">
397
+ <RmTag variant="gray" class="no-wrap">
398
+ {{ getCountryEmoji(value) }}
399
+ </RmTag>
400
+ </template>
401
+
402
+ <template v-for="key in countriesTable.typeKeys.Status" :key="key" #[key]="{ value }">
403
+ <RmTag :variant="setColorStatus(value)">
404
+ {{ ifNoRes(value) }}
405
+ </RmTag>
406
+ </template>
407
+
408
+ <template v-for="key in countriesTable.typeKeys.Link" :key="key" #[key]="{ value }">
409
+ <div v-if="value.rawData">
410
+ <div
411
+ class="kk_link_btn_underline"
412
+ v-if="value.rawData.sites.length > 0"
413
+ @click="sitesCountry = value.country"
414
+ >
415
+ {{
416
+ value.rawData.sites.filter(
417
+ // @ts-ignore
418
+ (site) => site.country === value.country
419
+ ).length +
420
+ ' ' +
421
+ (value.rawData.sites.filter(
422
+ // @ts-ignore
423
+ (site) => site.country === value.country
424
+ ).length > 1
425
+ ? $t('oipScience.trial.sites')
426
+ : $t('oipScience.trial.site'))
427
+ }}
428
+ </div>
429
+ </div>
430
+ </template>
431
+ </RmTable>
432
+ <OipScienceClinicalTrialsSitesDrawer v-model:country="sitesCountry" :trial="resultsQueryEuct" />
433
+ </div>
434
+ </div>
435
+
436
+ <!-- ENDS POINTS -->
437
+ <div class="oip_card oip_grid gap-24" id="end_points">
438
+ <div class="oip_grid gap-4">
439
+ <h1 class="kk_title_medium_2">{{ $t('oipScience.trial.primaryEndPoints') }}</h1>
440
+ </div>
441
+
442
+ <div
443
+ v-if="
444
+ resultsQueryEuct?.primary_end_point &&
445
+ resultsQueryEuct?.primary_end_point.length > 0
446
+ "
447
+ class="oip_grid gap-4"
448
+ >
449
+ <div
450
+ v-for="(obj, index) in resultsQueryEuct?.primary_end_point.sort(
451
+ (a, b) =>
452
+ (typeof a.end_point_number === 'number'
453
+ ? a.end_point_number
454
+ : 0) -
455
+ (typeof b.end_point_number === 'number'
456
+ ? b.end_point_number
457
+ : 0)
458
+ )"
459
+ :key="index"
460
+ >
461
+ <RmTextHighlight
462
+ :text="obj.end_point_number + '. ' + obj.primary_end_point_en"
463
+ class="oip_body"
464
+ />
465
+ </div>
466
+ </div>
467
+ <p v-else class="oip_body">{{ ifNoRes(null) }}</p>
468
+
469
+ <div class="oip_grid gap-4">
470
+ <h1 class="kk_title_medium_2">{{ $t('oipScience.trial.secondaryEndPoints') }}</h1>
471
+ </div>
472
+
473
+ <div
474
+ v-if="
475
+ resultsQueryEuct?.secondary_end_point &&
476
+ resultsQueryEuct?.secondary_end_point.length > 0
477
+ "
478
+ class="oip_grid gap-4"
479
+ >
480
+ <div
481
+ v-for="(
482
+ obj, index
483
+ ) in resultsQueryEuct?.secondary_end_point.sort(
484
+ (a, b) =>
485
+ (typeof a.end_point_number === 'number'
486
+ ? a.end_point_number
487
+ : 0) -
488
+ (typeof b.end_point_number === 'number'
489
+ ? b.end_point_number
490
+ : 0)
491
+ )"
492
+ :key="index"
493
+ >
494
+ <RmTextHighlight
495
+ :text="obj.end_point_number + '. ' + obj.secondary_end_point_en"
496
+ class="oip_body"
497
+ />
498
+ </div>
499
+ </div>
500
+ <p v-else class="oip_body">{{ ifNoRes(null) }}</p>
501
+ </div>
502
+
503
+ <!-- ALL DATES — every date field the EUCT index exposes for
504
+ this trial, split in two tables: trial-level (root) and
505
+ per Member State (countries[]). Field names use the raw
506
+ index keys so the table doubles as documentation. -->
507
+ <div class="oip_card oip_grid gap-16" id="all-dates">
508
+ <h1 class="kk_title_medium_2">{{ $t('oipScience.trial.allDatesTitle') }}</h1>
509
+
510
+ <!-- Table 1 — Niveau essai -->
511
+ <div class="all-dates-section">
512
+ <h2 class="all-dates-h2">{{ $t('oipScience.trial.trialLevelRoot') }}</h2>
513
+ <div class="all-dates-wrap">
514
+ <table class="all-dates-table">
515
+ <thead>
516
+ <tr>
517
+ <th style="width: 34%">{{ $t('oipScience.trial.field') }}</th>
518
+ <th style="width: 18%">{{ $t('oipScience.trial.dateForThisTrial') }}</th>
519
+ <th>{{ $t('oipScience.trial.whatItRepresents') }}</th>
520
+ </tr>
521
+ </thead>
522
+ <tbody>
523
+ <tr v-for="row in trialLevelDates" :key="row.field">
524
+ <td><code class="ad-field-code">{{ row.field }}</code></td>
525
+ <td>
526
+ <span :class="['ad-value', !row.value && 'ad-na']">
527
+ {{ row.value ? ifNoRes(row.value, 'date') : '-' }}
528
+ </span>
529
+ </td>
530
+ <td>{{ row.desc }}</td>
531
+ </tr>
532
+ </tbody>
533
+ </table>
534
+ </div>
535
+ </div>
536
+
537
+ <!-- Table 2 — Niveau État membre -->
538
+ <div class="all-dates-section">
539
+ <h2 class="all-dates-h2">{{ $t('oipScience.trial.memberStateLevel') }} (<code>countries[]</code>)</h2>
540
+ <div class="all-dates-wrap">
541
+ <table class="all-dates-table">
542
+ <thead>
543
+ <tr>
544
+ <th style="width: 34%">{{ $t('oipScience.trial.field') }}</th>
545
+ <th style="width: 30%">{{ $t('oipScience.trial.datesPerMemberState') }}</th>
546
+ <th>{{ $t('oipScience.trial.whatItRepresents') }}</th>
547
+ </tr>
548
+ </thead>
549
+ <tbody>
550
+ <tr v-for="row in countryLevelDates" :key="row.field">
551
+ <td><code class="ad-field-code">{{ row.field }}</code></td>
552
+ <td>
553
+ <ul v-if="row.values.length" class="ad-ms-list">
554
+ <li v-for="r in row.values" :key="r.country">
555
+ <span class="ad-flag">{{ countryFlag(r.country) }}</span>
556
+ <span class="ad-country">{{ r.country }}</span>
557
+ <span :class="['ad-value', !r.date && 'ad-na']">{{ r.date ? ifNoRes(r.date, 'date') : '-' }}</span>
558
+ </li>
559
+ </ul>
560
+ <span v-else class="ad-value ad-na">-</span>
561
+ </td>
562
+ <td>{{ row.desc }}</td>
563
+ </tr>
564
+ </tbody>
565
+ </table>
566
+ </div>
567
+ </div>
568
+ </div>
569
+
570
+ <!-- Link -->
571
+ <div class="oip_card oip_grid gap-24" id="links">
572
+ <h1 class="kk_title_medium_2">{{ $t('oipScience.trial.links') }}</h1>
573
+
574
+ <div class="oip_grid gap-4">
575
+ <div class="oip_row v-center gap-4">
576
+ <RmIcon name="Link" :size="12" color="var(--cool-grey)" />
577
+ <h1 class="oip_underline">
578
+ {{ $t('oipScience.trial.linkTo') }} <b>{{ resultsQueryEuct.euct_number }}</b>
579
+ </h1>
580
+ </div>
581
+
582
+ <a
583
+ class="kk_link_btn_underline"
584
+ :href="
585
+ 'https://euclinicaltrials.eu/search-for-clinical-trials/?lang=en&EUCT=' +
586
+ resultsQueryEuct.euct_number
587
+ "
588
+ target="_blank"
589
+ >
590
+ <span>
591
+ https://euclinicaltrials.eu/search-for-clinical-trials/?lang=en&EUCT=<b
592
+ >{{ resultsQueryEuct.euct_number }}</b
593
+ >
594
+ </span>
595
+ </a>
596
+ </div>
597
+
598
+ <div class="oip_grid gap-4" v-if="resultsQueryEuct.nct_number">
599
+ <div class="oip_row v-center gap-4">
600
+ <RmIcon name="Link" :size="12" color="var(--cool-grey)" />
601
+ <h1 class="oip_underline">
602
+ {{ $t('oipScience.trial.linkTo') }} <b>{{ resultsQueryEuct.nct_number }}</b>
603
+ </h1>
604
+ </div>
605
+
606
+ <a
607
+ class="kk_link_btn_underline"
608
+ :href="
609
+ 'https://clinicaltrials.gov/ct2/show/' +
610
+ resultsQueryEuct.nct_number
611
+ "
612
+ target="_blank"
613
+ >
614
+ <span>
615
+ https://clinicaltrials.gov/ct2/show/<b>
616
+ {{ resultsQueryEuct.nct_number }}
617
+ </b>
618
+ </span>
619
+ </a>
620
+ </div>
621
+ </div>
622
+ </div>
623
+ </div>
624
+ </div>
625
+ </div>
626
+
627
+ <RmLoadingState v-if="!loaded || isLoadingQueryEuct" :lines="3" :chart-height="240" />
628
+ <RmEmptyState
629
+ v-else-if="!resultsQueryEuct"
630
+ :title="$t('oipScience.common.noDataTitle')"
631
+ :text="$t('oipScience.common.noDataText')"
632
+ />
633
+ </div>
634
+ </template>
635
+
636
+ <script setup lang="ts">
637
+ import useEuct from '../../../oip-science/composables/useEuct';
638
+ import { getCountryEmoji, getSexe, ifNoRes, removeDuplicate, setColorStatus } from '../../../oip-science/utils/styleAndFormat';
639
+ import { valueTable } from '../../../oip-science/utils/valueTable';
640
+
641
+ const props = withDefaults(
642
+ defineProps<{
643
+ /** Numero EUCT de l'essai. */
644
+ id: string;
645
+ /** Termes de la recherche en cours, surlignes dans la fiche. */
646
+ highlight?: string[];
647
+ lang?: 'fr' | 'en';
648
+ }>(),
649
+ { highlight: () => [], lang: 'fr' },
650
+ );
651
+
652
+ // Le sommaire defile le conteneur de la fiche (cadre ou tiroir de projet).
653
+ const root = ref<HTMLElement | null>(null);
654
+ const { chaptersOffset, stickyTop, scrollToChapter } = useOipChapterScroll(root);
655
+ const chaptersList = computed(() => [
656
+ {
657
+ label: t('oipScience.trial.summary'),
658
+ value: 'summary',
659
+ },
660
+
661
+ {
662
+ label: t('oipScience.trial.trialDetails'),
663
+ value: 'trial-details',
664
+ },
665
+ {
666
+ label: t('oipScience.trial.oipDiscoveryTags'),
667
+ value: 'tags',
668
+ },
669
+ {
670
+ label: t('oipScience.trial.conditions'),
671
+ value: 'conditions',
672
+ },
673
+ {
674
+ label: t('oipScience.trial.products'),
675
+ value: 'products',
676
+ },
677
+ {
678
+ label: t('oipScience.trial.objectives'),
679
+ value: 'objectives',
680
+ },
681
+
682
+ {
683
+ label: t('oipScience.trial.eligibilityCriteria'),
684
+ value: 'eligibility-criteria',
685
+ },
686
+
687
+ {
688
+ label: t('oipScience.trial.enrollment'),
689
+ value: 'enrolment',
690
+ },
691
+ {
692
+ label: t('oipScience.trial.endPoints'),
693
+ value: 'end_points',
694
+ },
695
+ {
696
+ label: t('oipScience.trial.allDatesTitle'),
697
+ value: 'all-dates',
698
+ },
699
+ {
700
+ label: t('oipScience.trial.links'),
701
+ value: 'links',
702
+ }]);
703
+ const { t } = useI18n();
704
+ const { findEuctById, resultsQueryEuct, isLoadingQueryEuct } = useEuct();
705
+
706
+ // Some EUCT date fields ship as 'DD/MM/YYYY' strings
707
+ // (`trial_end_or_early_termination`) rather than ISO, and empty
708
+ // strings should be treated as missing. Normalize to ISO yyyy-mm-dd
709
+ // so `ifNoRes(value, 'date')` (which calls `new Date(value)`) parses
710
+ // them correctly.
711
+ const normEuctDate = (v: any): string | null => {
712
+ if (v == null) return null;
713
+ const s = String(v).trim();
714
+ if (!s) return null;
715
+ const dmy = s.match(/^(\d{2})\/(\d{2})\/(\d{4})$/);
716
+ if (dmy) return `${dmy[3]}-${dmy[2]}-${dmy[1]}`;
717
+ return s;
718
+ };
719
+
720
+ // CTIS notifies Start of Trial / End of Recruitment / End of Trial
721
+ // PER MEMBER STATE. We surface every MS the trial touches so the
722
+ // reader sees the full timeline rather than a single aggregated
723
+ // value. Helpers below feed the per-MS rows of the All Dates table.
724
+ // Returns one entry per Member State the trial declares, even if it
725
+ // hasn't reported the requested date yet — the missing row falls back
726
+ // to '—' in the template, which matches the user's expectation that
727
+ // every country gets a line.
728
+ const perCountryDate = (field: string) => {
729
+ const cs = (resultsQueryEuct.value as any)?.countries;
730
+ if (!Array.isArray(cs)) return [] as Array<{ country: string; date: string | null }>;
731
+ return cs
732
+ .map((c: any) => ({
733
+ country: c?.country_name || '',
734
+ date: normEuctDate(c?.[field]),
735
+ }))
736
+ .filter((row) => row.country);
737
+ };
738
+
739
+ // All Dates — table 1 (trial-level, root fields) is driven by this list
740
+ // so adding/removing a row is a one-line change. Order follows the
741
+ // index mapping screenshot. The two `_y` fields are declared in the
742
+ // EUCT mapping but never populated; we keep them visible for
743
+ // completeness and mark them as such in the description column.
744
+ const trialLevelDates = computed(() => {
745
+ const r = (resultsQueryEuct.value || {}) as any;
746
+ return [
747
+ {
748
+ field: 'decision_date',
749
+ value: normEuctDate(r.decision_date),
750
+ desc: t('oipScience.trial.euDecisionDateDesc'),
751
+ },
752
+ {
753
+ field: 'first_submitted_date',
754
+ value: normEuctDate(r.first_submitted_date),
755
+ desc: t('oipScience.trial.euFirstSubmittedDesc'),
756
+ },
757
+ {
758
+ field: 'last_update_date',
759
+ value: normEuctDate(r.last_update_date),
760
+ desc: t('oipScience.trial.euLastUpdateDesc'),
761
+ },
762
+ {
763
+ field: 'start_date',
764
+ value: normEuctDate(r.start_date),
765
+ desc: t('oipScience.trial.euStartDateDesc'),
766
+ },
767
+ {
768
+ field: 'end_date',
769
+ value: normEuctDate(r.end_date),
770
+ desc: t('oipScience.trial.euEndDateDesc'),
771
+ },
772
+ {
773
+ field: 'global_end_date',
774
+ value: normEuctDate(r.global_end_date),
775
+ desc: t('oipScience.trial.euGlobalEndDateDesc'),
776
+ },
777
+ {
778
+ field: 'estimated_recruitment_start_date_in_eu',
779
+ value: normEuctDate(r.estimated_recruitment_start_date_in_eu),
780
+ desc: t('oipScience.trial.euEstRecruitmentStartDesc'),
781
+ },
782
+ {
783
+ field: 'estimated_trial_end_date_in_eu',
784
+ value: normEuctDate(r.estimated_trial_end_date_in_eu),
785
+ desc: t('oipScience.trial.euEstEndEuDesc'),
786
+ },
787
+ {
788
+ field: 'estimated_trial_end_date_in_eu_y',
789
+ value: normEuctDate(r.estimated_trial_end_date_in_eu_y),
790
+ desc: t('oipScience.trial.euEstEndEuYDesc'),
791
+ },
792
+ {
793
+ field: 'estimated_trial_global_end_date',
794
+ value: normEuctDate(r.estimated_trial_global_end_date),
795
+ desc: t('oipScience.trial.euEstGlobalEndDesc'),
796
+ },
797
+ {
798
+ field: 'estimated_trial_global_end_date_y',
799
+ value: normEuctDate(r.estimated_trial_global_end_date_y),
800
+ desc: t('oipScience.trial.euEstGlobalEndYDesc'),
801
+ },
802
+ ];
803
+ });
804
+
805
+ // All Dates — table 2 (per Member State). Each row covers one CTIS
806
+ // date that lives inside `countries[]`. `perCountryDate(field)`
807
+ // returns one entry per MS the trial declares; the template falls
808
+ // back to '—' for countries that haven't reported the field yet.
809
+ const countryLevelDates = computed(() => [
810
+ {
811
+ field: 'trial_decision_date',
812
+ values: perCountryDate('trial_decision_date'),
813
+ desc: t('oipScience.trial.msDecisionDateDesc'),
814
+ },
815
+ {
816
+ field: 'trial_start_date',
817
+ values: perCountryDate('trial_start_date'),
818
+ desc: t('oipScience.trial.msStartDateDesc'),
819
+ },
820
+ {
821
+ field: 'trial_last_update',
822
+ values: perCountryDate('trial_last_update'),
823
+ desc: t('oipScience.trial.msLastUpdateDesc'),
824
+ },
825
+ {
826
+ field: 'trial_restart',
827
+ values: perCountryDate('trial_restart'),
828
+ desc: t('oipScience.trial.msRestartDesc'),
829
+ },
830
+ {
831
+ field: 'trial_temporary_halt',
832
+ values: perCountryDate('trial_temporary_halt'),
833
+ desc: t('oipScience.trial.msTemporaryHaltDesc'),
834
+ },
835
+ {
836
+ field: 'recruitment_start',
837
+ values: perCountryDate('recruitment_start'),
838
+ desc: t('oipScience.trial.msRecruitmentStartDesc'),
839
+ },
840
+ {
841
+ field: 'recruitment_end',
842
+ values: perCountryDate('recruitment_end'),
843
+ desc: t('oipScience.trial.msRecruitmentEndDesc'),
844
+ },
845
+ {
846
+ field: 'recruitment_restart',
847
+ values: perCountryDate('recruitment_restart'),
848
+ desc: t('oipScience.trial.msRecruitmentRestartDesc'),
849
+ },
850
+ ]);
851
+
852
+ // Inline EU/EEA flag lookup so we control exactly what lands in the
853
+ // dedicated flag column. The shared `getCountryEmoji` helper returns
854
+ // 'emoji Country Name' (double-space joined) — even with a split
855
+ // that produced the country name printed twice in production, so we
856
+ // bypass it here.
857
+ const COUNTRY_FLAG: Record<string, string> = {
858
+ Austria: '🇦🇹', Belgium: '🇧🇪', Bulgaria: '🇧🇬', Croatia: '🇭🇷',
859
+ Cyprus: '🇨🇾', Czechia: '🇨🇿', Denmark: '🇩🇰', Estonia: '🇪🇪',
860
+ Finland: '🇫🇮', France: '🇫🇷', Germany: '🇩🇪', Greece: '🇬🇷',
861
+ Hungary: '🇭🇺', Iceland: '🇮🇸', Ireland: '🇮🇪', Italy: '🇮🇹',
862
+ Latvia: '🇱🇻', Liechtenstein: '🇱🇮', Lithuania: '🇱🇹', Luxembourg: '🇱🇺',
863
+ Malta: '🇲🇹', Netherlands: '🇳🇱', Norway: '🇳🇴', Poland: '🇵🇱',
864
+ Portugal: '🇵🇹', Romania: '🇷🇴', Slovakia: '🇸🇰', Slovenia: '🇸🇮',
865
+ Spain: '🇪🇸', Sweden: '🇸🇪',
866
+ };
867
+ const countryFlag = (country: string) => COUNTRY_FLAG[country] || '';
868
+
869
+ const sitesCountry = ref<string | null>(null);
870
+ // :::::::::::::::::::::::::::::
871
+ // :::::::::::::::::::::::::::::
872
+ // Le composable part a `false` : sans ce drapeau, l'etat vide sortirait avant la requete.
873
+ const loaded = ref(false);
874
+
875
+ // QUERY
876
+ async function load() {
877
+ if (!props.id) return;
878
+ await findEuctById({ id: props.id.toString() });
879
+ loaded.value = true;
880
+ }
881
+
882
+ onMounted(load);
883
+ watch(() => props.id, load);
884
+ // :::::::::::::::::::::::::::::
885
+ // BRANCHES ET INTERVENTIONS
886
+ const getColumnType = computed(() => {
887
+ return ['Country', '', 'Status', 'Link'];
888
+ });
889
+
890
+ const getHeader = computed(() => {
891
+ return [t('oipScience.trial.country'), t('oipScience.trial.subjectsCount'), t('oipScience.trial.status'), t('oipScience.trial.sitesCount')];
892
+
893
+ });
894
+
895
+ const getContent = computed(() => {
896
+ if (
897
+ resultsQueryEuct.value &&
898
+ resultsQueryEuct.value?.countries &&
899
+ resultsQueryEuct.value?.countries?.length > 0
900
+ ) {
901
+ return resultsQueryEuct.value.countries.map((item: any) => ({
902
+ values: [
903
+ item.country_name,
904
+ item.planned_number_of_subjects,
905
+ item.trial_current_status,
906
+ { country: item.country_name, rawData: resultsQueryEuct.value as any },
907
+ ],
908
+ }));
909
+ } else {
910
+ return [];
911
+ }
912
+ });
913
+
914
+ const countriesTable = computed(() =>
915
+ valueTable(getHeader.value, getContent.value, getColumnType.value, {
916
+ align: ["left", "right", "left", "right"],
917
+ }),
918
+ );
919
+
920
+ const getRareDiseases = computed(() => {
921
+ return removeDuplicate(
922
+ (resultsQueryEuct.value &&
923
+ resultsQueryEuct.value.rare_diseases &&
924
+ resultsQueryEuct.value.rare_diseases.map(
925
+ (res: any) => res.concat_disease_name_orpha_code
926
+ )) ??
927
+ []
928
+ );
929
+ });
930
+
931
+ const getGenomicsSymbols = computed(() => {
932
+ if (
933
+ resultsQueryEuct.value &&
934
+ resultsQueryEuct.value.genomics &&
935
+ resultsQueryEuct.value.genomics.length > 0 ) {
936
+ return resultsQueryEuct.value.genomics.map((g: any) => g.symbol);
937
+ }
938
+
939
+ return [];
940
+ });
941
+
942
+ </script>
943
+
944
+ <style scoped>
945
+ .trial-id-badge {
946
+ display: inline-block;
947
+ padding: 2px 10px;
948
+ border-radius: 6px;
949
+ background: var(--bg-surface-2);
950
+ color: var(--text-secondary);
951
+ font-size: 12px;
952
+ font-weight: 600;
953
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
954
+ letter-spacing: 0.2px;
955
+ }
956
+
957
+ /* All Dates section — reference tables inside the trial detail page */
958
+ .all-dates-section {
959
+ display: flex;
960
+ flex-direction: column;
961
+ gap: 8px;
962
+ }
963
+ .all-dates-section + .all-dates-section {
964
+ margin-top: 8px;
965
+ }
966
+ .all-dates-h2 {
967
+ font-size: 13px;
968
+ font-weight: 700;
969
+ color: var(--text-secondary);
970
+ text-transform: uppercase;
971
+ letter-spacing: 0.04em;
972
+ margin: 0;
973
+ }
974
+ .all-dates-h2 code {
975
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
976
+ font-size: 12px;
977
+ text-transform: none;
978
+ background: var(--bg-surface-2);
979
+ color: var(--text-secondary);
980
+ padding: 1px 6px;
981
+ border-radius: 4px;
982
+ letter-spacing: 0;
983
+ }
984
+ .ad-field-code {
985
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
986
+ font-size: 12px;
987
+ font-weight: 600;
988
+ color: var(--primary);
989
+ background: var(--bg-surface-2);
990
+ padding: 2px 6px;
991
+ border-radius: 4px;
992
+ word-break: break-all;
993
+ }
994
+ .all-dates-wrap {
995
+ overflow-x: auto;
996
+ }
997
+ .all-dates-table {
998
+ width: 100%;
999
+ border-collapse: collapse;
1000
+ font-size: 13px;
1001
+ }
1002
+ .all-dates-table thead th {
1003
+ background: var(--bg-surface-2);
1004
+ color: var(--text-primary);
1005
+ font-weight: 600;
1006
+ text-align: left;
1007
+ padding: 10px 12px;
1008
+ border-bottom: 2px solid var(--border-default);
1009
+ font-size: 12px;
1010
+ letter-spacing: 0.02em;
1011
+ }
1012
+ .all-dates-table tbody td {
1013
+ padding: 12px;
1014
+ border-bottom: 1px solid var(--border-default);
1015
+ vertical-align: top;
1016
+ color: var(--text-primary);
1017
+ line-height: 1.5;
1018
+ }
1019
+ .all-dates-table tbody tr:last-child td {
1020
+ border-bottom: 0;
1021
+ }
1022
+ .ad-field {
1023
+ font-weight: 600;
1024
+ color: var(--primary);
1025
+ }
1026
+ .ad-ms-list {
1027
+ list-style: none;
1028
+ margin: 0;
1029
+ padding: 0;
1030
+ display: flex;
1031
+ flex-direction: column;
1032
+ gap: 4px;
1033
+ }
1034
+ .ad-ms-list li {
1035
+ display: grid;
1036
+ grid-template-columns: 22px 140px 1fr;
1037
+ align-items: baseline;
1038
+ gap: 8px;
1039
+ font-size: 12.5px;
1040
+ }
1041
+ .ad-ms-list .ad-value {
1042
+ white-space: nowrap;
1043
+ }
1044
+ .ad-flag {
1045
+ font-size: 14px;
1046
+ line-height: 1;
1047
+ text-align: center;
1048
+ }
1049
+ .ad-country {
1050
+ color: var(--text-secondary);
1051
+ font-weight: 500;
1052
+ white-space: nowrap;
1053
+ overflow: hidden;
1054
+ text-overflow: ellipsis;
1055
+ }
1056
+ .ad-value {
1057
+ font-weight: 600;
1058
+ color: var(--text-primary);
1059
+ font-size: 13px;
1060
+ }
1061
+ .ad-na {
1062
+ color: var(--text-muted);
1063
+ font-weight: 500;
1064
+ font-style: italic;
1065
+ font-size: 12px;
1066
+ }
1067
+
1068
+ /* Le sommaire colle sous l'en-tete de fiche (cf. useOipChapterScroll). */
1069
+ .chapters_col {
1070
+ flex: 0 0 auto;
1071
+ }
1072
+ .chapters_sticky {
1073
+ position: sticky;
1074
+ }
1075
+ </style>