Skip to content

Commit 0b43ce9

Browse files
authored
feat(preview): add datafair embed preview (#773)
Replace [udata-data-fair](https://github.com/koumoul-dev/udata-data-fair) plugin. - Use iframe for map, table or field structure preview. - Use swagger client for api doc preview. For now, it's restricted only to certified organization as a security measure.
1 parent 3aa8977 commit 0b43ce9

File tree

3 files changed

+97
-1
lines changed

3 files changed

+97
-1
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<template>
2+
<div class="fr-text--xs">
3+
<div v-if="dataFairEmbedUrl">
4+
<iframe
5+
:src="dataFairEmbedUrl"
6+
width="100%"
7+
height="500px"
8+
style="background-color: transparent;
9+
border: none;"
10+
/>
11+
</div>
12+
<SimpleBanner
13+
v-else
14+
type="warning"
15+
class="flex items-center space-x-2"
16+
>
17+
<RiErrorWarningLine class="shink-0 size-6" />
18+
<span>{{ t("Erreur lors de l'affichage de l'aperçu.") }}</span>
19+
</SimpleBanner>
20+
</div>
21+
</template>
22+
23+
<script setup lang="ts">
24+
import { computed } from 'vue'
25+
import { RiErrorWarningLine } from '@remixicon/vue'
26+
import SimpleBanner from '../SimpleBanner.vue'
27+
import type { Resource } from '../../types/resources.ts'
28+
import type { Dataset, DatasetV2 } from '../../types/datasets.ts'
29+
import { useTranslation } from '../../composables/useTranslation.ts'
30+
31+
const props = defineProps<{
32+
resource: Resource
33+
dataset: Dataset | DatasetV2
34+
}>()
35+
36+
const { t } = useTranslation()
37+
38+
const datafairOrigin = computed(() => {
39+
return props.resource.extras['datafairOrigin'] || props.dataset.extras['datafairOrigin']
40+
})
41+
const datafairDatasetId = computed(() => {
42+
return props.resource.extras['datafairDatasetId'] || props.dataset.extras['datafairDatasetId']
43+
})
44+
const embed = computed(() => props.resource.extras['datafairEmbed'])
45+
46+
const dataFairEmbedUrl = computed(() => {
47+
// if the return value is null, a banner error is shown
48+
if (!datafairOrigin.value || !datafairDatasetId.value)
49+
return null
50+
return `${datafairOrigin.value}/embed/dataset/${datafairDatasetId.value}/${embed.value}`
51+
})
52+
</script>

datagouv-components/src/components/ResourceAccordion/ResourceAccordion.vue

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,17 @@
211211
v-else-if="resource.format && resource.format.toLowerCase() === 'xml'"
212212
:resource="resource"
213213
/>
214+
<!-- Show Datafair embedded preview (koumoul) -->
215+
<DatafairPreview
216+
v-else-if="hasDatafairPreview"
217+
:resource="resource"
218+
:dataset="dataset"
219+
/>
220+
<!-- Show Datafair embedded preview (koumoul) -->
221+
<SwaggerClient
222+
v-else-if="hasOpenAPIPreview"
223+
:url="resource.extras['apidocUrl'] as string"
224+
/>
214225
<!-- Show regular preview for other file types -->
215226
<Preview
216227
v-else
@@ -373,6 +384,8 @@ import ResourceIcon from './ResourceIcon.vue'
373384
import EditButton from './EditButton.vue'
374385
import DataStructure from './DataStructure.vue'
375386
import Preview from './Preview.vue'
387+
import { isOrganizationCertified } from '../../functions/organizations'
388+
import SwaggerClient from './Swagger.client.vue'
376389
377390
const GENERATED_FORMATS = ['parquet', 'pmtiles', 'geojson']
378391
const URL_FORMATS = ['url', 'doi', 'www:link', ' www:link-1.0-http--link', 'www:link-1.0-http--partners', 'www:link-1.0-http--related', 'www:link-1.0-http--samples']
@@ -397,6 +410,7 @@ const Pmtiles = defineAsyncComponent(() => import('./Pmtiles.client.vue'))
397410
const JsonPreview = defineAsyncComponent(() => import('./JsonPreview.client.vue'))
398411
const PdfPreview = defineAsyncComponent(() => import('./PdfPreview.client.vue'))
399412
const XmlPreview = defineAsyncComponent(() => import('./XmlPreview.client.vue'))
413+
const DatafairPreview = defineAsyncComponent(() => import('./Datafair.client.vue'))
400414
401415
const { t } = useTranslation()
402416
const { formatRelativeIfRecentDate } = useFormatDate()
@@ -421,6 +435,18 @@ const hasPmtiles = computed(() => {
421435
return props.resource.extras['analysis:parsing:pmtiles_url'] || props.resource.format === 'pmtiles'
422436
})
423437
438+
const hasDatafairPreview = computed(() => {
439+
// Checks if there are the corresponding extras for a datafair preview.
440+
// Limited only to datasets published by certified organizations since it will load an iframe.
441+
return isOrganizationCertified(props.dataset.organization) && props.resource.extras['datafairEmbed']
442+
})
443+
444+
const hasOpenAPIPreview = computed(() => {
445+
// Checks if there are the corresponding extras for a datafair preview.
446+
// Limited only to datasets published by certified organizations since it will load an iframe.
447+
return isOrganizationCertified(props.dataset.organization) && props.resource.extras['apidocUrl']
448+
})
449+
424450
const format = computed(() => getResourceFormatIcon(props.resource.format) ? props.resource.format : t('Fichier'))
425451
426452
const ogcService = computed(() => detectOgcService(props.resource))
@@ -464,7 +490,7 @@ const tabsOptions = computed(() => {
464490
options.push({ key: 'map', label: t('Carte') })
465491
}
466492
467-
if (hasTabularData.value || hasPreview.value) {
493+
if (hasTabularData.value || hasPreview.value || hasDatafairPreview.value || hasOpenAPIPreview.value) {
468494
options.push({ key: 'data', label: t('Aperçu') })
469495
}
470496

pages/design/previews.vue

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,23 @@
6262
:dataset="xmlAndJsonDataset"
6363
/>
6464
</div>
65+
<div>
66+
<h2>datafair Preview</h2>
67+
<h3>Iframe preview</h3>
68+
<p>One added tab: <b>Aperçu</b>. The iframe could show a map, data table or a structure field table.</p>
69+
<ResourceAccordion
70+
:resource="datafairDataset.resources.filter((r) => r.extras['datafairEmbed'] === 'map')[0]"
71+
:dataset="datafairDataset"
72+
/>
73+
<h3 class="mt-4">
74+
API doc preview
75+
</h3>
76+
<p>One added tab: <b>Aperçu</b>. Show the api doc with a Swagger UI.</p>
77+
<ResourceAccordion
78+
:resource="datafairDataset.resources.filter((r) => r.extras['apidocUrl'])[0]"
79+
:dataset="datafairDataset"
80+
/>
81+
</div>
6582
</div>
6683
</div>
6784
</template>
@@ -75,4 +92,5 @@ const { data: pmtilesDataset } = await useAPI<Dataset>('/api/1/datasets/proposit
7592
const { data: ogcDataset } = await useAPI<Dataset>('/api/1/datasets/bassins-versant-topographiques-metropole-2023-bd-topage-r/')
7693
const { data: pdfDataset } = await useAPI<Dataset>('/api/1/datasets/etudes-impact-open-data/')
7794
const { data: xmlAndJsonDataset } = await useAPI<Dataset>('/api/1/datasets/paris-2024-sites-de-competition/')
95+
const { data: datafairDataset } = await useAPI<Dataset>('/api/1/datasets/mobilite-stationnement-des-parkings-en-temps-reel-1/')
7896
</script>

0 commit comments

Comments
 (0)