@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,738 @@
1
+ <!--
2
+ Tout ce que l'index tarifaire sait d'une presentation.
3
+
4
+ L'onglet Prix ne montre par presentation que le tarif en vigueur : c'est ce
5
+ qu'on veut lire d'un coup d'oeil, mais l'ancienne page de detail donnait bien
6
+ davantage : l'historique des tarifs, celui des changements, les periodes
7
+ d'homologation, l'exploitant successif, la majoration outre-mer. Tout cela
8
+ revient ici, en onglets, sans quitter la fiche produit.
9
+ -->
10
+ <script setup lang="ts">
11
+ import { heartCodeLabel, heartPriceChannel } from "../../../../../oip-drugs/utils/heartStyleAndFormat";
12
+
13
+ const props = defineProps<{
14
+ /** La presentation (_source de l'index tarifaire), ou null quand ferme. */
15
+ product: any | null;
16
+ checkedAt?: string | null;
17
+ }>();
18
+
19
+ const emit = defineEmits<{ (event: "close"): void }>();
20
+
21
+ const { locale } = useI18n();
22
+
23
+ const isEn = computed(() => locale.value === "en");
24
+
25
+ function money(value: number | null | undefined) {
26
+ if (value === null || value === undefined) return "-";
27
+ return new Intl.NumberFormat(isEn.value ? "en-US" : "fr-FR", {
28
+ style: "currency",
29
+ currency: "EUR",
30
+ maximumFractionDigits: 2,
31
+ }).format(Number(value));
32
+ }
33
+
34
+ function date(raw: string | null | undefined) {
35
+ if (!raw) return "-";
36
+ const value = new Date(raw);
37
+ if (Number.isNaN(value.getTime())) return String(raw);
38
+ return value.toLocaleDateString(isEn.value ? "en-GB" : "fr-FR", {
39
+ day: "numeric",
40
+ month: "short",
41
+ year: "numeric",
42
+ });
43
+ }
44
+
45
+ function percent(value: number | null | undefined) {
46
+ if (value === null || value === undefined) return "-";
47
+ return `${Math.round(Number(value) * 100)} %`;
48
+ }
49
+
50
+ /** Entrees d'un historique, de la plus recente a la plus ancienne. */
51
+ function sorted(list: any[] | null | undefined, field: string) {
52
+ return Array.isArray(list)
53
+ ? [...list].sort((a, b) => new Date(b?.[field]).getTime() - new Date(a?.[field]).getTime())
54
+ : [];
55
+ }
56
+
57
+ const tariffs = computed(() => sorted(props.product?.reimbursement_history, "application_date"));
58
+ const homologations = computed(() => sorted(props.product?.homologation_history, "start_date"));
59
+ const manufacturers = computed(() => sorted(props.product?.manufacturer_history, "start_date"));
60
+ const overseas = computed(() => sorted(props.product?.pacific_area_fr_increase, "application_date"));
61
+
62
+ /**
63
+ * L'historique tarifaire, chaque ligne portant son evolution de PFHT.
64
+ *
65
+ * L'ecart se lit sur la colonne d'a cote : c'est la difference avec le PFHT de
66
+ * la ligne suivante qui en porte un, celle du tarif precedent. `changed_history`
67
+ * donnerait parfois un montant qui ne correspond a aucun des deux PFHT
68
+ * affiches : il ne decoupe ni aux memes dates ni aux memes circuits.
69
+ */
70
+ const tariffRows = computed(() =>
71
+ tariffs.value.map((row: any, index: number) => {
72
+ const price = typeof row?.factory_price === "number" ? row.factory_price : null;
73
+ const previous =
74
+ tariffs.value
75
+ .slice(index + 1)
76
+ .find((older: any) => typeof older?.factory_price === "number")?.factory_price ?? null;
77
+ const variation = price !== null && previous !== null ? price - previous : null;
78
+
79
+ return {
80
+ row,
81
+ variation,
82
+ variationPct: variation !== null && previous ? (variation / previous) * 100 : null,
83
+ };
84
+ }),
85
+ );
86
+
87
+ /**
88
+ * Les periodes de taux de remboursement, de la plus recente a la plus ancienne.
89
+ *
90
+ * Le taux est ecrit sur chaque tarif (« 0.65 », en chaine), ville seulement :
91
+ * une ligne par tarif repeterait 65 % dix fois. On ne garde que les tarifs ou
92
+ * le taux change par rapport au precedent, chacun ouvrant une periode.
93
+ */
94
+ const reimbursementRows = computed(() => {
95
+ const ascending = [...tariffs.value]
96
+ .reverse()
97
+ .filter((row: any) => row?.reimbursement_rate !== null && row?.reimbursement_rate !== undefined && row?.reimbursement_rate !== "");
98
+ const periods: any[] = [];
99
+ for (const row of ascending) {
100
+ const rate = Number(row.reimbursement_rate);
101
+ if (Number.isNaN(rate)) continue;
102
+ const previous = periods.length ? periods[periods.length - 1].rate : null;
103
+ if (previous === rate) continue;
104
+ // Evolution en points de taux par rapport a la periode precedente.
105
+ periods.push({ rate, row, variation: previous === null ? null : (rate - previous) * 100 });
106
+ }
107
+ return periods.reverse();
108
+ });
109
+
110
+ /**
111
+ * Circuit et source ne sont remplis que sur les tarifs hopital : en ville les
112
+ * deux colonnes ne porteraient que des tirets, on les enleve.
113
+ */
114
+ const hasChannel = computed(() => tariffs.value.some((row: any) => row?.distribution_type));
115
+ const hasSource = computed(() =>
116
+ tariffs.value.some((row: any) => row?.documentary_source || row?.source_date),
117
+ );
118
+
119
+ /**
120
+ * La marge de retrocession n'existe que sur les tarifs hopital : une colonne
121
+ * vide sur toute la hauteur du tableau ne dit rien, on l'enleve.
122
+ */
123
+ const hasMarge = computed(() =>
124
+ tariffs.value.some((row: any) => row?.marge !== null && row?.marge !== undefined),
125
+ );
126
+
127
+ /** « +1,4 % », pour accompagner l'ecart en euros. */
128
+ function signedPercent(value: number | null) {
129
+ if (value === null) return "";
130
+ const formatted = value.toLocaleString(isEn.value ? "en-US" : "fr-FR", {
131
+ maximumFractionDigits: 1,
132
+ });
133
+ return `${value > 0 ? "+" : ""}${formatted} %`;
134
+ }
135
+
136
+ /** Circuit de distribution, en clair : c'est lui qui dit ville ou hopital. */
137
+ const channel = computed(() =>
138
+ heartPriceChannel(props.product?.distribution_channel, locale.value),
139
+ );
140
+
141
+ /** CIP en ville, UCD a l'hopital : `local_drug_id` porte les deux. */
142
+ const codeLabel = computed(() => heartCodeLabel(props.product?.distribution_channel));
143
+
144
+ /**
145
+ * Regimes de prise en charge coches sur la presentation.
146
+ *
147
+ * `is_outpatient` et `is_inpatient` ne sont jamais vrais hors UCD(Hospital) :
148
+ * ce sont l'homologation en retrocession et la T2A, pas les circuits ville et
149
+ * hopital. Les nommer ainsi mettait un tag "Ville" sur des produits purement
150
+ * hospitaliers et n'en mettait aucun sur les produits de ville.
151
+ */
152
+ /** Inscription la plus ancienne, quand la commercialisation n'est pas renseignee. */
153
+ const listedOn = computed(() => {
154
+ const history = props.product?.homologation_history;
155
+ const dates = (Array.isArray(history) ? history : [])
156
+ .map((entry: any) => entry?.start_date)
157
+ .filter(Boolean)
158
+ .sort();
159
+ return dates[0] ?? null;
160
+ });
161
+
162
+ const channels = computed(() => {
163
+ const product = props.product ?? {};
164
+ return [
165
+ {
166
+ key: "is_outpatient",
167
+ label: isEn.value ? "Retrocession (registration)" : "Rétrocession (homologation)",
168
+ },
169
+ { key: "is_inpatient", label: "T2A" },
170
+ {
171
+ key: "is_retrocession",
172
+ label: isEn.value ? "Retrocession (channel)" : "Rétrocession (circuit)",
173
+ },
174
+ { key: "is_sus", label: isEn.value ? "SUS list" : "Liste en sus" },
175
+ { key: "is_taa_mco", label: "T2A MCO" },
176
+ { key: "is_taa_ssr", label: "T2A SSR" },
177
+ { key: "is_community", label: isEn.value ? "Community" : "Collectivités" },
178
+ { key: "is_social_insured", label: isEn.value ? "Social insurance" : "Assurés sociaux" },
179
+ { key: "is_generic_reference", label: isEn.value ? "Generic reference" : "Référent générique" },
180
+ ].filter((entry) => product[entry.key]);
181
+ });
182
+
183
+ /** Onglets : seuls ceux qui ont de la matiere sont proposes. */
184
+ const tabs = computed(() =>
185
+ [
186
+ { key: "identity", label: isEn.value ? "Identity" : "Identité", filled: true },
187
+ {
188
+ key: "tariffs",
189
+ label: isEn.value ? "Tariff history" : "Historique tarifaire",
190
+ filled: tariffs.value.length > 0,
191
+ },
192
+ {
193
+ key: "homologation",
194
+ label: isEn.value ? "Homologation" : "Homologation",
195
+ filled: homologations.value.length > 0 || manufacturers.value.length > 0,
196
+ },
197
+ {
198
+ key: "overseas",
199
+ label: isEn.value ? "Overseas uplift" : "Majoration outre-mer",
200
+ filled: overseas.value.length > 0,
201
+ },
202
+ ].filter((tab) => tab.filled),
203
+ );
204
+
205
+ const active = ref(0);
206
+
207
+ // Une autre presentation s'ouvre : elle n'a pas forcement les memes onglets.
208
+ watch(() => props.product, () => (active.value = 0));
209
+
210
+ const activeKey = computed(() => tabs.value[active.value]?.key ?? "identity");
211
+
212
+ function onKeydown(event: KeyboardEvent) {
213
+ if (event.key === "Escape") emit("close");
214
+ }
215
+
216
+ onMounted(() => document.addEventListener("keydown", onKeydown));
217
+ onBeforeUnmount(() => document.removeEventListener("keydown", onKeydown));
218
+ </script>
219
+
220
+ <template>
221
+ <Teleport to="body">
222
+ <Transition name="drawer">
223
+ <div v-if="product" class="pd oip-drugs" @click.self="emit('close')">
224
+ <aside class="pd_panel">
225
+ <header class="pd_head">
226
+ <p class="pd_type">
227
+ <RmIcon name="Pill" :size="16" />
228
+ {{ isEn ? "Presentation" : "Présentation" }}
229
+ </p>
230
+ <button
231
+ type="button"
232
+ class="pd_close"
233
+ :aria-label="isEn ? 'Close' : 'Fermer'"
234
+ @click="emit('close')"
235
+ >
236
+ <RmIcon name="X" :size="16" />
237
+ </button>
238
+ </header>
239
+
240
+ <div class="pd_body">
241
+ <h2 class="pd_title">{{ product.label }}</h2>
242
+ <p class="pd_sub">
243
+ <span v-if="product.local_drug_id">{{ codeLabel }} {{ product.local_drug_id }}</span>
244
+ <span v-if="product.manufacturer"> · {{ product.manufacturer }}</span>
245
+ </p>
246
+
247
+ <div v-if="tabs.length > 1" class="pd_tabs">
248
+ <button
249
+ v-for="(tab, index) in tabs"
250
+ :key="tab.key"
251
+ type="button"
252
+ class="pd_tab"
253
+ :class="{ is_active: index === active }"
254
+ @click="active = index"
255
+ >
256
+ {{ tab.label }}
257
+ </button>
258
+ </div>
259
+
260
+ <!-- Identite -->
261
+ <dl v-if="activeKey === 'identity'" class="pd_grid">
262
+ <div v-if="product.cis_label">
263
+ <dt>{{ isEn ? "Specialty" : "Spécialité" }}</dt>
264
+ <dd>{{ product.cis_label }}</dd>
265
+ </div>
266
+ <div v-if="product.presentation_label">
267
+ <dt>{{ isEn ? "Presentation" : "Présentation" }}</dt>
268
+ <dd>{{ product.presentation_label }}</dd>
269
+ </div>
270
+ <div v-if="product.brand">
271
+ <dt>{{ isEn ? "Brand" : "Marque" }}</dt>
272
+ <dd>{{ product.brand }}</dd>
273
+ </div>
274
+ <div v-if="product.molecule">
275
+ <dt>{{ isEn ? "Molecule" : "Molécule" }}</dt>
276
+ <dd>{{ product.molecule }}</dd>
277
+ </div>
278
+ <div v-if="product.atc5 || product.atc4">
279
+ <dt>ATC</dt>
280
+ <dd>{{ [product.atc5, product.atc4].filter(Boolean).join(" · ") }}</dd>
281
+ </div>
282
+ <div v-if="channel">
283
+ <dt>{{ isEn ? "Channel" : "Circuit" }}</dt>
284
+ <dd><RmTag :variant="channel.variant">{{ channel.label }}</RmTag></dd>
285
+ </div>
286
+ <div v-if="product.status_generic">
287
+ <dt>{{ isEn ? "Generic status" : "Statut générique" }}</dt>
288
+ <dd>{{ product.status_generic }}</dd>
289
+ </div>
290
+ <div v-if="product.commercialization_status">
291
+ <dt>{{ isEn ? "Marketing" : "Commercialisation" }}</dt>
292
+ <dd>
293
+ {{ product.commercialization_status }}
294
+ <template v-if="product.commercialization_date">
295
+ · {{ date(product.commercialization_date) }}
296
+ </template>
297
+ </dd>
298
+ </div>
299
+ <!-- Cote hopital, l'index ne porte pas de commercialisation : la
300
+ mise a disposition se lit sur l'inscription au circuit. -->
301
+ <div v-else-if="listedOn">
302
+ <dt>{{ isEn ? "Listed since" : "Inscrit depuis" }}</dt>
303
+ <dd>{{ date(listedOn) }}</dd>
304
+ </div>
305
+ <div v-if="product.last_price_update">
306
+ <dt>{{ isEn ? "Latest price change" : "Dernier changement de prix" }}</dt>
307
+ <dd>{{ date(product.last_price_update) }}</dd>
308
+ </div>
309
+ <div v-if="channels.length" class="pd_grid_wide">
310
+ <dt>{{ isEn ? "Coverage" : "Prise en charge" }}</dt>
311
+ <dd class="pd_chips">
312
+ <span v-for="regime in channels" :key="regime.key" class="pd_chip">
313
+ {{ regime.label }}
314
+ </span>
315
+ </dd>
316
+ </div>
317
+ </dl>
318
+
319
+ <!-- Historique tarifaire, evolution du PFHT comprise -->
320
+ <!-- Sept colonnes tiennent tout juste dans le tiroir : elles
321
+ defilent horizontalement plutot que d'en deborder. -->
322
+ <template v-else-if="activeKey === 'tariffs'">
323
+ <div class="pd_scroll">
324
+ <table class="pd_table">
325
+ <thead>
326
+ <tr>
327
+ <th>{{ isEn ? "Applies from" : "Applicable au" }}</th>
328
+ <th>PFHT</th>
329
+ <th>{{ isEn ? "Change" : "Évolution" }}</th>
330
+ <th>{{ isEn ? "Public price" : "Prix public" }}</th>
331
+ <th v-if="hasMarge">{{ isEn ? "Margin" : "Marge" }}</th>
332
+ <th v-if="hasChannel">{{ isEn ? "Channel" : "Circuit" }}</th>
333
+ <th v-if="hasSource">{{ isEn ? "Source" : "Source" }}</th>
334
+ </tr>
335
+ </thead>
336
+ <tbody>
337
+ <tr v-for="(item, index) in tariffRows" :key="index">
338
+ <td>{{ date(item.row.application_date) }}</td>
339
+ <td>{{ money(item.row.factory_price) }}</td>
340
+ <td
341
+ :class="
342
+ item.variation && item.variation > 0
343
+ ? 'is_up'
344
+ : item.variation && item.variation < 0
345
+ ? 'is_down'
346
+ : ''
347
+ "
348
+ >
349
+ <template v-if="item.variation === null">-</template>
350
+ <template v-else>
351
+ {{ item.variation > 0 ? "+" : "" }}{{ money(item.variation) }}
352
+ <span v-if="item.variationPct !== null" class="pd_muted">
353
+ ({{ signedPercent(item.variationPct) }})
354
+ </span>
355
+ </template>
356
+ </td>
357
+ <td>{{ money(item.row.public_price) }}</td>
358
+ <!-- La marge de retrocession est un forfait en euros par
359
+ ligne de dispensation (28 € jusqu'en 2008, 26 € en 2009,
360
+ 22 € depuis 2010), pas un taux : l'ecart PFHT vers prix
361
+ public vaut 2,1 %, c'est la TVA. -->
362
+ <td v-if="hasMarge">{{ money(item.row.marge) }}</td>
363
+ <td v-if="hasChannel">{{ item.row.distribution_type || "-" }}</td>
364
+ <td v-if="hasSource">
365
+ {{ item.row.documentary_source || "-" }}
366
+ <template v-if="item.row.source_date">
367
+ · {{ date(item.row.source_date) }}
368
+ </template>
369
+ </td>
370
+ </tr>
371
+ </tbody>
372
+ </table>
373
+ </div>
374
+
375
+ <!-- Remboursement : une ligne par periode de taux (ville seulement) -->
376
+ <h3 v-if="reimbursementRows.length" class="pd_section">
377
+ {{ isEn ? "Reimbursement" : "Remboursement" }}
378
+ </h3>
379
+ <table v-if="reimbursementRows.length" class="pd_table">
380
+ <thead>
381
+ <tr>
382
+ <th>{{ isEn ? "Applies from" : "Applicable au" }}</th>
383
+ <th>{{ isEn ? "Rate" : "Taux" }}</th>
384
+ <th>{{ isEn ? "Change" : "Évolution" }}</th>
385
+ <th v-if="hasChannel">{{ isEn ? "Channel" : "Circuit" }}</th>
386
+ <th v-if="hasSource">{{ isEn ? "Source" : "Source" }}</th>
387
+ </tr>
388
+ </thead>
389
+ <tbody>
390
+ <tr v-for="(item, index) in reimbursementRows" :key="index">
391
+ <td>{{ date(item.row.application_date) }}</td>
392
+ <td>{{ percent(item.rate) }}</td>
393
+ <td
394
+ :class="
395
+ item.variation && item.variation > 0
396
+ ? 'is_up'
397
+ : item.variation && item.variation < 0
398
+ ? 'is_down'
399
+ : ''
400
+ "
401
+ >
402
+ <template v-if="item.variation === null">-</template>
403
+ <template v-else>
404
+ {{ item.variation > 0 ? "+" : "" }}{{ Math.round(item.variation) }} pts
405
+ </template>
406
+ </td>
407
+ <td v-if="hasChannel">{{ item.row.distribution_type || "-" }}</td>
408
+ <td v-if="hasSource">
409
+ {{ item.row.documentary_source || "-" }}
410
+ <template v-if="item.row.source_date">
411
+ · {{ date(item.row.source_date) }}
412
+ </template>
413
+ </td>
414
+ </tr>
415
+ </tbody>
416
+ </table>
417
+ </template>
418
+
419
+ <!-- Homologation et exploitant -->
420
+ <template v-else-if="activeKey === 'homologation'">
421
+ <h3 v-if="homologations.length" class="pd_section">
422
+ {{ isEn ? "Homologation periods" : "Périodes d'homologation" }}
423
+ </h3>
424
+ <table v-if="homologations.length" class="pd_table">
425
+ <thead>
426
+ <tr>
427
+ <th>{{ isEn ? "From" : "Début" }}</th>
428
+ <th>{{ isEn ? "To" : "Fin" }}</th>
429
+ <th>{{ isEn ? "Channel" : "Circuit" }}</th>
430
+ <th>{{ isEn ? "Source" : "Source" }}</th>
431
+ </tr>
432
+ </thead>
433
+ <tbody>
434
+ <tr v-for="(row, index) in homologations" :key="index">
435
+ <td>{{ date(row.start_date) }}</td>
436
+ <td>
437
+ {{ row.end_date ? date(row.end_date) : isEn ? "Ongoing" : "En cours" }}
438
+ <template v-if="row.end_motif && row.end_motif !== '-'">
439
+ · {{ row.end_motif }}
440
+ </template>
441
+ </td>
442
+ <td>{{ row.distribution_type || "-" }}</td>
443
+ <td>{{ row.documentary_source || "-" }}</td>
444
+ </tr>
445
+ </tbody>
446
+ </table>
447
+
448
+ <h3 v-if="manufacturers.length" class="pd_section">
449
+ {{ isEn ? "Marketing authorisation holder" : "Exploitant" }}
450
+ </h3>
451
+ <table v-if="manufacturers.length" class="pd_table">
452
+ <thead>
453
+ <tr>
454
+ <th>{{ isEn ? "Holder" : "Exploitant" }}</th>
455
+ <th>{{ isEn ? "From" : "Début" }}</th>
456
+ <th>{{ isEn ? "To" : "Fin" }}</th>
457
+ </tr>
458
+ </thead>
459
+ <tbody>
460
+ <tr v-for="(row, index) in manufacturers" :key="index">
461
+ <td>{{ row.manufacturer || "-" }}</td>
462
+ <td>{{ date(row.start_date) }}</td>
463
+ <td>
464
+ {{ row.end_date ? date(row.end_date) : isEn ? "Ongoing" : "En cours" }}
465
+ </td>
466
+ </tr>
467
+ </tbody>
468
+ </table>
469
+ </template>
470
+
471
+ <!-- Majoration outre-mer -->
472
+ <table v-else-if="activeKey === 'overseas'" class="pd_table">
473
+ <thead>
474
+ <tr>
475
+ <th>{{ isEn ? "Applies from" : "Applicable au" }}</th>
476
+ <th>Guadeloupe</th>
477
+ <th>Guyane</th>
478
+ <th>Martinique</th>
479
+ <th>Réunion</th>
480
+ </tr>
481
+ </thead>
482
+ <tbody>
483
+ <tr v-for="(row, index) in overseas" :key="index">
484
+ <td>{{ date(row.application_date) }}</td>
485
+ <td>{{ row.guadeloupe ?? "-" }}</td>
486
+ <td>{{ row.guyane ?? "-" }}</td>
487
+ <td>{{ row.martinique ?? "-" }}</td>
488
+ <td>{{ row.reunion ?? "-" }}</td>
489
+ </tr>
490
+ </tbody>
491
+ </table>
492
+ </div>
493
+
494
+ <footer class="pd_foot">
495
+ <span>Source : <b>{{ isEn ? "Health Insurance" : "Assurance Maladie" }}</b></span>
496
+ <span v-if="checkedAt || product.last_source_update">
497
+ ·
498
+ {{ isEn ? "Last updated" : "Dernière mise à jour le" }}
499
+ {{ date(checkedAt || product.last_source_update) }}
500
+ </span>
501
+ </footer>
502
+ </aside>
503
+ </div>
504
+ </Transition>
505
+ </Teleport>
506
+ </template>
507
+
508
+ <style scoped>
509
+ /* Le voile epouse le cadre de la coque, comme les autres tiroirs du hub. */
510
+ .pd {
511
+ position: fixed;
512
+ inset: var(--rm-shell-inset, 8px);
513
+ border-radius: var(--rm-shell-radius, 12px);
514
+ overflow: hidden;
515
+ z-index: 300;
516
+ display: flex;
517
+ justify-content: flex-end;
518
+ background: rgba(30, 41, 59, 0.35);
519
+ -webkit-backdrop-filter: blur(2px);
520
+ backdrop-filter: blur(2px);
521
+ }
522
+
523
+ .pd_panel {
524
+ display: flex;
525
+ flex-direction: column;
526
+ width: min(860px, 100%);
527
+ height: 100%;
528
+ background: var(--white);
529
+ box-shadow: var(--overlay-shadow);
530
+ }
531
+
532
+ .pd_head {
533
+ display: flex;
534
+ align-items: center;
535
+ justify-content: space-between;
536
+ gap: 16px;
537
+ padding: 16px 20px;
538
+ border-bottom: 1px solid var(--light);
539
+ }
540
+
541
+ .pd_type {
542
+ display: flex;
543
+ align-items: center;
544
+ gap: 8px;
545
+ color: var(--primary);
546
+ font-size: 15px;
547
+ font-weight: 700;
548
+ }
549
+
550
+ .pd_close {
551
+ display: flex;
552
+ align-items: center;
553
+ justify-content: center;
554
+ width: 32px;
555
+ height: 32px;
556
+ border: 1px solid var(--light);
557
+ border-radius: 8px;
558
+ background: var(--white);
559
+ color: var(--dark);
560
+ cursor: pointer;
561
+ }
562
+
563
+ .pd_close:hover {
564
+ background: var(--lightest);
565
+ }
566
+
567
+ .pd_body {
568
+ flex: 1 1 auto;
569
+ overflow-y: auto;
570
+ display: flex;
571
+ flex-direction: column;
572
+ gap: 12px;
573
+ padding: 20px;
574
+ }
575
+
576
+ .pd_title,
577
+ .pd_sub,
578
+ .pd_tabs {
579
+ flex: 0 0 auto;
580
+ }
581
+
582
+ .pd_title {
583
+ font-size: 18px;
584
+ font-weight: 800;
585
+ line-height: 1.3;
586
+ }
587
+
588
+ .pd_sub {
589
+ color: var(--medium);
590
+ font-size: 13px;
591
+ }
592
+
593
+ .pd_tabs {
594
+ display: flex;
595
+ flex-wrap: wrap;
596
+ gap: 4px;
597
+ border-bottom: 1px solid var(--light);
598
+ }
599
+
600
+ .pd_tab {
601
+ padding: 8px 12px;
602
+ border: none;
603
+ border-bottom: 2px solid transparent;
604
+ background: none;
605
+ color: var(--medium);
606
+ font-family: inherit;
607
+ font-size: 14px;
608
+ font-weight: 600;
609
+ cursor: pointer;
610
+ }
611
+
612
+ .pd_tab.is_active {
613
+ border-bottom-color: var(--primary);
614
+ background: color-mix(in srgb, var(--primary) 6%, transparent);
615
+ color: var(--primary);
616
+ }
617
+
618
+ .pd_grid {
619
+ display: grid;
620
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
621
+ gap: 14px;
622
+ margin: 0;
623
+ }
624
+
625
+ .pd_grid_wide {
626
+ grid-column: 1 / -1;
627
+ }
628
+
629
+ .pd_grid dt {
630
+ color: var(--medium);
631
+ font-size: 11px;
632
+ font-weight: 700;
633
+ letter-spacing: 0.04em;
634
+ text-transform: uppercase;
635
+ }
636
+
637
+ .pd_grid dd {
638
+ margin: 2px 0 0;
639
+ color: var(--dark);
640
+ font-size: 14px;
641
+ }
642
+
643
+ .pd_chips {
644
+ display: flex;
645
+ flex-wrap: wrap;
646
+ gap: 6px;
647
+ }
648
+
649
+ .pd_chip {
650
+ padding: 2px 8px;
651
+ border-radius: 999px;
652
+ background: color-mix(in srgb, var(--primary) 8%, transparent);
653
+ color: var(--primary);
654
+ font-size: 12px;
655
+ font-weight: 600;
656
+ }
657
+
658
+ .pd_section {
659
+ margin-top: 8px;
660
+ font-size: 14px;
661
+ font-weight: 800;
662
+ }
663
+
664
+ .pd_table {
665
+ width: 100%;
666
+ border-collapse: collapse;
667
+ font-size: 13px;
668
+ }
669
+
670
+ .pd_table th {
671
+ padding: 8px 10px;
672
+ border-bottom: 1px solid var(--light);
673
+ color: var(--medium);
674
+ font-size: 11px;
675
+ font-weight: 700;
676
+ letter-spacing: 0.04em;
677
+ text-align: left;
678
+ text-transform: uppercase;
679
+ white-space: nowrap;
680
+ }
681
+
682
+ .pd_table td {
683
+ padding: 8px 10px;
684
+ border-bottom: 1px solid var(--lightest);
685
+ color: var(--dark);
686
+ vertical-align: top;
687
+ }
688
+
689
+ .pd_scroll {
690
+ overflow-x: auto;
691
+ }
692
+
693
+ .pd_muted {
694
+ color: var(--medium);
695
+ font-weight: 400;
696
+ }
697
+
698
+ .pd_table .is_up {
699
+ color: var(--system-alert-hover);
700
+ font-weight: 700;
701
+ }
702
+
703
+ .pd_table .is_down {
704
+ color: var(--system-success-hover);
705
+ font-weight: 700;
706
+ }
707
+
708
+ .pd_foot {
709
+ display: flex;
710
+ align-items: center;
711
+ gap: 6px;
712
+ padding: 14px 20px;
713
+ border-top: 1px solid var(--light);
714
+ background: var(--demi-fond);
715
+ color: var(--medium);
716
+ font-size: 13px;
717
+ }
718
+
719
+ .drawer-enter-active,
720
+ .drawer-leave-active {
721
+ transition: opacity 0.2s ease;
722
+ }
723
+
724
+ .drawer-enter-active .pd_panel,
725
+ .drawer-leave-active .pd_panel {
726
+ transition: transform 0.2s ease;
727
+ }
728
+
729
+ .drawer-enter-from,
730
+ .drawer-leave-to {
731
+ opacity: 0;
732
+ }
733
+
734
+ .drawer-enter-from .pd_panel,
735
+ .drawer-leave-to .pd_panel {
736
+ transform: translateX(24px);
737
+ }
738
+ </style>