@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.
- package/dist/module.json +1 -1
- package/dist/runtime/utils/projects.d.ts +8 -0
- package/dist/runtime/utils/projects.js +8 -5
- package/layer/app/app.config.ts +6 -0
- package/layer/app/components/OipPubmedSummary.vue +223 -0
- package/layer/app/components/OipResourceDrawer.vue +331 -0
- package/layer/app/components/OipResourceDrawerLink.vue +40 -0
- package/layer/app/components/OipResourceField.vue +41 -0
- package/layer/app/components/OipResourceGenericCard.vue +54 -0
- package/layer/app/components/OipResourceSource.vue +50 -0
- package/layer/app/components/OipResourceTags.vue +65 -0
- package/layer/app/components/OipResourceView.vue +107 -0
- package/layer/app/components/oip-drugs/fiches/AssociationFiche.vue +775 -0
- package/layer/app/components/oip-drugs/fiches/BrandFiche.vue +1111 -0
- package/layer/app/components/oip-drugs/fiches/IndicationFiche.vue +182 -0
- package/layer/app/components/oip-drugs/fiches/JoFiche.vue +101 -0
- package/layer/app/components/oip-drugs/fiches/PndsFiche.vue +279 -0
- package/layer/app/components/oip-drugs/heart/JoCard.vue +314 -0
- package/layer/app/components/oip-drugs/heart/TextHighlight.vue +326 -0
- package/layer/app/components/oip-drugs/heart/ansm/BrandCard.vue +187 -0
- package/layer/app/components/oip-drugs/heart/ansm/CisDetail.vue +363 -0
- package/layer/app/components/oip-drugs/heart/ansm/CisTable.vue +167 -0
- package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyCard.vue +373 -0
- package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyFiche.vue +372 -0
- package/layer/app/components/oip-drugs/heart/associations/TranscriptionCard.vue +55 -0
- package/layer/app/components/oip-drugs/heart/elastic/SourceContainerElastic.vue +103 -0
- package/layer/app/components/oip-drugs/heart/france/BrandGroupCard.vue +450 -0
- package/layer/app/components/oip-drugs/heart/mitm/Card.vue +199 -0
- package/layer/app/components/oip-drugs/heart/mitm/Source.vue +23 -0
- package/layer/app/components/oip-drugs/heart/mitm/details/Overview.vue +381 -0
- package/layer/app/components/oip-drugs/heart/put/Card.vue +212 -0
- package/layer/app/components/oip-drugs/heart/results/AccesCard.vue +696 -0
- package/layer/app/components/oip-drugs/heart/results/AlertCard.vue +342 -0
- package/layer/app/components/oip-drugs/heart/results/AsmrCursor.vue +229 -0
- package/layer/app/components/oip-drugs/heart/results/EvalCard.vue +370 -0
- package/layer/app/components/oip-drugs/heart/results/IndicCard.vue +743 -0
- package/layer/app/components/oip-drugs/heart/results/MatchExplain.vue +243 -0
- package/layer/app/components/oip-drugs/heart/results/PrefRows.vue +105 -0
- package/layer/app/components/oip-drugs/heart/results/details/HasHistory.vue +487 -0
- package/layer/app/components/oip-drugs/heart/results/details/InternationalCountryCard.vue +461 -0
- package/layer/app/components/oip-drugs/heart/results/details/InternationalOverview.vue +200 -0
- package/layer/app/components/oip-drugs/heart/results/details/InternationalPriceDrawer.vue +647 -0
- package/layer/app/components/oip-drugs/heart/results/details/Monograph.vue +128 -0
- package/layer/app/components/oip-drugs/heart/results/details/OpinionDrawer.vue +535 -0
- package/layer/app/components/oip-drugs/heart/results/details/PresentationDrawer.vue +738 -0
- package/layer/app/components/oip-drugs/heart/results/details/Price.vue +227 -0
- package/layer/app/components/oip-drugs/heart/results/details/PriceOverview.vue +1261 -0
- package/layer/app/components/oip-drugs/heart/results/details/RecordDrawer.vue +494 -0
- package/layer/app/components/oip-drugs/heart/results/details/RecordList.vue +426 -0
- package/layer/app/components/oip-drugs/heart/results/les/Card.vue +340 -0
- package/layer/app/components/oip-drugs/heart/results/les/Drawer.vue +67 -0
- package/layer/app/components/oip-drugs/heart/results/les/IndicationDetail.vue +149 -0
- package/layer/app/components/oip-drugs/heart/results/les/Source.vue +20 -0
- package/layer/app/components/oip-drugs/heart/results/les/SourceTag.vue +39 -0
- package/layer/app/components/oip-drugs/heart/searchPage/DynamicSource.vue +29 -0
- package/layer/app/components/oip-drugs/medicam/market/Chart.vue +121 -0
- package/layer/app/components/oip-drugs/outpatient/ProductEvolution.vue +236 -0
- package/layer/app/components/oip-drugs/resources/Ansm.vue +75 -0
- package/layer/app/components/oip-drugs/resources/Association.vue +30 -0
- package/layer/app/components/oip-drugs/resources/Brand.vue +65 -0
- package/layer/app/components/oip-drugs/resources/Contribution.vue +216 -0
- package/layer/app/components/oip-drugs/resources/Hospital.vue +151 -0
- package/layer/app/components/oip-drugs/resources/Indication.vue +54 -0
- package/layer/app/components/oip-drugs/resources/Jo.vue +57 -0
- package/layer/app/components/oip-drugs/resources/Medicam.vue +140 -0
- package/layer/app/components/oip-drugs/resources/Mitm.vue +84 -0
- package/layer/app/components/oip-drugs/resources/NationalAssembly.vue +93 -0
- package/layer/app/components/oip-drugs/resources/Outpatient.vue +97 -0
- package/layer/app/components/oip-drugs/resources/Pnds.vue +24 -0
- package/layer/app/components/oip-drugs/resources/Price.vue +106 -0
- package/layer/app/components/oip-drugs/resources/Put.vue +143 -0
- package/layer/app/components/oip-science/appel-a-projet/Cards.vue +162 -0
- package/layer/app/components/oip-science/clinical-trials/Cards.vue +382 -0
- package/layer/app/components/oip-science/clinical-trials/CardsGlobal.vue +273 -0
- package/layer/app/components/oip-science/clinical-trials/LinkedList.vue +366 -0
- package/layer/app/components/oip-science/clinical-trials/PairBadge.vue +25 -0
- package/layer/app/components/oip-science/clinical-trials/PairBody.vue +182 -0
- package/layer/app/components/oip-science/clinical-trials/SitesDrawer.vue +95 -0
- package/layer/app/components/oip-science/common/Form.vue +73 -0
- package/layer/app/components/oip-science/common/MatchAvgTag.vue +283 -0
- package/layer/app/components/oip-science/common/NoData.vue +16 -0
- package/layer/app/components/oip-science/common/ScoreBadge.vue +35 -0
- package/layer/app/components/oip-science/common/Source.vue +31 -0
- package/layer/app/components/oip-science/common/TableTags.vue +279 -0
- package/layer/app/components/oip-science/common/Timeline.vue +104 -0
- package/layer/app/components/oip-science/common/ToolTips.vue +89 -0
- package/layer/app/components/oip-science/cross/publicationPubmed.vue +484 -0
- package/layer/app/components/oip-science/crossData/CrossDataLocations.vue +71 -0
- package/layer/app/components/oip-science/ctgov/Investigators.vue +148 -0
- package/layer/app/components/oip-science/ctgov/Locations.vue +102 -0
- package/layer/app/components/oip-science/ctgov/Publications.vue +52 -0
- package/layer/app/components/oip-science/ctgov/Sponsors.vue +66 -0
- package/layer/app/components/oip-science/ctgov/StudyDescription.vue +1136 -0
- package/layer/app/components/oip-science/ctgov-centers/ClinicalTrials.vue +40 -0
- package/layer/app/components/oip-science/ctgov-centers/Publications.vue +50 -0
- package/layer/app/components/oip-science/ctgov-investigators/ClinicalTrials.vue +43 -0
- package/layer/app/components/oip-science/ctgov-investigators/Informations.vue +171 -0
- package/layer/app/components/oip-science/ctgov-investigators/Publications.vue +44 -0
- package/layer/app/components/oip-science/elastic/DateRangeFilter.vue +209 -0
- package/layer/app/components/oip-science/elastic/Filter.vue +719 -0
- package/layer/app/components/oip-science/elastic/MatchExplain.vue +152 -0
- package/layer/app/components/oip-science/establishments/Cards.vue +183 -0
- package/layer/app/components/oip-science/establishments/ClinicalTrials.vue +62 -0
- package/layer/app/components/oip-science/establishments/Hcps.vue +227 -0
- package/layer/app/components/oip-science/establishments/Informations.vue +263 -0
- package/layer/app/components/oip-science/establishments/LinkedList.vue +153 -0
- package/layer/app/components/oip-science/establishments/MatchedLabels.vue +194 -0
- package/layer/app/components/oip-science/establishments/PublicFinance.vue +62 -0
- package/layer/app/components/oip-science/euct/Investigators.vue +125 -0
- package/layer/app/components/oip-science/euct/Locations.vue +102 -0
- package/layer/app/components/oip-science/euct/Products.vue +101 -0
- package/layer/app/components/oip-science/euct/Publications.vue +89 -0
- package/layer/app/components/oip-science/euct/Sponsors.vue +86 -0
- package/layer/app/components/oip-science/euct/StudyDescription.vue +1075 -0
- package/layer/app/components/oip-science/euct-centers/ClinicalTrials.vue +40 -0
- package/layer/app/components/oip-science/euct-centers/Publications.vue +55 -0
- package/layer/app/components/oip-science/euct-investigators/ClinicalTrials.vue +41 -0
- package/layer/app/components/oip-science/euct-investigators/Informations.vue +171 -0
- package/layer/app/components/oip-science/euct-investigators/Publications.vue +51 -0
- package/layer/app/components/oip-science/hcps/AppelsAProjet.vue +62 -0
- package/layer/app/components/oip-science/hcps/Cards.vue +237 -0
- package/layer/app/components/oip-science/hcps/ClinicalTrials.vue +78 -0
- package/layer/app/components/oip-science/hcps/Informations.vue +296 -0
- package/layer/app/components/oip-science/hcps/LinkedList.vue +153 -0
- package/layer/app/components/oip-science/hcps/MatchedLabels.vue +207 -0
- package/layer/app/components/oip-science/hcps/Pubmed.vue +128 -0
- package/layer/app/components/oip-science/local/SliderTabs.vue +99 -0
- package/layer/app/components/oip-science/local/SliderTabsFull.vue +135 -0
- package/layer/app/components/oip-science/pubmed/Authors.vue +128 -0
- package/layer/app/components/oip-science/pubmed/Cards.vue +417 -0
- package/layer/app/components/oip-science/pubmed/ClinicalTrials.vue +71 -0
- package/layer/app/components/oip-science/resources/Phrc.vue +39 -0
- package/layer/app/composables/useOipChapterScroll.ts +43 -0
- package/layer/app/composables/useOipResourceHooks.ts +27 -0
- package/layer/app/composables/useOipResourceNav.ts +66 -0
- package/layer/app/composables/useOipResourceViews.ts +117 -0
- package/layer/app/composables/useOipResources.ts +83 -0
- package/layer/app/oip-drugs/PORTING.md +120 -0
- package/layer/app/oip-drugs/composables/useAssociationDetail.ts +220 -0
- package/layer/app/oip-drugs/composables/useAssociationExport.ts +63 -0
- package/layer/app/oip-drugs/composables/useAssociationLookup.ts +105 -0
- package/layer/app/oip-drugs/composables/useCardPrefOptions.ts +201 -0
- package/layer/app/oip-drugs/composables/useCurrency.ts +121 -0
- package/layer/app/oip-drugs/composables/useDirectusSources.ts +224 -0
- package/layer/app/oip-drugs/composables/useHeartBrandMatches.ts +112 -0
- package/layer/app/oip-drugs/composables/useHeartDetailSearches.ts +479 -0
- package/layer/app/oip-drugs/composables/useHeartIntlApprovals.ts +661 -0
- package/layer/app/oip-drugs/composables/useHeartLesIndication.ts +168 -0
- package/layer/app/oip-drugs/composables/useHeartMatchExplain.ts +231 -0
- package/layer/app/oip-drugs/composables/useHeartReports.ts +239 -0
- package/layer/app/oip-drugs/composables/useHeartSearchFields.ts +233 -0
- package/layer/app/oip-drugs/composables/useMergeByKey.ts +23 -0
- package/layer/app/oip-drugs/composables/useOipDrugsElastic.ts +621 -0
- package/layer/app/oip-drugs/composables/usePageEmpty.ts +58 -0
- package/layer/app/oip-drugs/composables/v-useSalesSource.ts +64 -0
- package/layer/app/oip-drugs/drugs.css +500 -0
- package/layer/app/oip-drugs/esIndices.ts +322 -0
- package/layer/app/oip-drugs/kinds.ts +264 -0
- package/layer/app/oip-drugs/locales/en.json +1571 -0
- package/layer/app/oip-drugs/locales/fr.json +1571 -0
- package/layer/app/oip-drugs/monographs/blincyto-has.vue +289 -0
- package/layer/app/oip-drugs/monographs/darzalex-has.vue +276 -0
- package/layer/app/oip-drugs/monographs/dupixent-has.vue +216 -0
- package/layer/app/oip-drugs/monographs/enhertu-has.vue +280 -0
- package/layer/app/oip-drugs/monographs/has-sein-2023.vue +1649 -0
- package/layer/app/oip-drugs/monographs/ibrance-has.vue +277 -0
- package/layer/app/oip-drugs/monographs/imfinzi-has.vue +293 -0
- package/layer/app/oip-drugs/monographs/keytruda-has.vue +334 -0
- package/layer/app/oip-drugs/monographs/kisqali-has.vue +201 -0
- package/layer/app/oip-drugs/monographs/opdivo-has.vue +231 -0
- package/layer/app/oip-drugs/monographs/ozempic-has.vue +186 -0
- package/layer/app/oip-drugs/monographs/tecentriq-has.vue +312 -0
- package/layer/app/oip-drugs/monographs/yescarta-has.vue +272 -0
- package/layer/app/oip-drugs/types/elastic.ts +113 -0
- package/layer/app/oip-drugs/types/es/agencies.ts +152 -0
- package/layer/app/oip-drugs/types/es/directory.ts +54 -0
- package/layer/app/oip-drugs/types/es/docByIndex.ts +151 -0
- package/layer/app/oip-drugs/types/es/openmedic.ts +67 -0
- package/layer/app/oip-drugs/types/es/outpatient.ts +71 -0
- package/layer/app/oip-drugs/types/es/portal.ts +130 -0
- package/layer/app/oip-drugs/types/es/retail.ts +107 -0
- package/layer/app/oip-drugs/types/es/statistics.ts +29 -0
- package/layer/app/oip-drugs/types/heart/CurrencyRate.ts +16 -0
- package/layer/app/oip-drugs/types/heart/ansm.ts +106 -0
- package/layer/app/oip-drugs/types/heart/tag.ts +29 -0
- package/layer/app/oip-drugs/utils/associationFields.ts +118 -0
- package/layer/app/oip-drugs/utils/contributions.ts +36 -0
- package/layer/app/oip-drugs/utils/countryMapping.ts +387 -0
- package/layer/app/oip-drugs/utils/esHighlight.ts +21 -0
- package/layer/app/oip-drugs/utils/exportSheets.ts +57 -0
- package/layer/app/oip-drugs/utils/heartIntlPrice.ts +228 -0
- package/layer/app/oip-drugs/utils/heartStyleAndFormat.ts +262 -0
- package/layer/app/oip-drugs/utils/heartStyleAnsm.ts +71 -0
- package/layer/app/oip-drugs/utils/latestDate.ts +31 -0
- package/layer/app/oip-drugs/utils/p-heartCardTags.ts +53 -0
- package/layer/app/oip-drugs/utils/priceExport.ts +269 -0
- package/layer/app/oip-drugs/utils/q-hasRatings.ts +43 -0
- package/layer/app/oip-drugs/utils/s-openCard.ts +20 -0
- package/layer/app/oip-drugs/utils/sourceUuids.ts +155 -0
- package/layer/app/oip-drugs/utils/styleAndFormat.ts +40 -0
- package/layer/app/oip-drugs/utils/v-salesSeries.ts +156 -0
- package/layer/app/oip-drugs/utils/v-scopeTags.ts +23 -0
- package/layer/app/oip-science/PORTING.md +93 -0
- package/layer/app/oip-science/assets/smartSelection.json +428 -0
- package/layer/app/oip-science/composables/useAuthorMatching.ts +342 -0
- package/layer/app/oip-science/composables/useCTCenter.ts +107 -0
- package/layer/app/oip-science/composables/useCTInvestigators.ts +107 -0
- package/layer/app/oip-science/composables/useEstablishmentCrossData.ts +139 -0
- package/layer/app/oip-science/composables/useEstablishmentMatching.ts +176 -0
- package/layer/app/oip-science/composables/useEuct.ts +418 -0
- package/layer/app/oip-science/composables/useEuctCenter.ts +107 -0
- package/layer/app/oip-science/composables/useEuctInvestigators.ts +107 -0
- package/layer/app/oip-science/composables/useGlobalClinicalTrials.ts +624 -0
- package/layer/app/oip-science/composables/useLinkedTrials.ts +83 -0
- package/layer/app/oip-science/composables/useOipElastic.ts +658 -0
- package/layer/app/oip-science/composables/useOipSearchExplain.ts +166 -0
- package/layer/app/oip-science/composables/usePubmed.ts +487 -0
- package/layer/app/oip-science/composables/useSearchFields.ts +153 -0
- package/layer/app/oip-science/indexConfig.json +794 -0
- package/layer/app/oip-science/locales/en.json +483 -0
- package/layer/app/oip-science/locales/fr.json +483 -0
- package/layer/app/oip-science/science.css +1182 -0
- package/layer/app/oip-science/utils/a-openCard.ts +23 -0
- package/layer/app/oip-science/utils/b-openCard.ts +35 -0
- package/layer/app/oip-science/utils/c-filterContext.ts +81 -0
- package/layer/app/oip-science/utils/f-types.ts +241 -0
- package/layer/app/oip-science/utils/frDiseaseTerms.ts +269 -0
- package/layer/app/oip-science/utils/matchMethod.ts +53 -0
- package/layer/app/oip-science/utils/sliderTabs.ts +13 -0
- package/layer/app/oip-science/utils/sources.ts +15 -0
- package/layer/app/oip-science/utils/styleAndFormat.ts +608 -0
- package/layer/app/oip-science/utils/toPresets.ts +7 -0
- package/layer/app/oip-science/utils/valueTable.ts +126 -0
- package/layer/app/plugins/oip-science-i18n.ts +17 -0
- package/layer/app/utils/oipFicheRoutes.ts +118 -0
- package/layer/app/utils/oipResources.ts +440 -0
- package/layer/nuxt.config.ts +22 -2
- package/layer/public/reports/blincyto-has.html +292 -0
- package/layer/public/reports/blincyto-has.pdf +0 -0
- package/layer/public/reports/darzalex-has.html +279 -0
- package/layer/public/reports/darzalex-has.pdf +0 -0
- package/layer/public/reports/dupixent-has.html +217 -0
- package/layer/public/reports/dupixent-has.pdf +0 -0
- package/layer/public/reports/enhertu-has.html +283 -0
- package/layer/public/reports/enhertu-has.pdf +0 -0
- package/layer/public/reports/has-sein-2023.html +1639 -0
- package/layer/public/reports/ibrance-has.html +280 -0
- package/layer/public/reports/ibrance-has.pdf +0 -0
- package/layer/public/reports/imfinzi-has.html +297 -0
- package/layer/public/reports/imfinzi-has.pdf +0 -0
- package/layer/public/reports/keytruda-has.html +337 -0
- package/layer/public/reports/keytruda-has.pdf +0 -0
- package/layer/public/reports/kisqali-has.html +202 -0
- package/layer/public/reports/kisqali-has.pdf +0 -0
- package/layer/public/reports/opdivo-has.html +232 -0
- package/layer/public/reports/opdivo-has.pdf +0 -0
- package/layer/public/reports/ozempic-has.html +188 -0
- package/layer/public/reports/ozempic-has.pdf +0 -0
- package/layer/public/reports/tecentriq-has.html +315 -0
- package/layer/public/reports/tecentriq-has.pdf +0 -0
- package/layer/public/reports/yescarta-has.html +275 -0
- package/layer/public/reports/yescarta-has.pdf +0 -0
- package/layer/server/api/oip/es.post.ts +99 -0
- package/layer/server/api/oip/pdf.get.ts +70 -0
- package/layer/server/api/oip/reports.get.ts +98 -0
- package/layer/server/api/oip/resource-types.get.ts +22 -0
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -49,6 +49,12 @@ export interface RmProjectGroup {
|
|
|
49
49
|
export interface RmProjectKind<S = any> {
|
|
50
50
|
/** Valeur de `resourceType` cote API. */
|
|
51
51
|
type: string;
|
|
52
|
+
/**
|
|
53
|
+
* La famille sous laquelle la ligne s'affiche, quand deux types designent
|
|
54
|
+
* les memes documents (un essai range depuis deux apps, sous deux cles) :
|
|
55
|
+
* une seule puce, un seul filtre. Absent : le type lui-meme.
|
|
56
|
+
*/
|
|
57
|
+
group?: string;
|
|
52
58
|
index: string;
|
|
53
59
|
label: Record<RmProjectLang, string>;
|
|
54
60
|
/** Rubrique de l'app d'ou vient la famille (Essais, Publications...). */
|
|
@@ -82,6 +88,8 @@ export interface RmProjectResource {
|
|
|
82
88
|
path?: string;
|
|
83
89
|
/** Libelle de la famille. */
|
|
84
90
|
family: string;
|
|
91
|
+
/** Cle de la famille affichee (cf. RmProjectKind.group) ; `type` reste celui de l'API. */
|
|
92
|
+
group?: string;
|
|
85
93
|
section?: string;
|
|
86
94
|
icon: string;
|
|
87
95
|
createdAt?: string;
|
|
@@ -74,8 +74,9 @@ export async function rmProjectHydrate(items, kinds, search) {
|
|
|
74
74
|
return out;
|
|
75
75
|
}
|
|
76
76
|
export function rmProjectRows(items, documents, kinds, lang = "fr") {
|
|
77
|
+
const groupOf = (type) => kinds.find((kind) => kind.type === type)?.group ?? type;
|
|
77
78
|
const rank = (type) => {
|
|
78
|
-
const index = kinds.findIndex((kind) => kind.type === type);
|
|
79
|
+
const index = kinds.findIndex((kind) => kind.type === groupOf(type));
|
|
79
80
|
return index < 0 ? kinds.length : index;
|
|
80
81
|
};
|
|
81
82
|
return [...items].sort((a, b) => rank(a.resourceType) - rank(b.resourceType)).map((item) => {
|
|
@@ -88,6 +89,7 @@ export function rmProjectRows(items, documents, kinds, lang = "fr") {
|
|
|
88
89
|
subtitle: kind && source ? kind.subtitle?.(source, lang) ?? "" : "",
|
|
89
90
|
path: kind && source ? kind.path?.(source, item.resourceId) ?? "" : "",
|
|
90
91
|
family: kind ? kind.label[lang] : item.resourceType,
|
|
92
|
+
group: kind?.group ?? item.resourceType,
|
|
91
93
|
section: kind?.section?.[lang],
|
|
92
94
|
icon: kind?.icon ?? "Question",
|
|
93
95
|
createdAt: item.createdAt,
|
|
@@ -100,9 +102,10 @@ export function rmProjectRows(items, documents, kinds, lang = "fr") {
|
|
|
100
102
|
export function rmProjectFamilies(rows) {
|
|
101
103
|
const out = [];
|
|
102
104
|
for (const row of rows) {
|
|
103
|
-
const
|
|
105
|
+
const key = row.group ?? row.type;
|
|
106
|
+
const found = out.find((family) => family.type === key);
|
|
104
107
|
if (found) found.count++;
|
|
105
|
-
else out.push({ type:
|
|
108
|
+
else out.push({ type: key, label: row.family, icon: row.icon, count: 1 });
|
|
106
109
|
}
|
|
107
110
|
return out;
|
|
108
111
|
}
|
|
@@ -110,14 +113,14 @@ export function rmProjectFilterRows(rows, filter, lang = "fr") {
|
|
|
110
113
|
const words = fold(filter.search).split(/\s+/).filter(Boolean);
|
|
111
114
|
const family = filter.family && filter.family !== "all" ? filter.family : "";
|
|
112
115
|
const order = rmProjectFamilies(rows).map((f) => f.type);
|
|
113
|
-
const list = rows.filter((row) => !family || row.type === family).filter((row) => !filter.onlyCommented || (row.comments ?? 0) > 0).filter((row) => !words.length || words.every((word) => fold([row.title, row.subtitle, row.family, row.section, row.id].join(" ")).includes(word)));
|
|
116
|
+
const list = rows.filter((row) => !family || (row.group ?? row.type) === family).filter((row) => !filter.onlyCommented || (row.comments ?? 0) > 0).filter((row) => !words.length || words.every((word) => fold([row.title, row.subtitle, row.family, row.section, row.id].join(" ")).includes(word)));
|
|
114
117
|
const byDate = (a, b) => String(a.createdAt ?? "").localeCompare(String(b.createdAt ?? ""));
|
|
115
118
|
const byTitle = (a, b) => (a.title || a.id).localeCompare(b.title || b.id, lang);
|
|
116
119
|
const sort = filter.sort ?? "date-desc";
|
|
117
120
|
return [...list].sort((a, b) => {
|
|
118
121
|
if (sort === "date-asc") return byDate(a, b);
|
|
119
122
|
if (sort === "title") return byTitle(a, b);
|
|
120
|
-
if (sort === "family") return order.indexOf(a.type) - order.indexOf(b.type) || byTitle(a, b);
|
|
123
|
+
if (sort === "family") return order.indexOf(a.group ?? a.type) - order.indexOf(b.group ?? b.type) || byTitle(a, b);
|
|
121
124
|
return byDate(b, a);
|
|
122
125
|
});
|
|
123
126
|
}
|
package/layer/app/app.config.ts
CHANGED
|
@@ -39,6 +39,12 @@ export default defineAppConfig({
|
|
|
39
39
|
* pointe la route qui nourrit le panneau : une app peut la detourner vers
|
|
40
40
|
* la sienne (ou une autre API) sans toucher au panneau.
|
|
41
41
|
*/
|
|
42
|
+
/**
|
|
43
|
+
* L'app, pour les vues de ressources (cf. utils/oipResources) : une fiche
|
|
44
|
+
* dont c'est l'app d'origine s'ouvre sur place, les autres dans leur app.
|
|
45
|
+
* « france_augmented_science_app », « fr_drugs_app »... Vide : aucune.
|
|
46
|
+
*/
|
|
47
|
+
resourceApp: "",
|
|
42
48
|
/**
|
|
43
49
|
* Registre des index Elasticsearch (collection Directus ElasticIndices).
|
|
44
50
|
* `preload` : charge les noms au demarrage, pour les apps qui designent
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Resume d'une publication PubMed : l'onglet « Summary » de la fiche de France
|
|
3
|
+
Augmented Science, son app d'origine.
|
|
4
|
+
|
|
5
|
+
Une seule vue pour toutes les apps : la page /pubmed/[id]/summary de France
|
|
6
|
+
Augmented Science la monte, et le tiroir d'un projet la monte dans
|
|
7
|
+
n'importe quelle app du portail. Elle ne lit ni route ni store : l'id et
|
|
8
|
+
les termes a surligner lui sont donnes.
|
|
9
|
+
-->
|
|
10
|
+
<template>
|
|
11
|
+
<div ref="root" class="oip-pm">
|
|
12
|
+
<RmLoadingState v-if="pending" :lines="3" :chart-height="240" />
|
|
13
|
+
<RmEmptyState
|
|
14
|
+
v-else-if="!doc"
|
|
15
|
+
icon="FileX"
|
|
16
|
+
:title="en ? 'Record unavailable' : 'Fiche indisponible'"
|
|
17
|
+
:text="en ? 'The record is no longer in the data.' : 'La fiche n\'est plus dans la donnée.'"
|
|
18
|
+
/>
|
|
19
|
+
<div v-else class="oip-pm__layout">
|
|
20
|
+
<div class="oip-pm__nav">
|
|
21
|
+
<RmChaptersNav
|
|
22
|
+
class="oip-pm__chapters"
|
|
23
|
+
:chapters="chapters"
|
|
24
|
+
:title="en ? 'Chapters' : 'Chapitres'"
|
|
25
|
+
:offset="chaptersOffset"
|
|
26
|
+
:style="{ top: `${stickyTop}px` }"
|
|
27
|
+
@select="scrollToChapter"
|
|
28
|
+
/>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div class="oip-pm__content">
|
|
32
|
+
<section id="introduction" class="oip-pm__card">
|
|
33
|
+
<div class="oip-pm__head">
|
|
34
|
+
<RmTag v-if="doc.delete" variant="rose">
|
|
35
|
+
{{ en ? "Article has been deleted" : "L'article a été supprimé" }}
|
|
36
|
+
</RmTag>
|
|
37
|
+
<h2 class="oip-pm__title">Introduction</h2>
|
|
38
|
+
<OipResourceSource :uuid="PUBMED_SOURCE_UUID" :lang="lang" />
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<OipResourceField :label="en ? 'Full Title' : 'Titre complet'" :text="orNA(doc.title)" :highlight="highlight" :lang="lang" />
|
|
42
|
+
<OipResourceField
|
|
43
|
+
:label="en ? 'First Published' : 'Première publication'"
|
|
44
|
+
:text="doc.article_date ? longDate(doc.article_date) : 'N/A'"
|
|
45
|
+
:lang="lang"
|
|
46
|
+
/>
|
|
47
|
+
<OipResourceTags v-for="group in tagGroups" :key="group.label" v-bind="group" :highlight="highlight" :lang="lang" />
|
|
48
|
+
</section>
|
|
49
|
+
|
|
50
|
+
<section v-for="section in sections" :id="anchor(section.label)" :key="section.label" class="oip-pm__card">
|
|
51
|
+
<h2 class="oip-pm__title">{{ titleCase(section.label) }}</h2>
|
|
52
|
+
<RmTextHighlight class="oip-pm__body" :queries="highlight" :text="section.text" :lang="lang" />
|
|
53
|
+
</section>
|
|
54
|
+
|
|
55
|
+
<section v-if="hasLinks" id="links" class="oip-pm__card">
|
|
56
|
+
<h2 class="oip-pm__title">{{ en ? "Links" : "Liens" }}</h2>
|
|
57
|
+
<div v-if="!doc.delete" class="oip-pm__link">
|
|
58
|
+
<p class="oip-pm__link-label">
|
|
59
|
+
<RmIcon name="Link" :size="12" color="var(--text-muted)" />
|
|
60
|
+
{{ en ? "Pubmed article" : "Article PubMed" }}
|
|
61
|
+
</p>
|
|
62
|
+
<a :href="`https://pubmed.ncbi.nlm.nih.gov/${id}/`" target="_blank" rel="noopener">
|
|
63
|
+
https://pubmed.ncbi.nlm.nih.gov/<b>{{ id }}</b>/
|
|
64
|
+
</a>
|
|
65
|
+
</div>
|
|
66
|
+
<div v-if="doc.doi" class="oip-pm__link">
|
|
67
|
+
<p class="oip-pm__link-label">
|
|
68
|
+
<RmIcon name="Link" :size="12" color="var(--text-muted)" />
|
|
69
|
+
{{ en ? "Complete article" : "Article complet" }}
|
|
70
|
+
</p>
|
|
71
|
+
<a :href="`https://www.doi.org/${doc.doi}`" target="_blank" rel="noopener">
|
|
72
|
+
https://www.doi.org/<b>{{ doc.doi }}</b>
|
|
73
|
+
</a>
|
|
74
|
+
</div>
|
|
75
|
+
</section>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</template>
|
|
80
|
+
|
|
81
|
+
<script setup lang="ts">
|
|
82
|
+
import { oipResourceSpec } from "../utils/oipResources";
|
|
83
|
+
import { oipEsGet } from "../composables/useOipResources";
|
|
84
|
+
|
|
85
|
+
const props = withDefaults(
|
|
86
|
+
defineProps<{
|
|
87
|
+
/** `_id` de la publication (son PMID). */
|
|
88
|
+
id: string;
|
|
89
|
+
/** Termes de la recherche en cours, surlignes dans la fiche. */
|
|
90
|
+
highlight?: string[];
|
|
91
|
+
lang?: "fr" | "en";
|
|
92
|
+
}>(),
|
|
93
|
+
{ highlight: () => [], lang: "fr" },
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
/** `source` : l'uuid Directus d'une source citee, pour ouvrir le panneau Sources. */
|
|
97
|
+
const emit = defineEmits<{ source: [uuid: string] }>();
|
|
98
|
+
|
|
99
|
+
/** La fiche Directus « PubMed » (cf. useSources de France Augmented Science). */
|
|
100
|
+
const PUBMED_SOURCE_UUID = "63805e97-48ac-4c04-afd6-8a15055dec97";
|
|
101
|
+
|
|
102
|
+
const en = computed(() => props.lang === "en");
|
|
103
|
+
const root = ref<HTMLElement | null>(null);
|
|
104
|
+
const { chaptersOffset, stickyTop, scrollToChapter } = useOipChapterScroll(root);
|
|
105
|
+
|
|
106
|
+
const spec = oipResourceSpec("PubMed")!;
|
|
107
|
+
const { data: doc, pending } = useAsyncData(
|
|
108
|
+
() => `oip-pubmed-${props.id}`,
|
|
109
|
+
async () => (await oipEsGet(spec.cluster, spec.kind.index, props.id))?._source ?? null,
|
|
110
|
+
{ server: false, watch: [() => props.id] },
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const orNA = (value: unknown) => (value == null || value === "" ? "N/A" : String(value));
|
|
114
|
+
const unique = (values: unknown[]) => [...new Set(values.filter((v): v is string => typeof v === "string" && v !== ""))];
|
|
115
|
+
const titleCase = (value: string) => (value || "").toLowerCase().replace(/(^|\s)\S/g, (c) => c.toUpperCase());
|
|
116
|
+
const anchor = (label: string) => (label || "").toLowerCase();
|
|
117
|
+
const longDate = (value: string) =>
|
|
118
|
+
new Date(value).toLocaleDateString(en.value ? "en-US" : "fr-FR", { year: "numeric", month: "long", day: "numeric" });
|
|
119
|
+
|
|
120
|
+
const tagGroups = computed(() => {
|
|
121
|
+
const d = doc.value ?? {};
|
|
122
|
+
const diseases: any[] = d.diseases ?? [];
|
|
123
|
+
return [
|
|
124
|
+
{ icon: "Article", label: en.value ? "Article type" : "Type d'article", values: unique(d.publication_types ?? []) },
|
|
125
|
+
{ icon: "BookOpen", label: en.value ? "Journal name" : "Nom de la revue", values: [orNA(d.journal)] },
|
|
126
|
+
{ icon: "Molecule", label: en.value ? "Molecules" : "Molécules", values: unique(d.chemical_list ?? []) },
|
|
127
|
+
{ icon: "TextAa", label: en.value ? "Keywords" : "Mots-clés", values: unique(d.keywords ?? []) },
|
|
128
|
+
{ icon: "Tag", label: en.value ? "Mesh Terms" : "Termes MeSH", values: unique(d.mesh_terms ?? []) },
|
|
129
|
+
{
|
|
130
|
+
icon: "Bandaids",
|
|
131
|
+
label: en.value ? "Rare diseases" : "Maladies rares",
|
|
132
|
+
values: unique((d.rare_diseases ?? []).map((r: any) => r.concat_disease_name_orpha_code)),
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
icon: "FaceMask",
|
|
136
|
+
label: "ICD 10",
|
|
137
|
+
values: unique(diseases.flatMap((x) => x.icd10?.map((i: any) => i.combined_label) ?? [])),
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
icon: "ListMagnifyingGlass",
|
|
141
|
+
label: en.value ? "Oncology Terms" : "Termes oncologiques",
|
|
142
|
+
values: unique(diseases.flatMap((x) => x.section?.map((s: any) => s.label) ?? [])),
|
|
143
|
+
},
|
|
144
|
+
];
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const sections = computed<{ label: string; text: string; order: number }[]>(() =>
|
|
148
|
+
[...(doc.value?.sections ?? [])].sort((a, b) => a?.order - b?.order),
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const hasLinks = computed(() => !doc.value?.delete || !!doc.value?.doi);
|
|
152
|
+
|
|
153
|
+
const chapters = computed(() => [
|
|
154
|
+
{ label: "Introduction", value: "introduction" },
|
|
155
|
+
...sections.value.map((s) => ({ label: titleCase(s.label), value: anchor(s.label) })),
|
|
156
|
+
...(hasLinks.value ? [{ label: en.value ? "Links" : "Liens", value: "links" }] : []),
|
|
157
|
+
]);
|
|
158
|
+
</script>
|
|
159
|
+
|
|
160
|
+
<style scoped>
|
|
161
|
+
.oip-pm__layout {
|
|
162
|
+
display: flex;
|
|
163
|
+
gap: var(--space-16);
|
|
164
|
+
align-items: flex-start;
|
|
165
|
+
}
|
|
166
|
+
.oip-pm__nav {
|
|
167
|
+
flex: 0 0 auto;
|
|
168
|
+
align-self: stretch;
|
|
169
|
+
}
|
|
170
|
+
.oip-pm__chapters {
|
|
171
|
+
position: sticky;
|
|
172
|
+
}
|
|
173
|
+
.oip-pm__content {
|
|
174
|
+
flex: 1 1 auto;
|
|
175
|
+
min-width: 0;
|
|
176
|
+
display: grid;
|
|
177
|
+
gap: var(--space-16);
|
|
178
|
+
}
|
|
179
|
+
.oip-pm__card {
|
|
180
|
+
display: grid;
|
|
181
|
+
gap: var(--space-24);
|
|
182
|
+
padding: var(--space-24);
|
|
183
|
+
border: 1px solid var(--border-default);
|
|
184
|
+
border-radius: 12px;
|
|
185
|
+
background: var(--bg-surface);
|
|
186
|
+
box-shadow: var(--shadow-sm);
|
|
187
|
+
}
|
|
188
|
+
.oip-pm__head {
|
|
189
|
+
display: grid;
|
|
190
|
+
gap: var(--space-4);
|
|
191
|
+
justify-items: start;
|
|
192
|
+
}
|
|
193
|
+
.oip-pm__title {
|
|
194
|
+
margin: 0;
|
|
195
|
+
color: var(--text-primary);
|
|
196
|
+
font-size: var(--font-18);
|
|
197
|
+
font-weight: 800;
|
|
198
|
+
line-height: normal;
|
|
199
|
+
}
|
|
200
|
+
.oip-pm__body {
|
|
201
|
+
color: var(--text-primary);
|
|
202
|
+
font-size: var(--font-14);
|
|
203
|
+
line-height: 160%;
|
|
204
|
+
}
|
|
205
|
+
.oip-pm__link {
|
|
206
|
+
display: grid;
|
|
207
|
+
gap: var(--space-4);
|
|
208
|
+
}
|
|
209
|
+
.oip-pm__link-label {
|
|
210
|
+
display: flex;
|
|
211
|
+
align-items: center;
|
|
212
|
+
gap: var(--space-4);
|
|
213
|
+
margin: 0;
|
|
214
|
+
color: var(--text-primary);
|
|
215
|
+
font-size: var(--font-12);
|
|
216
|
+
}
|
|
217
|
+
.oip-pm__link a {
|
|
218
|
+
color: var(--color-primary);
|
|
219
|
+
font-size: var(--font-14);
|
|
220
|
+
text-decoration: underline;
|
|
221
|
+
word-break: break-all;
|
|
222
|
+
}
|
|
223
|
+
</style>
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Le detail d'une ressource de projet, ouvert sans quitter la page, dans
|
|
3
|
+
n'importe quelle app du portail.
|
|
4
|
+
|
|
5
|
+
- Un type que le layer sait montrer (cf. OIP_RESOURCE_SPECS) s'affiche avec
|
|
6
|
+
la fiche de son app d'origine, ses onglets compris : une publication comme
|
|
7
|
+
dans France Augmented Science, une marque comme dans fr_drugs_app, dans
|
|
8
|
+
l'une comme dans l'autre.
|
|
9
|
+
- Les autres passent par la carte generique, decrite dans le registre des
|
|
10
|
+
types de l'API : un type ajoute la-bas s'affiche ici sans deploiement.
|
|
11
|
+
|
|
12
|
+
Sous le titre, un lien mene a la fiche dans son app d'origine. Dans l'app
|
|
13
|
+
d'origine elle-meme, il navigue sur place ; ailleurs, il ouvre l'autre app
|
|
14
|
+
dans un onglet.
|
|
15
|
+
|
|
16
|
+
Une fiche ouverte depuis la fiche du tiroir (l'auteur d'une publication, la
|
|
17
|
+
marque d'une contribution...) s'empile dans un tiroir de plus ; celui de
|
|
18
|
+
dessous reste ou il etait. Un lien vers une page qui n'est pas une fiche
|
|
19
|
+
quitte le tiroir.
|
|
20
|
+
-->
|
|
21
|
+
<template>
|
|
22
|
+
<RmDrawer
|
|
23
|
+
v-for="(entry, index) in entries"
|
|
24
|
+
:key="`${index}-${entry.type ?? entry.fiche?.view}-${entry.id ?? entry.fiche?.path}`"
|
|
25
|
+
:model-value="index === 0 ? open : true"
|
|
26
|
+
:size="index === 0 ? size : `min(${1180 - index * 40}px, calc(100vw - ${96 + index * 24}px))`"
|
|
27
|
+
:z-index="index === 0 ? undefined : 100 + index * 2"
|
|
28
|
+
:label="entry.title || entry.id || ''"
|
|
29
|
+
@update:model-value="(value: boolean) => !value && close(index)"
|
|
30
|
+
>
|
|
31
|
+
<!-- Carte generique : l'en-tete du tiroir porte le titre et le lien. -->
|
|
32
|
+
<template v-if="!isPage(entry)" #header>
|
|
33
|
+
<div class="oip-rd__head">
|
|
34
|
+
<p class="oip-rd__title">{{ entry.title || entry.id }}</p>
|
|
35
|
+
<OipResourceDrawerLink :origin="originOf(entry)" :lang="lang" @leave="close(0)" />
|
|
36
|
+
</div>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<div v-if="index > 0 || open" class="oip-rd__body" :class="{ 'oip-rd__body--page': isPage(entry) }">
|
|
40
|
+
<!-- Une fiche porte son bandeau (titre, lien dessous, onglets) : l'en-tete
|
|
41
|
+
du tiroir s'efface, sa croix se pose au bout du bandeau. -->
|
|
42
|
+
<RmButton
|
|
43
|
+
v-if="isPage(entry)"
|
|
44
|
+
class="oip-rd__close"
|
|
45
|
+
variant="tertiary"
|
|
46
|
+
size="md"
|
|
47
|
+
icon-only
|
|
48
|
+
ghost
|
|
49
|
+
prefix-icon="X"
|
|
50
|
+
:aria-label="en ? 'Close' : 'Fermer'"
|
|
51
|
+
@click="close(index)"
|
|
52
|
+
/>
|
|
53
|
+
<!-- Une fiche visee par un lien, montee par sa vue. -->
|
|
54
|
+
<div v-if="entry.fiche && !entry.fiche.view.startsWith('resource:')" class="oip-rv oip-rv--whole">
|
|
55
|
+
<component :is="oipResourceViewComponent(entry.fiche.view)" v-bind="entry.fiche.props" :lang="lang">
|
|
56
|
+
<template #title-link>
|
|
57
|
+
<OipResourceDrawerLink :origin="originOf(entry)" :lang="lang" @leave="close(0)" />
|
|
58
|
+
</template>
|
|
59
|
+
</component>
|
|
60
|
+
</div>
|
|
61
|
+
<OipResourceView
|
|
62
|
+
v-else-if="entry.type && oipResourceViewSpec(entry.type)"
|
|
63
|
+
:type="entry.type"
|
|
64
|
+
:id="entry.id ?? ''"
|
|
65
|
+
:title="entry.title"
|
|
66
|
+
:tab="entry.tab"
|
|
67
|
+
:highlight="index === 0 ? highlight : []"
|
|
68
|
+
:lang="lang"
|
|
69
|
+
@source="emit('source', $event)"
|
|
70
|
+
>
|
|
71
|
+
<template v-if="isPage(entry)" #title-link>
|
|
72
|
+
<OipResourceDrawerLink :origin="originOf(entry)" :lang="lang" @leave="close(0)" />
|
|
73
|
+
</template>
|
|
74
|
+
</OipResourceView>
|
|
75
|
+
<OipResourceGenericCard
|
|
76
|
+
v-else
|
|
77
|
+
:type="entry.type ?? ''"
|
|
78
|
+
:id="entry.id ?? ''"
|
|
79
|
+
:title="entry.title"
|
|
80
|
+
:source="source"
|
|
81
|
+
:lang="lang"
|
|
82
|
+
/>
|
|
83
|
+
</div>
|
|
84
|
+
</RmDrawer>
|
|
85
|
+
</template>
|
|
86
|
+
|
|
87
|
+
<script setup lang="ts">
|
|
88
|
+
import {
|
|
89
|
+
OIP_RESOURCE_APPS,
|
|
90
|
+
oipFillTemplate,
|
|
91
|
+
oipResourceAppUrl,
|
|
92
|
+
oipResourceViewSpec,
|
|
93
|
+
type OipResourceApp,
|
|
94
|
+
} from "../utils/oipResources";
|
|
95
|
+
import { oipFicheOf, type OipFicheTarget } from "../utils/oipFicheRoutes";
|
|
96
|
+
import { oipHydrate, useOipResourceTypes } from "../composables/useOipResources";
|
|
97
|
+
import { OIP_RESOURCE_NAV, useOipResourceApp } from "../composables/useOipResourceNav";
|
|
98
|
+
import { oipResourceViewComponent } from "../composables/useOipResourceViews";
|
|
99
|
+
|
|
100
|
+
const props = withDefaults(
|
|
101
|
+
defineProps<{
|
|
102
|
+
modelValue: boolean;
|
|
103
|
+
/** `resourceType` de l'API. */
|
|
104
|
+
type: string;
|
|
105
|
+
/** `resourceId` de l'API : l'`_id` du document. */
|
|
106
|
+
id: string;
|
|
107
|
+
title?: string;
|
|
108
|
+
/** L'app qui ouvre le tiroir. Absente : `oip.resourceApp` de l'app.config. */
|
|
109
|
+
app?: OipResourceApp | "";
|
|
110
|
+
/** Le document deja relu par l'app, pour la carte generique. */
|
|
111
|
+
source?: Record<string, unknown> | null;
|
|
112
|
+
highlight?: string[];
|
|
113
|
+
lang?: "fr" | "en";
|
|
114
|
+
size?: string;
|
|
115
|
+
}>(),
|
|
116
|
+
{
|
|
117
|
+
title: "",
|
|
118
|
+
app: "",
|
|
119
|
+
source: null,
|
|
120
|
+
highlight: () => [],
|
|
121
|
+
lang: "fr",
|
|
122
|
+
size: "min(1280px, calc(100vw - 48px))",
|
|
123
|
+
},
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const emit = defineEmits<{
|
|
127
|
+
"update:modelValue": [value: boolean];
|
|
128
|
+
/** Une source citee dans la fiche : l'app ouvre son panneau Sources. */
|
|
129
|
+
source: [uuid: string];
|
|
130
|
+
}>();
|
|
131
|
+
|
|
132
|
+
const open = computed({
|
|
133
|
+
get: () => props.modelValue,
|
|
134
|
+
set: (value: boolean) => emit("update:modelValue", value),
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const en = computed(() => props.lang === "en");
|
|
138
|
+
const configuredApp = useOipResourceApp();
|
|
139
|
+
const app = computed(() => props.app || configuredApp);
|
|
140
|
+
const localePath = useLocalePath();
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Une fiche ouverte : une ressource (`type`, `id`, l'onglet et le document
|
|
144
|
+
* relu pour son lien) ou une fiche visee par un lien (`fiche`).
|
|
145
|
+
*/
|
|
146
|
+
interface Entry {
|
|
147
|
+
type?: string;
|
|
148
|
+
id?: string;
|
|
149
|
+
tab?: string;
|
|
150
|
+
title: string;
|
|
151
|
+
doc?: Record<string, any> | null;
|
|
152
|
+
fiche?: OipFicheTarget & { app: OipResourceApp; path: string };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Les fiches ouvertes au-dessus de celle du projet, de bas en haut. */
|
|
156
|
+
const stacked = ref<Entry[]>([]);
|
|
157
|
+
/** Le document de la ressource du projet : certaines fiches s'adressent par un de ses champs. */
|
|
158
|
+
const rootDoc = ref<Record<string, any> | null>(null);
|
|
159
|
+
const entries = computed<Entry[]>(() => [
|
|
160
|
+
{ type: props.type, id: props.id, title: props.title, doc: rootDoc.value },
|
|
161
|
+
...stacked.value,
|
|
162
|
+
]);
|
|
163
|
+
|
|
164
|
+
watch(
|
|
165
|
+
() => [props.modelValue, props.type, props.id] as const,
|
|
166
|
+
async ([isOpen, type, id]) => {
|
|
167
|
+
rootDoc.value = null;
|
|
168
|
+
const spec = oipResourceViewSpec(type);
|
|
169
|
+
if (!isOpen || !spec) return;
|
|
170
|
+
rootDoc.value = (await oipHydrate(spec.type, [id]).catch(() => ({})))[id] ?? null;
|
|
171
|
+
},
|
|
172
|
+
{ immediate: true },
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
/** Fermer un tiroir ferme aussi ceux qui sont au-dessus de lui. */
|
|
176
|
+
function close(index: number) {
|
|
177
|
+
if (index === 0) open.value = false;
|
|
178
|
+
stacked.value = stacked.value.slice(0, Math.max(0, index - 1));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
watch(open, (isOpen) => {
|
|
182
|
+
if (!isOpen) stacked.value = [];
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
/** Empile une ressource, titree par la description de son type des que son document arrive. */
|
|
186
|
+
async function pushResource(type: string, id: string, tab?: string) {
|
|
187
|
+
const spec = oipResourceViewSpec(type);
|
|
188
|
+
if (!spec) return;
|
|
189
|
+
const entry = reactive<Entry>({ type, id, tab, title: "", doc: null });
|
|
190
|
+
stacked.value = [...stacked.value, entry];
|
|
191
|
+
const doc = (await oipHydrate(spec.type, [id]).catch(() => ({})))[id];
|
|
192
|
+
if (doc) {
|
|
193
|
+
entry.doc = doc;
|
|
194
|
+
entry.title = spec.kind.title(doc, props.lang);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const appHref = (target: OipResourceApp, path: string) =>
|
|
199
|
+
target === app.value ? localePath(path) : oipResourceAppUrl(target, path, props.lang);
|
|
200
|
+
|
|
201
|
+
// Les liens des fiches du tiroir empilent un tiroir de plus ; un lien vers une
|
|
202
|
+
// page qui n'est pas une fiche quitte le tiroir (sur place dans l'app, dans un
|
|
203
|
+
// onglet ailleurs).
|
|
204
|
+
provide(OIP_RESOURCE_NAV, {
|
|
205
|
+
href: (type, id, tab) => urlOf(type, id, tab)?.url ?? "",
|
|
206
|
+
open: (type, id, tab) => pushResource(type, id, tab),
|
|
207
|
+
appHref,
|
|
208
|
+
openApp(target, path) {
|
|
209
|
+
const fiche = oipFicheOf(target, path);
|
|
210
|
+
if (fiche?.view.startsWith("resource:")) {
|
|
211
|
+
pushResource(fiche.props.type!, fiche.props.id!, fiche.props.tab || undefined);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (fiche) {
|
|
215
|
+
stacked.value = [...stacked.value, { title: fiche.title, fiche: { ...fiche, app: target, path } }];
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const url = appHref(target, path);
|
|
219
|
+
if (target === app.value) {
|
|
220
|
+
close(0);
|
|
221
|
+
navigateTo(url);
|
|
222
|
+
} else window.open(url, "_blank", "noopener");
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
/** L'URL de la fiche : locale dans son app d'origine, absolue ailleurs. */
|
|
227
|
+
function urlOf(type: string, id: string, tab?: string, doc?: Record<string, any> | null) {
|
|
228
|
+
const spec = oipResourceViewSpec(type);
|
|
229
|
+
if (!spec) return null;
|
|
230
|
+
const path = spec.detailPath(id, tab, doc);
|
|
231
|
+
if (!path) return null;
|
|
232
|
+
if (spec.app === app.value) return { local: true, url: localePath(path), name: "" };
|
|
233
|
+
return { local: false, url: oipResourceAppUrl(spec.app, path, props.lang), name: OIP_RESOURCE_APPS[spec.app].name };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Les vues sans bandeau a elles (une carte de resultat) : le tiroir garde son
|
|
238
|
+
* en-tete, titre et lien.
|
|
239
|
+
*/
|
|
240
|
+
const CARD_VIEWS = new Set(["drugs-national-assembly", "science-phrc"]);
|
|
241
|
+
|
|
242
|
+
/** Une fiche du layer : elle porte son bandeau, le tiroir efface le sien. */
|
|
243
|
+
function isPage(entry: Entry) {
|
|
244
|
+
if (entry.fiche) return true;
|
|
245
|
+
const spec = entry.type ? oipResourceViewSpec(entry.type) : undefined;
|
|
246
|
+
return !!spec && !CARD_VIEWS.has(spec.view ?? "");
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function originOf(entry: Entry) {
|
|
250
|
+
if (entry.fiche) {
|
|
251
|
+
const local = entry.fiche.app === app.value;
|
|
252
|
+
return { local, url: appHref(entry.fiche.app, entry.fiche.path), name: OIP_RESOURCE_APPS[entry.fiche.app].name };
|
|
253
|
+
}
|
|
254
|
+
if (entry.type && oipResourceViewSpec(entry.type)) return urlOf(entry.type, entry.id ?? "", entry.tab, entry.doc);
|
|
255
|
+
return registryUrl.value ? { local: false, url: registryUrl.value, name: registryName.value } : null;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Un type sans vue : son lien vient du registre de l'API.
|
|
259
|
+
const registryUrl = ref("");
|
|
260
|
+
const registryName = ref("");
|
|
261
|
+
watch(
|
|
262
|
+
() => [props.modelValue, props.type, props.id] as const,
|
|
263
|
+
async ([isOpen, type, id]) => {
|
|
264
|
+
registryUrl.value = "";
|
|
265
|
+
registryName.value = "";
|
|
266
|
+
if (!isOpen || oipResourceViewSpec(type)) return;
|
|
267
|
+
const entry = (await useOipResourceTypes()).find((e) => e.key === type);
|
|
268
|
+
registryUrl.value = entry?.display?.detailUrl ? oipFillTemplate(entry.display.detailUrl, props.source ?? {}, id) : "";
|
|
269
|
+
const owner = entry?.ownerApp ?? "";
|
|
270
|
+
registryName.value = OIP_RESOURCE_APPS[owner as OipResourceApp]?.name ?? owner;
|
|
271
|
+
},
|
|
272
|
+
{ immediate: true },
|
|
273
|
+
);
|
|
274
|
+
</script>
|
|
275
|
+
|
|
276
|
+
<style scoped>
|
|
277
|
+
.oip-rd__head {
|
|
278
|
+
display: grid;
|
|
279
|
+
gap: var(--space-4);
|
|
280
|
+
min-width: 0;
|
|
281
|
+
}
|
|
282
|
+
.oip-rd__title {
|
|
283
|
+
margin: 0;
|
|
284
|
+
color: var(--text-primary);
|
|
285
|
+
font-size: var(--font-18);
|
|
286
|
+
font-weight: 700;
|
|
287
|
+
line-height: 1.3;
|
|
288
|
+
}
|
|
289
|
+
.oip-rd__body {
|
|
290
|
+
padding: var(--space-16) 0;
|
|
291
|
+
}
|
|
292
|
+
.oip-rd__body.oip-rd__body--page {
|
|
293
|
+
padding: 0 0 var(--space-32);
|
|
294
|
+
}
|
|
295
|
+
.oip-rd__close {
|
|
296
|
+
position: absolute;
|
|
297
|
+
top: var(--space-16);
|
|
298
|
+
right: var(--space-24);
|
|
299
|
+
z-index: 96;
|
|
300
|
+
}
|
|
301
|
+
/* La place de la croix, au bout de la rangee titre + actions du bandeau. */
|
|
302
|
+
.oip-rd__body--page :deep(.rm-page-header__row) {
|
|
303
|
+
padding-right: 72px;
|
|
304
|
+
}
|
|
305
|
+
/* La fleche de retour des pages remonterait l'historique de l'ecran dessous. */
|
|
306
|
+
.oip-rd__body--page :deep(.rm-page-header__back) {
|
|
307
|
+
display: none;
|
|
308
|
+
}
|
|
309
|
+
/* Le contenu d'une fiche d'un seul tenant recoit les gouttieres de la colonne
|
|
310
|
+
de contenu ; son bandeau reste bord a bord. On vise l'element qui porte le
|
|
311
|
+
bandeau, a quelque profondeur qu'il soit : une vue de ressource enveloppe
|
|
312
|
+
souvent sa fiche, et la marge posee sur l'enveloppe decalerait le bandeau. */
|
|
313
|
+
.oip-rd__body--page :deep(.oip-rv--whole :has(> .rm-page-header) > :not(.rm-page-header)) {
|
|
314
|
+
box-sizing: border-box;
|
|
315
|
+
padding-inline: var(--space-24);
|
|
316
|
+
}
|
|
317
|
+
.oip-rd__body--page :deep(.oip-rv--whole :has(> .rm-page-header) > .rm-page-header + *) {
|
|
318
|
+
margin-top: var(--space-24);
|
|
319
|
+
}
|
|
320
|
+
</style>
|
|
321
|
+
|
|
322
|
+
<style>
|
|
323
|
+
/* Hors portee : l'en-tete et le corps appartiennent au tiroir (teleporte).
|
|
324
|
+
Sans marge interne, le bandeau colle de la fiche tient le haut du tiroir. */
|
|
325
|
+
.rm-drawer:has(> .rm-drawer__body .oip-rd__body--page) > .rm-drawer__header {
|
|
326
|
+
display: none;
|
|
327
|
+
}
|
|
328
|
+
.rm-drawer:has(> .rm-drawer__body .oip-rd__body--page) > .rm-drawer__body {
|
|
329
|
+
padding: 0;
|
|
330
|
+
}
|
|
331
|
+
</style>
|