@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,719 @@
1
+ <!--
2
+ Filtre a valeurs d'une liste (anciennement ElasticFilter de France Augmented
3
+ Science). Ses valeurs et sa clause Elasticsearch vont dans le contexte de la
4
+ liste parente (c-filterContext), plus dans les stores par route de FAS.
5
+ -->
6
+ <template>
7
+ <div @click="lauchQuery">
8
+ <!---->
9
+
10
+ <RmComplexSelect
11
+ v-if="typeSelection == 'complex'"
12
+ select-all-count
13
+ :has-more="filtersData.length === sizeTarget"
14
+ :loading="isLoadingQuery"
15
+ :label="label || filterName"
16
+ :label-info="haveInfo"
17
+ v-model="selectedItem"
18
+
19
+ :options="filtersData"
20
+ :show-select-all="show_select_all"
21
+ @search="changeAddInputValue"
22
+ :presets="toPresets(isSmartSelection ? getSourceSmartSelection : null)"
23
+ :filter-search="
24
+ otherResearchField.length || otherResearchNestedField.length
25
+ ? false
26
+ : true
27
+ " :lang="$i18n.locale === 'en' ? 'en' : 'fr'" />
28
+
29
+ <RmMultipleSelect
30
+
31
+ v-if="typeSelection == 'multiple'"
32
+ :loading="isLoadingQuery"
33
+ :label="label || filterName"
34
+ :label-info="haveInfo"
35
+ :search="false"
36
+ v-model="selectedItem"
37
+ :options="filtersData"
38
+ @search="changeAddInputValue"
39
+ :filter-search="
40
+ otherResearchField.length || otherResearchNestedField.length
41
+ ? false
42
+ : true
43
+ " :lang="$i18n.locale === 'en' ? 'en' : 'fr'" />
44
+ </div>
45
+ </template>
46
+ <script setup lang="ts">
47
+ import smartSelection from "../../../oip-science/assets/smartSelection.json";
48
+ import useOipElastic from "../../../oip-science/composables/useOipElastic";
49
+ import { useOipFilterContext } from "../../../oip-science/utils/c-filterContext";
50
+ import { toPresets } from "../../../oip-science/utils/toPresets";
51
+ import { caseInsensitive, frDiseaseRegex } from "../../../oip-science/utils/frDiseaseTerms";
52
+
53
+ const filterContext = useOipFilterContext();
54
+
55
+ const { queryFilter, querySearchFilter } = useOipElastic();
56
+
57
+ const emit = defineEmits(["changeElement"]);
58
+
59
+ const props = defineProps({
60
+ filterValues: {
61
+ type: [Array, String, Function],
62
+ required: true,
63
+ },
64
+ field: {
65
+ type: String,
66
+ required: false,
67
+ default: null,
68
+ },
69
+ filterName: {
70
+ type: String,
71
+ required: true,
72
+ },
73
+ label: {
74
+ type: String,
75
+ default: null,
76
+ },
77
+ sizeTarget: {
78
+ type: Number,
79
+ required: false,
80
+ default: 50,
81
+ },
82
+ nestedField: {
83
+ type: String,
84
+ required: false,
85
+ default: null,
86
+ },
87
+ indexQuery: {
88
+ type: String,
89
+ required: true,
90
+ },
91
+ isSmartSelection: {
92
+ type: Boolean,
93
+ required: false,
94
+ default: false,
95
+ },
96
+ typeSelection: {
97
+ type: String,
98
+ required: false,
99
+ default: "complex",
100
+ },
101
+ matchQuery: {
102
+ type: Array,
103
+ required: false,
104
+ default: [],
105
+ },
106
+ keyName: {
107
+ type: String,
108
+ required: false,
109
+ default: null,
110
+ },
111
+ includeFields: {
112
+ type: Array,
113
+ required: false,
114
+ default: [],
115
+ },
116
+ excludeFields: {
117
+ type: Array,
118
+ required: false,
119
+ default: [],
120
+ },
121
+ cluster: {
122
+ type: String,
123
+ required: false,
124
+ default: null,
125
+ },
126
+ fuziness: {
127
+ type: String,
128
+ required: false,
129
+ default: "AUTO",
130
+ },
131
+
132
+ otherResearchField: {
133
+ type: [String, Array],
134
+ required: false,
135
+ default: [],
136
+ },
137
+ otherResearchNestedField: {
138
+ type: [String, Array],
139
+ required: false,
140
+ default: [],
141
+ },
142
+ // Filtres maladie/oncologie : la saisie francaise cherche aussi l'anglais (cf. frDiseaseRegex)
143
+ frDiseaseSearch: {
144
+ type: Boolean,
145
+ default: false,
146
+ },
147
+ // props ajouté pour avoir un point info dans le titre du filtre
148
+ haveInfo: {
149
+ type: Boolean,
150
+ default: false,
151
+ },
152
+
153
+ sourceSmartSelection: {
154
+ type: String,
155
+ required: false,
156
+ default: "Countries",
157
+ },
158
+ forceList: {
159
+ type: Boolean,
160
+ required: false,
161
+ default: false,
162
+ },
163
+ show_select_all: {
164
+ type: Boolean,
165
+ required: false,
166
+ default: true,
167
+ },
168
+ mustNot: {
169
+ type: Array,
170
+ required: false,
171
+ default: null,
172
+ },
173
+ // Stable, locale-independent key used to read/write filterStore entries.
174
+ // The page's `getStore('Sponsors')` lookup uses an English hard-coded key
175
+ // while `filterName` is `$t('common.sponsors')` = "Promoteurs" in French.
176
+ // Without `storeKey`, Filter.vue would write under "Promoteurs" and the
177
+ // parent would read under "Sponsors" → no match → saved scopes never
178
+ // restore. Pages should pass `storeKey="Sponsors"` matching their
179
+ // getStore call. Falls back to `filterName` when omitted.
180
+ storeKey: {
181
+ type: String,
182
+ required: false,
183
+ default: null,
184
+ },
185
+ // Pass true when the field is numeric/keyword-direct (no `.keyword` sub-field).
186
+ // Required for `long`/`integer`/`float` ES fields like PHRC.period.
187
+ noKeyword: {
188
+ type: Boolean,
189
+ required: false,
190
+ default: false,
191
+ },
192
+ // Index EUCT (champs sans nested) ; FAS le lisait dans la route (`/euct`).
193
+ euct: {
194
+ type: Boolean,
195
+ required: false,
196
+ default: false,
197
+ },
198
+ });
199
+
200
+ // Returns the field path to use in queries / aggregations.
201
+ // When `noKeyword` is true, the raw field is used (numeric or already-keyword
202
+ // fields); otherwise `.keyword` is appended (text fields with a keyword sub-field).
203
+ const kw = (field: string) => (props.noKeyword ? field : `${field}.keyword`);
204
+
205
+ // Use `storeKey` when provided so reads (via the parent's `getStore(key)`)
206
+ // and writes (here) target the same filterStore entry regardless of the
207
+ // current locale. Otherwise fall back to `filterName` for back-compat.
208
+ const effectiveStoreKey = computed(
209
+ () => props.storeKey || props.filterName,
210
+ );
211
+
212
+ const search = ref("" as string);
213
+ const filtersData = ref([] as any);
214
+ const isLoadingQuery = ref(false);
215
+
216
+ const selectedItem = ref([] as any);
217
+
218
+ const timeoutId = ref(null as any);
219
+
220
+ const dynamicFuzzyThreshold = computed(() => {
221
+ const cleanInput = search.value.replace(/[+\/!]/g, "").trim();
222
+ // Pour les codes courts (≤ 6 caractères), threshold très permissif
223
+ if (cleanInput.length <= 6) {
224
+ return 0.9;
225
+ }
226
+ // Pour les recherches moyennes (7-10 caractères), threshold moyen
227
+ else if (cleanInput.length <= 10) {
228
+ return 0.7;
229
+ }
230
+ // Pour les recherches longues, threshold strict
231
+ else {
232
+ return 0.5;
233
+ }
234
+ });
235
+
236
+ // Le pays de retour de FAS (layoutConfig.returnCountry) n'existe pas hors de l'app.
237
+ onMounted(() => {
238
+ selectedItem.value =
239
+ props.filterValues.length > 0 ? props.filterValues : [];
240
+ });
241
+
242
+ const getSourceSmartSelection = computed(() => {
243
+ switch (props.sourceSmartSelection) {
244
+ case "Pubmed Journal Name":
245
+ return smartSelection.orphanetPubmedJournalSelection;
246
+ case "country_eu":
247
+ return smartSelection.countries_eu;
248
+ default:
249
+ return smartSelection.countries;
250
+ }
251
+ });
252
+
253
+ // Sync selectedItem when the parent passes a new `filterValues` prop —
254
+ // notably when a saved scope is recalled and the parent re-renders with
255
+ // the restored values. Without this watch, `selectedItem` is only read
256
+ // from the prop once in `onMounted`, so any later prop change is invisible
257
+ // and the UI stays empty even though the store has the recalled entries.
258
+ watch(
259
+ () => props.filterValues,
260
+ (newVal) => {
261
+ const target = Array.isArray(newVal) ? newVal : [];
262
+ // Skip no-op syncs (avoids feedback loops with the watch below).
263
+ if (JSON.stringify(selectedItem.value) === JSON.stringify(target)) return;
264
+ selectedItem.value = target;
265
+ },
266
+ { deep: true },
267
+ );
268
+
269
+ watch(
270
+ () => selectedItem.value,
271
+ async (val, oldVal) => {
272
+ if (val.length > 0) {
273
+ filterContext.setAppFilters({
274
+ filter: effectiveStoreKey.value,
275
+ keyName: props.keyName,
276
+ values: val,
277
+ });
278
+ } else if (oldVal && oldVal.length > 0) {
279
+ // Only reset when going from a real selection to empty — i.e. the
280
+ // user actually cleared the filter. Skip the noisy 0→0 transitions
281
+ // that fire on mount because `selectedItem` is initialized to `[]`
282
+ // and then re-assigned to (a possibly-still-empty) `props.filterValues`.
283
+ // Without this guard, recalled scope entries get wiped by the watch
284
+ // before the prop has a chance to deliver the restored values.
285
+ filterContext.resetSelectedFilters({
286
+ filter: effectiveStoreKey.value,
287
+ keyName: props.keyName,
288
+ });
289
+ }
290
+
291
+ setSearch(val);
292
+ }
293
+ );
294
+
295
+ const changeAddInputValue = (val: string) => {
296
+ clearTimeout(timeoutId.value);
297
+ search.value = val;
298
+ timeoutId.value = setTimeout(() => {
299
+ lauchQuery();
300
+ }, 300);
301
+ };
302
+
303
+ const setSearch = async (search: any) => {
304
+ let obj = null;
305
+
306
+ if (search.length > 0 && props.field) {
307
+ obj = {
308
+ terms: {
309
+ [kw(props.field)]: search,
310
+ },
311
+ };
312
+ }
313
+
314
+ if (search.length > 0 && props.nestedField) {
315
+ // Pour EUCT, pas de nested
316
+ if (props.euct) {
317
+ obj = {
318
+ terms: {
319
+ [`${props.nestedField}.keyword`]: search,
320
+ },
321
+ };
322
+ } else {
323
+ obj = {
324
+ nested: {
325
+ path: props.nestedField.split(".").slice(0, -1).join("."),
326
+ query: {
327
+ terms: {
328
+ [`${props.nestedField}.keyword`]: search,
329
+ },
330
+ },
331
+ },
332
+ };
333
+ }
334
+ }
335
+
336
+ if (obj != null) {
337
+ filterContext.setQueryFilters({
338
+ filter: props.filterName,
339
+ values: obj,
340
+ keyName: props.keyName,
341
+ });
342
+ } else {
343
+ filterContext.removeQueryFilter({
344
+ filter: props.filterName,
345
+ keyName: props.keyName,
346
+ });
347
+ }
348
+ };
349
+
350
+ const lauchQuery = async () => {
351
+ isLoadingQuery.value = true;
352
+ if (search.value != "") {
353
+ await lauchQueryWithSearch();
354
+ } else {
355
+ await launchQueryWithoutSearch();
356
+ }
357
+
358
+ isLoadingQuery.value = false;
359
+ };
360
+
361
+ const aggregationsQuery = computed(() => {
362
+ let obj;
363
+
364
+ if (props.field) {
365
+ obj = {
366
+ [`${props.field}`]: {
367
+ terms: {
368
+ field: kw(props.field),
369
+
370
+ size: props.sizeTarget,
371
+ order: {
372
+ _key: "asc",
373
+ },
374
+ },
375
+ },
376
+ };
377
+
378
+ if (props.includeFields.length > 0) {
379
+ obj[`${props.field}`].terms.include = props.includeFields;
380
+ }
381
+ if (props.excludeFields.length > 0) {
382
+ obj[`${props.field}`].terms.exclude = props.excludeFields;
383
+ }
384
+ }
385
+
386
+ if (props.nestedField) {
387
+ // Pour EUCT, pas de nested
388
+ if (props.euct) {
389
+ obj = {
390
+ [`${props.nestedField}`]: {
391
+ terms: {
392
+ field: `${props.nestedField}.keyword`,
393
+ size: props.sizeTarget,
394
+ order: {
395
+ _key: "asc",
396
+ },
397
+ },
398
+ },
399
+ };
400
+ } else {
401
+ obj = {
402
+ [`${props.nestedField.split(".")[0]}`]: {
403
+ nested: {
404
+ path: props.nestedField.split(".").slice(0, -1).join("."),
405
+ },
406
+ aggs: {
407
+ [`${props.nestedField}`]: {
408
+ terms: {
409
+ field: `${props.nestedField}.keyword`,
410
+ size: props.sizeTarget,
411
+ order: {
412
+ _key: "asc",
413
+ },
414
+ },
415
+ },
416
+ },
417
+ },
418
+ };
419
+ }
420
+
421
+ if (props.includeFields.length > 0) {
422
+ if (props.euct) {
423
+ obj[`${props.nestedField}`].terms.include = props.includeFields;
424
+ } else {
425
+ obj[`${props.nestedField.split(".").slice(0, -1).join(".")}`].aggs[
426
+ `${props.nestedField}`
427
+ ].terms.include = props.includeFields;
428
+ }
429
+ }
430
+ if (props.excludeFields.length > 0) {
431
+ if (props.euct) {
432
+ obj[`${props.nestedField}`].terms.exclude = props.excludeFields;
433
+ } else {
434
+ obj[`${props.nestedField.split(".").slice(0, -1).join(".")}`].aggs[
435
+ `${props.nestedField}`
436
+ ].terms.exclude = props.excludeFields;
437
+ }
438
+ }
439
+ }
440
+
441
+ return obj;
442
+ });
443
+
444
+ const aggregationsSearchQuery = computed(() => {
445
+ let obj;
446
+
447
+ // Normalise les codes médicaux en majuscules pour matcher les champs keyword
448
+ const normalizedQuery = search.value.replace(/\b(orpha|nct|icd|atc|ema|fda)([\w:-]*)/gi, (match: string, prefix: string, suffix: string) => {
449
+ return prefix.toUpperCase() + suffix.toUpperCase();
450
+ });
451
+
452
+ // Détecte si la valeur contient des wildcards
453
+ const hasWildcard = search.value.includes('*') || search.value.includes('?');
454
+
455
+ // Construit un pattern case-insensitive pour chaque caractère
456
+ const makeCaseInsensitive = (str: string) => {
457
+ return str.split('').map(char => {
458
+ if (/[a-zA-Z]/.test(char)) {
459
+ return `[${char.toLowerCase()}${char.toUpperCase()}]`;
460
+ }
461
+ // Échapper les caractères spéciaux regex
462
+ if (/[.*+?^${}()|[\]\\]/.test(char)) {
463
+ return `\\${char}`;
464
+ }
465
+ return char;
466
+ }).join('');
467
+ };
468
+
469
+ let includePattern = '';
470
+
471
+ if (hasWildcard) {
472
+ // Convertir les wildcards et rendre case-insensitive
473
+ const withWildcards = normalizedQuery.replace(/\*/g, '.*').replace(/\?/g, '.');
474
+ includePattern = makeCaseInsensitive(withWildcards);
475
+ } else {
476
+ // Chercher ce qui contient le terme (case insensitive)
477
+ includePattern = `.*${makeCaseInsensitive(normalizedQuery)}.*`;
478
+ }
479
+ if (frRegex.value) includePattern += `|.*(${caseInsensitive(frRegex.value)}).*`;
480
+
481
+ if (props.field) {
482
+ obj = [props.field, props.otherResearchField]
483
+ .flat()
484
+ .map((field) => {
485
+ return {
486
+ [`${field}`]: {
487
+ terms: {
488
+ field: kw(field as string),
489
+ size: props.sizeTarget,
490
+ include: includePattern,
491
+ },
492
+ },
493
+ };
494
+ })
495
+ .reduce((acc, curr) => Object.assign(acc, curr), {});
496
+ }
497
+
498
+ if (props.nestedField) {
499
+ obj = [props.nestedField, props.otherResearchNestedField]
500
+ .flat()
501
+ .map((field) => {
502
+ return {
503
+ [`${field}`]: {
504
+ nested: {
505
+ // @ts-ignore
506
+ path: field.split(".").slice(0, -1).join("."),
507
+ },
508
+ aggs: {
509
+ [`${field}`]: {
510
+ terms: {
511
+ field: `${field}.keyword`,
512
+ // Sans size, ES retombe sur 10 buckets triés par doc count :
513
+ // un code exact peu représenté (ex: ORPHA:18) sort du top 10
514
+ // et disparaît. On récupère un superset, trié côté client.
515
+ size: 1000,
516
+ include: includePattern,
517
+ },
518
+ },
519
+ },
520
+ },
521
+ };
522
+ })
523
+ .reduce((acc, curr) => Object.assign(acc, curr), {});
524
+ }
525
+
526
+ return obj;
527
+ });
528
+
529
+ // La saisie francaise traduite en anglais, null hors filtres maladie ou sans traduction.
530
+ const frRegex = computed(() => (props.frDiseaseSearch ? frDiseaseRegex(search.value) : null));
531
+
532
+ const searchQuery = computed(() => {
533
+ let query;
534
+
535
+ // Détecte si la requête contient des codes courts (ORPHA:, NCT, ICD, etc.)
536
+ const hasShortCodes = /\b(ORPHA|NCT|ICD|ATC|EMA|FDA)[\w:-]{1,10}\b/i.test(search.value);
537
+
538
+ // Détecte si ce sont des termes très courts (≤3 caractères)
539
+ const hasVeryShortTerms = search.value.split(/\s+/).some((term: string) =>
540
+ term.replace(/[^\w]/g, '').length <= 3 && term.replace(/[^\w]/g, '').length > 0
541
+ );
542
+
543
+ // Normalise les codes médicaux en majuscules pour matcher les champs keyword
544
+ const normalizedQuery = search.value.replace(/\b(orpha|nct|icd|atc|ema|fda)([\w:-]*)/gi, (match: string, prefix: string, suffix: string) => {
545
+ return prefix.toUpperCase() + suffix.toUpperCase();
546
+ });
547
+
548
+ // Ajuste la fuzzyness et phrase_slop selon le contexte
549
+ const adaptiveFuzziness = hasShortCodes || hasVeryShortTerms ? "0" : props.fuziness;
550
+ const adaptivePhraseSlop = hasShortCodes ? 0 : 2;
551
+
552
+ // Détecte si la valeur contient des wildcards
553
+ const hasWildcard = search.value.includes('*') || search.value.includes('?');
554
+
555
+ if (props.field) {
556
+ query = {
557
+ bool: {
558
+ should: [
559
+ ...[props.field, props.otherResearchField].flat().map((field) => ({
560
+ bool: {
561
+ should: [
562
+ // Regexp pour recherche partielle (contient)
563
+ {
564
+ regexp: {
565
+ [`${field}.keyword`]: {
566
+ value: `.*${normalizedQuery}.*${frRegex.value ? `|.*(${frRegex.value}).*` : ''}`,
567
+ flags: "ALL",
568
+ case_insensitive: true,
569
+ },
570
+ },
571
+ },
572
+ // Wildcard pour recherche préfixe (commence par)
573
+ {
574
+ wildcard: {
575
+ [`${field}.keyword`]: {
576
+ value: hasWildcard ? normalizedQuery.toLowerCase() : `${normalizedQuery.toLowerCase()}*`,
577
+ case_insensitive: true,
578
+ },
579
+ },
580
+ },
581
+ ],
582
+ },
583
+ })),
584
+ ],
585
+ },
586
+ };
587
+ }
588
+ if (props.nestedField) {
589
+ query = {
590
+ bool: {
591
+ should: [
592
+ ...[props.nestedField, props.otherResearchNestedField]
593
+ .flat()
594
+ .map((field) => ({
595
+ nested: {
596
+ path: (field as string).split(".").slice(0, -1).join("."),
597
+ query: {
598
+ bool: {
599
+ should: [
600
+ // Regexp pour recherche partielle (contient)
601
+ {
602
+ regexp: {
603
+ [`${field}.keyword`]: {
604
+ value: `.*${normalizedQuery}.*${frRegex.value ? `|.*(${frRegex.value}).*` : ''}`,
605
+ flags: "ALL",
606
+ case_insensitive: true,
607
+ },
608
+ },
609
+ },
610
+ // Wildcard pour recherche préfixe (commence par)
611
+ {
612
+ wildcard: {
613
+ [`${field}.keyword`]: {
614
+ value: hasWildcard ? normalizedQuery.toLowerCase() : `${normalizedQuery.toLowerCase()}*`,
615
+ case_insensitive: true,
616
+ },
617
+ },
618
+ },
619
+ ],
620
+ },
621
+ },
622
+ },
623
+ })),
624
+ ],
625
+ },
626
+ };
627
+ }
628
+ return query;
629
+ });
630
+
631
+ const filtersWithoutSearch = computed(() => {
632
+ return {
633
+ name: props.filterName,
634
+ agg: aggregationsQuery.value,
635
+ path: props.field ? props.field : props.nestedField,
636
+ values: selectedItem.value,
637
+ isNested: props.nestedField,
638
+ };
639
+ });
640
+ const filtersWitSearch = computed(() => {
641
+ return {
642
+ agg: aggregationsSearchQuery.value,
643
+ path: props.field ? props.field : props.nestedField,
644
+ isNested: props.nestedField ? true : false,
645
+ searchQuery: searchQuery.value,
646
+ };
647
+ });
648
+
649
+ const launchQueryWithoutSearch = async () => {
650
+ await queryFilter({
651
+ index: props.indexQuery,
652
+ filters: filtersWithoutSearch.value,
653
+ take: props.sizeTarget,
654
+ customQuery: props.matchQuery,
655
+ cluster: props.cluster,
656
+ mustNot: props.mustNot,
657
+ }).then((data) => {
658
+ filtersData.value = data;
659
+ });
660
+ };
661
+
662
+ // Trie les résultats par pertinence vis-à-vis de la recherche : ES renvoie les
663
+ // buckets par doc count, donc un match exact (ex: ORPHA:18) n'est jamais premier.
664
+ // Ordre : code orpha exact > préfixe de code > préfixe de nom > contient > reste.
665
+ const sortByRelevance = (results: any): string[] => {
666
+ const raw = search.value.trim();
667
+ if (!raw || !Array.isArray(results)) return results;
668
+ const q = raw.toLowerCase();
669
+ const codeQueryMatch = raw.match(/^(?:orpha:?\s*)?(\d+)$/i);
670
+ const qCode = codeQueryMatch ? codeQueryMatch[1] : null;
671
+
672
+ const codeOf = (item: string) => (item.match(/orpha:\s*(\d+)/i) || [])[1];
673
+
674
+ const score = (item: string) => {
675
+ const lower = item.toLowerCase();
676
+ const code = codeOf(item);
677
+ if (qCode && code) {
678
+ if (code === qCode) return 0;
679
+ if (code.startsWith(qCode)) return 1;
680
+ }
681
+ if (lower.startsWith(q)) return 2;
682
+ if (lower.includes(q)) return 3;
683
+ return 4;
684
+ };
685
+
686
+ return [...results].sort((a, b) => {
687
+ const diff = score(a) - score(b);
688
+ if (diff !== 0) return diff;
689
+ const ca = codeOf(a);
690
+ const cb = codeOf(b);
691
+ // À score égal, code le plus court d'abord (ORPHA:18 avant ORPHA:180)
692
+ if (ca && cb && ca.length !== cb.length) return ca.length - cb.length;
693
+ return a.localeCompare(b);
694
+ });
695
+ };
696
+
697
+ const lauchQueryWithSearch = async () => {
698
+ // await querySearchFilter({
699
+ // index: props.indexQuery,
700
+ // filters: filtersWitSearch.value,
701
+ // take: props.sizeTarget,
702
+ // customQuery: props.matchQuery,
703
+ // cluster: props.cluster,
704
+ // }).then((data) => {
705
+ // filtersData.value = data;
706
+ // });
707
+
708
+ await querySearchFilter({
709
+ index: props.indexQuery,
710
+ filters: filtersWitSearch.value,
711
+ take: props.sizeTarget,
712
+ customQuery: [],
713
+ cluster: props.cluster,
714
+ mustNot: props.mustNot,
715
+ }).then((data) => {
716
+ filtersData.value = sortByRelevance(data).slice(0, props.sizeTarget);
717
+ });
718
+ };
719
+ </script>